ERC-6220: Equippable NFTs (RMRK Equippable)
ERC-6220 layers modular assembly on top of ERC-6059 nestable + ERC-5773 multi-asset NFTs. A parent NFT (e.g. a game character) has named equip slots (helmet, sword, shield); child NFTs whose contracts publish themselves as compatible parts can be equipped into those slots, changing the parent's rendered composition. The renderer reads the parent's active composition and overlays the child equipped assets in the right positions.
The standard introduces three new primitives:
- Catalog — a registry contract listing slots and parts that parents can declare equippable.
- Equip slot — a (parent, slot, child) tuple stored in the parent's contract.
- Equippable definition — a child contract declaring "I'm equippable into slot S of catalog C with asset A".
Required Interface (abridged)
interface IERC6220 {
struct IntakeEquip {
uint256 tokenId;
uint256 childIndex;
uint64 assetId;
uint64 slotPartId;
address childAddress;
uint256 childTokenId;
uint64 childAssetId;
}
event ChildAssetEquipped(uint256 indexed tokenId, uint64 indexed assetId, uint64 indexed slotPartId,
uint256 childTokenId, address childAddress, uint64 childAssetId);
event ChildAssetUnequipped(uint256 indexed tokenId, uint64 indexed assetId, uint64 indexed slotPartId);
function equip(IntakeEquip calldata data) external;
function unequip(uint256 tokenId, uint64 assetId, uint64 slotPartId) external;
function getEquipment(uint256 tokenId, address catalog, uint64 slotPartId)
external view returns (uint64 assetId, uint64 childAssetId, uint256 childTokenId, address childAddress);
}Neo Equivalent: NEP-11 + Catalog Contract + Equip-Slot Storage
The Neo port composes:
- Catalog contract — separate NEP-11 listing slot/part metadata.
- Equippable parent NFT — extends the [ERC-6059 mirror](./erc-6059) with equip-slot storage
(tokenId, slot) → (childContract, childTokenId, childAssetId). - Equippable child NFT — exposes
IsEquippable(slotId)for the parent to verify before equipping.
public static void Equip(ByteString tokenId, BigInteger assetId, BigInteger slotPartId,
UInt160 childContract, ByteString childTokenId, BigInteger childAssetId)
{
if (!Runtime.CheckWitness(OwnerOf(tokenId))) throw new Exception("NEP11:NotOwner");
// Verify child is a registered child of this parent (per ERC-6059 mirror).
if (!IsChild(tokenId, childContract, childTokenId)) throw new Exception("NEP11:NotChild");
// Verify child declares itself equippable into this slot.
var ok = (bool)Contract.Call(childContract, "isEquippable", CallFlags.ReadOnly,
new object[] { childTokenId, slotPartId, childAssetId });
if (!ok) throw new Exception("NEP11:NotEquippable");
Storage.Put(Storage.CurrentContext, EquipKey(tokenId, slotPartId),
StdLib.Serialize(new object[] { assetId, childAssetId, childTokenId, childContract }));
OnChildAssetEquipped(tokenId, assetId, slotPartId, childTokenId, childContract, childAssetId);
}| ERC-6220 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
Catalog contract | Separate Neo C# catalog contract | Registry of slots + parts |
equip(IntakeEquip) | Equip(tokenId, assetId, slotPartId, …) | Owner-witness-checked |
unequip(tokenId, assetId, slotPartId) | Unequip(tokenId, assetId, slotPartId) | Owner-witness-checked |
getEquipment(tokenId, catalog, slotPartId) | GetEquipment(tokenId, slotPartId) | Returns the equipped tuple or null |
Child IsEquippable declaration | Child contract's IsEquippable(tokenId, slotId, assetId) view | Cross-contract verification |
Composition Stack
The full RMRK Equippable stack composes four mirror pages:
- NEP-11 base — basic NFT.
- ERC-5773 — multi-asset (the parent has multiple renderable variants; equipping assets adds to the active set).
- ERC-6059 — nestable (the equipped child is also a nested child of the parent).
- ERC-6220 (this page) — equip slot mapping that decides which children render where.
Migration Notes
For RMRK Equippable collections porting to Neo:
- Implement the catalog contract as a thin Neo C# service exposing
getSlot(slotId)/getParts(catalogId). - The parent contract inherits NEP-11 + adds nestable + multi-asset + equip storage.
- Child contracts add the
IsEquippable(tokenId, slotId, assetId)view declaring compatibility. - The renderer (off-chain) resolves the parent's active assets, the per-slot equipment, and overlays the child rendered assets.
The on-chain footprint is the heaviest of any NFT extension — because the standard composes four other standards, the storage and gas cost per equip / unequip is non-trivial. Worth it for high-fidelity composable game assets; overkill for simple inventory systems.
