← Protocols
ERC-998 — Composable Non-Fungible Token
Standard / EIP·EVM

ERC-998 — Composable Non-Fungible Token

01Description

Draft ERC standard extending ERC-721 to let NFTs own other ERC-721 and ERC-20 tokens. Defines top-down (parent holds children) and bottom-up (child attached to parent) composability variants so a hierarchical bundle transfers as a single root-owner change.

02Best for
  • 01composable NFTs (avatars + traits, ships + parts)
  • 02in-game inventory bundles owned by a hero NFT
  • 03NFT-of-NFTs collections
  • 04atomic transfer of asset bundles via root owner update
  • 05RWA bundles with sub-asset accounting
03Install
  • pnpm add @openzeppelin/contracts
  • forge install mattlockyer/composables-998
05Prompt snippet
Implement composable NFTs against the four ERC-998 sub-interfaces: `ERC998ERC721TopDown` and `ERC998ERC20TopDown` (parent holds children) plus `ERC998ERC721BottomUp` and `ERC998ERC20BottomUp` (child attaches to parent). Top-down expects `getChild(from, tokenId, childContract, childTokenId)`, `safeTransferChild(...)`, `transferChild(...)`, `getERC20(tokenId, erc20Contract)` and emits `ReceivedChild` / `TransferChild` events. Resolve effective ownership via `rootOwnerOf(tokenId)` which walks the parent chain up to a root EOA or contract; clients should always check `rootOwnerOf` (not `ownerOf`) for authorization. Use ERC-165 to advertise the specific composable interface IDs (`0xcde244d9` top-down 721, `0x7294ffed` bottom-up 721, etc.) so wallets can render bundles correctly. Inherit ERC-721 from OpenZeppelin and layer the composable hooks on top, taking care that `_beforeTokenTransfer` clears or migrates child references.
06Gotchas
  • Ownership cycle hazard: if token A is transferred into token B and B is then transferred into A, `rootOwnerOf` recurses forever. Always validate that the incoming parent is not a descendant of the child before accepting `transferChild` / `safeTransferFrom` into a composable.
  • Authorization MUST use `rootOwnerOf(tokenId)` rather than `ownerOf(tokenId)` — the immediate `ownerOf` of a child is the parent contract, so naive ERC-721 marketplaces will mis-list composed children as owned by the bundle contract itself.
  • Bottom-up and top-down variants are not interchangeable; mixing them in one collection breaks tooling. Pick one direction per contract and document the supported interface IDs via ERC-165.
  • Gas griefing: deeply nested bundles make `rootOwnerOf` walks expensive — cap nesting depth on receive (e.g., reject children whose subtree depth exceeds N) so adversarial users cannot brick transfers.
  • ERC-998 is still Draft and tooling support is uneven — OpenSea, Blur, and most indexers will treat composed children as orphaned. Plan an off-chain indexer (subgraph) to surface bundle membership for the UI.
07Alternatives