Skip to content

Commit

Permalink
Adds methods to identify equipments where the parent or child asset w…
Browse files Browse the repository at this point in the history
…as replaced.
  • Loading branch information
steven2308 committed Jan 2, 2024
1 parent 8f44440 commit fdc6afb
Show file tree
Hide file tree
Showing 3 changed files with 534 additions and 2 deletions.
242 changes: 242 additions & 0 deletions contracts/RMRK/utils/RMRKCatalogUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ pragma solidity ^0.8.21;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IRMRKCatalog} from "../catalog/IRMRKCatalog.sol";
import {IERC6220} from "../equippable/IERC6220.sol";
import {IERC5773} from "../multiasset/IERC5773.sol";
import {IERC7401} from "../nestable/IERC7401.sol";
import {RMRKLib} from "../library/RMRKLib.sol";
import "../library/RMRKErrors.sol";

/**
* @title RMRKCatalogUtils
Expand All @@ -12,6 +17,25 @@ import {IRMRKCatalog} from "../catalog/IRMRKCatalog.sol";
* @dev Extra utility functions for RMRK contracts.
*/
contract RMRKCatalogUtils {
using RMRKLib for uint64[];
using RMRKLib for uint256[];

/**
* @notice Used to store the core structure of the `Equippable` RMRK lego.
* @return parentAssetId The ID of the parent asset equipping a child
* @return slotId The ID of the slot part
* @return childAddress Address of the collection to which the child asset belongs to
* @return childId The ID of token that is equipped
* @return childAssetId The ID of the asset used as equipment
*/
struct ExtendedEquipment {
uint64 parentAssetId;
uint64 slotId;
address childAddress;
uint256 childId;
uint64 childAssetId;
}

/**
* @notice Structure used to represent the extended part data.
* @return partId The part ID
Expand Down Expand Up @@ -111,4 +135,222 @@ contract RMRKCatalogUtils {
(owner, type_, metadataURI) = getCatalogData(catalog);
parts = getExtendedParts(catalog, partIds);
}

/**
* @notice Used to get data about children equipped to a specified token, where the parent asset has been replaced.
* @param parentAddress Address of the collection smart contract of parent token
* @param parentId ID of the parent token
* @param catalogAddress Address of the catalog the slot part Ids belong to
* @param slotPartIds Array of slot part IDs of the parent token's assets to search for orphan equipments
* @return equipments Array of extended equipment data structs containing the equipment data, including the slot part ID
*/
function getOrphanedEquipmentsFromParentAsset(
address parentAddress,
uint256 parentId,
address catalogAddress,
uint64[] memory slotPartIds
) public view returns (ExtendedEquipment[] memory equipments) {
uint256 length = slotPartIds.length;
ExtendedEquipment[] memory tempEquipments = new ExtendedEquipment[](
length
);
uint64[] memory parentAssetIds = IERC5773(parentAddress)
.getActiveAssets(parentId);
uint256 orphansFound;

for (uint256 i; i < length; ) {
uint64 slotPartId = slotPartIds[i];
IERC6220.Equipment memory equipment = IERC6220(parentAddress)
.getEquipment(parentId, catalogAddress, slotPartId);
if (equipment.assetId != 0) {
(, bool assetFound) = parentAssetIds.indexOf(equipment.assetId);
if (!assetFound) {
tempEquipments[orphansFound] = ExtendedEquipment({
parentAssetId: equipment.assetId,
slotId: slotPartId,
childAddress: equipment.childEquippableAddress,
childId: equipment.childId,
childAssetId: equipment.childAssetId
});
unchecked {
++orphansFound;
}
}
}
unchecked {
++i;
}
}

equipments = new ExtendedEquipment[](orphansFound);
for (uint256 i; i < orphansFound; ) {
equipments[i] = tempEquipments[i];
unchecked {
++i;
}
}
}

/**
* @notice Used to get data about children equipped to a specified token, where the child asset has been replaced.
* @param parentAddress Address of the collection smart contract of parent token
* @param parentId ID of the parent token
* @return equipments Array of extended equipment data structs containing the equipment data, including the slot part ID
*/
function getOrphanedEquipmentFromChildAsset(
address parentAddress,
uint256 parentId
) public view returns (ExtendedEquipment[] memory equipments) {
uint64[] memory parentAssetIds = IERC5773(parentAddress)
.getActiveAssets(parentId);

// In practice, there could be more equips than children, but this is a decent approximate since the real number cannot be known, also we do not expect a lot of orphans
uint256 totalChildren = IERC7401(parentAddress)
.childrenOf(parentId)
.length;
ExtendedEquipment[] memory tempEquipments = new ExtendedEquipment[](
totalChildren
);
uint256 orphansFound;

uint256 totalParentAssets = parentAssetIds.length;
for (uint256 i; i < totalParentAssets; ) {
(
uint64[] memory parentSlotPartIds,
address catalogAddress
) = getSlotPartsAndCatalog(
parentAddress,
parentId,
parentAssetIds[i]
);
uint256 totalSlots = parentSlotPartIds.length;
for (uint256 j; j < totalSlots; ) {
IERC6220.Equipment memory equipment = IERC6220(parentAddress)
.getEquipment(
parentId,
catalogAddress,
parentSlotPartIds[j]
);
if (equipment.assetId != 0) {
uint64[] memory childAssetIds = IERC5773(
equipment.childEquippableAddress
).getActiveAssets(equipment.childId);
(, bool assetFound) = childAssetIds.indexOf(
equipment.childAssetId
);
if (!assetFound) {
tempEquipments[orphansFound] = ExtendedEquipment({
parentAssetId: equipment.assetId,
slotId: parentSlotPartIds[j],
childAddress: equipment.childEquippableAddress,
childId: equipment.childId,
childAssetId: equipment.childAssetId
});
unchecked {
++orphansFound;
}
}
}
unchecked {
++j;
}
}
unchecked {
++i;
}
}

equipments = new ExtendedEquipment[](orphansFound);
for (uint256 i; i < orphansFound; ) {
equipments[i] = tempEquipments[i];
unchecked {
++i;
}
}
}

/**
* @notice Used to retrieve the parent address and its slot part IDs for a given target child, and the catalog of the parent asset.
* @param tokenAddress Address of the collection smart contract of parent token
* @param tokenId ID of the parent token
* @param assetId ID of the parent asset from which to get the slot parts
* @return parentSlotPartIds Array of slot part IDs of the parent token's asset
* @return catalogAddress Address of the catalog the parent asset belongs to
*/
function getSlotPartsAndCatalog(
address tokenAddress,
uint256 tokenId,
uint64 assetId
)
public
view
returns (uint64[] memory parentSlotPartIds, address catalogAddress)
{
uint64[] memory parentPartIds;
(, , catalogAddress, parentPartIds) = IERC6220(tokenAddress)
.getAssetAndEquippableData(tokenId, assetId);
if (catalogAddress == address(0)) revert RMRKNotComposableAsset();

(parentSlotPartIds, ) = splitSlotAndFixedParts(
parentPartIds,
catalogAddress
);
}

/**
* @notice Used to split slot and fixed parts.
* @param allPartIds[] An array of `Part` IDs containing both, `Slot` and `Fixed` parts
* @param catalogAddress An address of the catalog to which the given `Part`s belong to
* @return slotPartIds An array of IDs of the `Slot` parts included in the `allPartIds`
* @return fixedPartIds An array of IDs of the `Fixed` parts included in the `allPartIds`
*/
function splitSlotAndFixedParts(
uint64[] memory allPartIds,
address catalogAddress
)
public
view
returns (uint64[] memory slotPartIds, uint64[] memory fixedPartIds)
{
IRMRKCatalog.Part[] memory allParts = IRMRKCatalog(catalogAddress)
.getParts(allPartIds);
uint256 numFixedParts;
uint256 numSlotParts;

uint256 numParts = allPartIds.length;
// This for loop is just to discover the right size of the split arrays, since we can't create them dynamically
for (uint256 i; i < numParts; ) {
if (allParts[i].itemType == IRMRKCatalog.ItemType.Fixed)
numFixedParts += 1;
// We could just take the numParts - numFixedParts, but it doesn't hurt to double check it's not an uninitialized part:
else if (allParts[i].itemType == IRMRKCatalog.ItemType.Slot)
numSlotParts += 1;
unchecked {
++i;
}
}

slotPartIds = new uint64[](numSlotParts);
fixedPartIds = new uint64[](numFixedParts);
uint256 slotPartsIndex;
uint256 fixedPartsIndex;

// This for loop is to actually fill the split arrays
for (uint256 i; i < numParts; ) {
if (allParts[i].itemType == IRMRKCatalog.ItemType.Fixed) {
fixedPartIds[fixedPartsIndex] = allPartIds[i];
unchecked {
++fixedPartsIndex;
}
} else if (allParts[i].itemType == IRMRKCatalog.ItemType.Slot) {
slotPartIds[slotPartsIndex] = allPartIds[i];
unchecked {
++slotPartsIndex;
}
}
unchecked {
++i;
}
}
}
}
111 changes: 111 additions & 0 deletions docs/RMRK/utils/RMRKCatalogUtils.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,117 @@ Used to get the extended part data of many parts from the specified catalog in a
|---|---|---|
| parts | RMRKCatalogUtils.ExtendedPart[] | Array of extended part data structs containing the part data |

### getOrphanedEquipmentFromChildAsset

```solidity
function getOrphanedEquipmentFromChildAsset(address parentAddress, uint256 parentId) external view returns (struct RMRKCatalogUtils.ExtendedEquipment[] equipments)
```

Used to get data about children equipped to a specified token, where the child asset has been replaced.



#### Parameters

| Name | Type | Description |
|---|---|---|
| parentAddress | address | Address of the collection smart contract of parent token |
| parentId | uint256 | ID of the parent token |

#### Returns

| Name | Type | Description |
|---|---|---|
| equipments | RMRKCatalogUtils.ExtendedEquipment[] | Array of extended equipment data structs containing the equipment data, including the slot part ID |

### getOrphanedEquipmentsFromParentAsset

```solidity
function getOrphanedEquipmentsFromParentAsset(address parentAddress, uint256 parentId, address catalogAddress, uint64[] slotPartIds) external view returns (struct RMRKCatalogUtils.ExtendedEquipment[] equipments)
```

Used to get data about children equipped to a specified token, where the parent asset has been replaced.



#### Parameters

| Name | Type | Description |
|---|---|---|
| parentAddress | address | Address of the collection smart contract of parent token |
| parentId | uint256 | ID of the parent token |
| catalogAddress | address | Address of the catalog the slot part Ids belong to |
| slotPartIds | uint64[] | Array of slot part IDs of the parent token&#39;s assets to search for orphan equipments |

#### Returns

| Name | Type | Description |
|---|---|---|
| equipments | RMRKCatalogUtils.ExtendedEquipment[] | Array of extended equipment data structs containing the equipment data, including the slot part ID |

### getSlotPartsAndCatalog

```solidity
function getSlotPartsAndCatalog(address tokenAddress, uint256 tokenId, uint64 assetId) external view returns (uint64[] parentSlotPartIds, address catalogAddress)
```

Used to retrieve the parent address and its slot part IDs for a given target child, and the catalog of the parent asset.



#### Parameters

| Name | Type | Description |
|---|---|---|
| tokenAddress | address | Address of the collection smart contract of parent token |
| tokenId | uint256 | ID of the parent token |
| assetId | uint64 | ID of the parent asset from which to get the slot parts |

#### Returns

| Name | Type | Description |
|---|---|---|
| parentSlotPartIds | uint64[] | Array of slot part IDs of the parent token&#39;s asset |
| catalogAddress | address | Address of the catalog the parent asset belongs to |

### splitSlotAndFixedParts

```solidity
function splitSlotAndFixedParts(uint64[] allPartIds, address catalogAddress) external view returns (uint64[] slotPartIds, uint64[] fixedPartIds)
```

Used to split slot and fixed parts.



#### Parameters

| Name | Type | Description |
|---|---|---|
| allPartIds | uint64[] | [] An array of `Part` IDs containing both, `Slot` and `Fixed` parts |
| catalogAddress | address | An address of the catalog to which the given `Part`s belong to |

#### Returns

| Name | Type | Description |
|---|---|---|
| slotPartIds | uint64[] | An array of IDs of the `Slot` parts included in the `allPartIds` |
| fixedPartIds | uint64[] | An array of IDs of the `Fixed` parts included in the `allPartIds` |




## Errors

### RMRKNotComposableAsset

```solidity
error RMRKNotComposableAsset()
```

Attempting to compose an asset wihtout having an associated Catalog





Loading

0 comments on commit fdc6afb

Please sign in to comment.