ERC-6150: Hierarchical NFTs
ERC-6150 is the lighter alternative to ERC-6059. It encodes parent/child relationships between NFTs without the parent-governance accept-flow. Children are added directly when minted (the mint chooses a parent); transfers are owner-controlled without parent intermediation. Useful when:
- The composition is fixed at mint (e.g. albums of photos, books of chapters) rather than dynamically composed.
- Parent governance is out of scope — children move with their parent automatically, no propose-accept dance needed.
- The on-chain footprint should be minimal — ERC-6150 is roughly half the storage of ERC-6059.
Required Interface
interface IERC6150 {
event Minted(address indexed to, uint256 indexed tokenId, uint256 indexed parentId);
function parentOf(uint256 tokenId) external view returns (uint256);
function childrenOf(uint256 tokenId) external view returns (uint256[] memory);
function isRoot(uint256 tokenId) external view returns (bool);
function isLeaf(uint256 tokenId) external view returns (bool);
}A root has parentId = 0 (not in any tree); a leaf has no children. Tree traversal is straightforward — call parentOf to walk up, childrenOf to walk down.
Neo Equivalent: NEP-11 + Parent / Children Pointer Storage
public static ByteString ParentOf(ByteString tokenId)
=> Storage.Get(Storage.CurrentContext, ParentKey(tokenId));
public static ByteString[] ChildrenOf(ByteString tokenId)
{
var iter = Storage.Find(Storage.CurrentContext, ChildrenPrefix(tokenId), FindOptions.KeysOnly | FindOptions.RemovePrefix);
var list = new System.Collections.Generic.List<ByteString>();
while (iter.Next()) list.Add((ByteString)iter.Value);
return list.ToArray();
}
public static void MintWithParent(UInt160 to, ByteString tokenId, ByteString parentId)
{
if (parentId is not null && OwnerOf(parentId) is null) throw new Exception("NEP11:NoParent");
Storage.Put(Storage.CurrentContext, ParentKey(tokenId), parentId ?? new byte[0]);
if (parentId is not null)
Storage.Put(Storage.CurrentContext, ChildKey(parentId, tokenId), 1);
Mint(tokenId, new TokenState { Owner = to }); // NEP-11 base: Mint(tokenId, TokenState)
OnMinted(to, tokenId, parentId);
}| ERC-6150 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
parentOf(tokenId) | ParentOf(tokenId) returning ByteString or empty | Empty = root |
childrenOf(tokenId) | ChildrenOf(tokenId) enumerated via Storage.Find | |
isRoot(tokenId) | IsRoot(tokenId) returning ParentOf(tokenId) is null | |
isLeaf(tokenId) | IsLeaf(tokenId) returning iterator-empty | Cheap when no children registered |
| Mint with parent at creation | MintWithParent(to, tokenId, parentId) | Parent set at mint, immutable |
Comparison With ERC-6059
| Feature | ERC-6059 (Nestable) | ERC-6150 (Hierarchical) |
|---|---|---|
| Parent-controlled child addition | Yes (accept flow) | No (mint chooses parent) |
| Cross-contract nesting | Yes | No (single contract tree) |
| Storage cost | Higher (pending + active arrays) | Lower (single parent + children prefix) |
| Use case | Dynamic inventory composition | Static hierarchical structure |
ERC-6059 is right when the parent should govern what becomes its child (a character contract that vets each weapon NFT before equipping it); ERC-6150 is right when the structure is decided at mint time and needs no further governance (a book NFT minted with chapter NFTs as its children, no accept flow needed).
Composition
- ERC-6059 — nestable. Use 6150 for static trees, 6059 for dynamic.
- ERC-7066 — lockable. Locking the root locks the whole tree (override transfer to walk up).
- ERC-2981 — royalties. Distinct royalty splits per level of the tree if relevant.
Migration Notes
For ERC-6150 contracts porting to Neo:
- Single contract per tree (cross-contract nesting requires ERC-6059).
parentOfis set at mint; don't expose mutators.- Override
Transferto optionally walk children — by default ERC-6150 transfers move only the named token; the children stay with their tree position (conceptually still children even though their owner may now differ from the parent's owner).
For "transfer the whole tree" semantics, walk children recursively in the override.
