-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ERC-998 Composable Non-Fungible Token Standard #998
Comments
It was pointed out to me by @jeffwalsh that adding an ERC-20 token required it's own method for incrementing the balance rather than replacing it. He's also implemented an example using a basic ERC-721 in this fiddle. Many thanks Jeff. |
You might want to check out Delegated Non-Fungible Tokens: #994 which already sets out the architecture of parent/child NFTs. Whereas a single NFT is considered a "deed", a tree of NFTs could be considered a "zone". |
Not too different from the proposed mapping:
However not dealing with the same contract with parents and children being simply related key to key as is the case with #994.
Could it be
|
This was an edit, but then grew so I'll post again: I guess for fungible you want it to be an integer.
Where Alternatively the whole thing on it's head so that one child has one parent as is the case with #994.
edit: Although that then breaks when you're trying to figure out where a fungible is held. |
I see your point about the child tokenId being overwritten, that's a pain. I had several approaches to this and I knew this wasn't perfect. You are right that a child contract will only be able to have one ERC-721 child. Darn. A strict requirement of this proposal will be NOT requiring any changes to children that are already ERC-721 or ERC-20, meaning they won't have to upgrade to be a child of a composite. Another approach was to keep the mapping:
And upgrade to a pseudo unique address for ERC-721 children:
Note: this is actually how the EVM generates smart contract addresses, using the hash of address + nonce as a bytes20 address type. In this way we have a pseudo unique address for every child token. We will have to adopt your bookkeeping method 0 means no child and 1 means child exists. I really wanted to reduce the need for a deep mapping as in your proposed solution, although my unique address generation pushes a bit more computation and gas fees onto the miners and users. This would have to be calculated to transfer a child token as well, like so:
Thoughts on this @mryellow? |
Interesting, so it's I was thinking a lot of the issues go away when dealing with non-fungible or fungibles independently. It's always this bit where you have to start thinking about using "tokenId" index Perhaps the neatest solution is to not attempt to deal with them in the same variable, but storing differently once you know what type you're dealing with. Not sure how best to represent anything like that semantically though. Could add a boolean for |
I think you avoid it when dealing with fungibles. So in the fungible functions, simply use the fungible contract address. There's no need for extra computation in the fungibles functions. Could be possible to go with a completely new mapping for non-fungibles and deal with them seperately.
And still use the pseudo address construction for dealing with NFTs to ensure a unique address per NFT. |
Another thought on this concept, ERC: Ethereum Claims Registry #780 features some prior work which might be informative:
Having a
However would this add anything or is the Would anyone ever want to use the |
My feeling on this, and why I refer to is at "pseudo address creation" is because it is analogous to how smart contract addresses are generated given the nonce of the smart contract. So here we are creating an address for every NFT based on the contract address and tokenId of the NFT. This is exactly the same as if every NFT were deployed as a new contract from the main contract. |
This is a great start, thanks. Like the idea of pseudo-addresses for child tokens. Have a bunch of thoughts. Here're three major points.
As you said, I think this is a preferred solution. I don't think it's a good idea to have one mapping for two different standards. It adds complexity (and fragility), adds unexpected behaviors, makes it harder for the wallets to process two different types. Wallets actually don't know the type beforehand - how would they know what token is that without guessing that through presence of the methods, etc? To me that's the biggest problem that I don't see how to resolve without splitting. Token type needs to be defined very explicitly. So fungiblePossessions and nonfungiblePossessions may be more appropriate, with comments explicitly saying what standards to they support (ERC20+ERC677 and ERC721 respectively).
Also, more like a suggestion. "children" naming sounds a bit off for ERC20. It's more like stuff that belongs to this token. Maybe possessions would be better, or something like that. Nit. |
Hello everybody and thanks @mattlockyer for creating this ERC. I was thinking about something like this for over a month, happy to see it brought to attention of a broader community. My initial idea was very similar to this, that you embed such functionality as an extension to ERC721, and your ERC998 contract would be an owner of some ERC721 tokens (from the perspective of owned token) while keeping information that the true owner is a token this contract itself keeps track of. I see some problems with such approach. The most important one being that if you have a chain of tokens from different ERC998 contracts, as a user you will have hard time understanding what contract you need to call to transfer a CryptoKitty that is owned by a DogERC998 token that is owned by PersonERC998 token that you own. I might be wrong here, but my intuition tells me you would have to call PersonERC998 and pass it 2 arrays of all contracts and tokens that are owners (parent, grandparent, ...) of the token to be able to retrieve that poor little Kitty. Currently I'm thinking this is a perfect example where global / master registry could be used. I started drafting it (code is here) and would like to ask all of you if you also think this might be a good idea. I also agree with all the points @vernon99 mentioned and already partially incorporated them into the codebase. |
Thank you everyone for the comments. I have been grinding through an implementation with tests so we have a basis from which to work on finding the most optimal solutions together. @vernon99 I have address the first 2 concerns for nonfungible "possessions". I like this name.
This code is working now, it's here if you want to run it yourself. I have put ERC-20 on hold for a bit, seems relatively trivial once ERC-721 is working. @vernon99 thanks again for the comments on where to look for that implementation. @mg6maciej thanks for dropping in here. I think the global registry is a fantastic solution to a lot of the needs of developers out there. I do foresee some issues with functionality, specific implementations that restrict the composability of assets for example. For all basic compositions though, I think it's quite a good approach! |
I've made some updates to the comment at the top to reflect the dramatic changes to the originally proposed code. Thank you @vernon99 @mg6maciej for the feedback. It motivated me to push forward and get a more comprehensive and elegant solution up. |
Yes, there are things that need to be taken care of in 998. I can think of these cases:
|
Another thing worth discussing is what kind of events these transfers will fire.
|
Absolutely. I think this is a great start. @mg6maciej What are the thoughts on NFTP as a title for both the non-fungible token possessions and FTP for fungible token possessions? |
I'm not that interested in choosing names for anything for now. I think it's too early. I'd be more than happy to change names in my implementation at some point. I've noticed a potential issue with different implementations of ERC721 tho. There are a couple of ways to interact with them.
If you just use |
It looks that ERC-998 is perfect fit for intellectual property tokens. For example, parent contract could reflect the ownership of education course of author. Children tokens could reflect the copies for students. If there is any issues? |
@mg6maciej I'm going to be adding the callbacks and events soon to the implementation I know you're but syncing on the composables instances + registry will be crucial, let's touch base about this next week. If anyone has any feedback on how to interop a global composable NFT registry with individual instances of Composable 998 (721 extended) please feel free to chime in! Also there is a Discord for general #buidl chatter around NFTs here: https://discord.gg/3TtqP2C |
Very interesting work Matt! Following your progress closely - keep it up. |
Hi everyone! Myself, @flockonus and a few other folks from the CryptoKitties team had the opportunity to meet @mattlockyer a couple days ago to discuss designs for composable NFTs. We primarily discussed a different design than what is currently in this ERC, which I’ll summarize here to get feedback on from all of you fine NFT enthusiasts! Current Design IssuesOwnership state is split between child/parentWhen a token is owned by a composable “parent” NFT, the complete ownership state is split between the child contract and the parent contract. The child contract (which may not know about ERC998) state says that the token is owned by the parent contract address. The parent contract specifies which NFT within the contract actually owns the child token. Similarly, sometimes methods related to ownership are executed from the child contract and other times they are executed from the parent contract. Implementation ComplexityBasically, the current design requires implementors to maintain a lot of state! Parent contracts would need to track who owns their tokens, which child tokens are owned by the parent contract’s tokens, and reimplement/wrap Ownership terminology overloadingOwnership in Ethereum is currently strongly defined as a mapping from Ethereum addresses to tokens of some kind. Current contracts rely on this relationship for ownership operations (you Bottom-up Design ProposalThe current proposal describes ERC721 tokens with logic added so they can own child tokens. The bottom-up design inverts this, instead proposing an extension to ERC721 and ERC20/223 that enables them to be linked/attached to any ERC721 token, in addition to being owned by an Ethereum address (as is the case now). Similarly to the current proposal, the ownership hierarchy for a particular NFT can be thought of as a tree, with an Ethereum address at the root, NFTs in the middle, and either NFTs or fungible tokens at the leaves. RationaleIn addition to the benefits in the next section, the rationale behind this proposal comes from our experience with CryptoKitties. After releasing CryptoKitties, many developers built apps, tools, and tokens that extend the CryptoKitties ecosystem. It doesn’t make much sense for a CryptoKitty to own EOS tokens, but it does make sense (and is super cool!) for a CryptoKitty to own a provably unique cat-hat token (see KittyHats). In general, we see value in NFTs being extended by contextual tokens with semantic meaning, not NFTs being able to own arbitrary tokens. We think the bottom-up approach best fits this ecosystem-extension view of composable tokens. Benefits
Drawbacks
|
Wonder if there are issues similar to "approval" where some "attachment" might be orphaned by a chance in "ownership" at the child level. Does a cat continue to be attached to a hat after the hat's ownership has changed? Who can detach the hat?
I'm rather interested by the possibilities when it comes to arbitrary standard tokens rather than specifically crafted tokens setup to be children. Semantics can be re-imagined in new contexts. i.e. Turn the power on for your city in a dApp game by adding This "composable" approach may be entirely unsuited for such tasks in the end, the proper approach is probably a lot more custodian and based around "deposits", but worth mentioning a use-case for the parent-child ownership style even if I'm not seeing the cleanest way to lay it out.
Perhaps the best approach is to separate everything. ERC721's go on being themselves, ERC20's do their thing. Then another contract can associate them, perhaps by taking custody and wrapping into a new combined entity. |
I have been looking into the bottom-up approach. Some comments: Using the bottom-up approach it is possible for new composable-aware ERC721 parent contracts to have onchain enumeration for any token that is transferred to it. This is done by using the I really like the idea of doing enumeration off-chain because I don't like the expense and complexity of the bookkeeping on-chain. I like the idea of using events to track child tokens of parents. A cache of this info (child tokens) could be included in the JSON file returned by the tokenURI for each parent. There is another issue which is buy/trade/sales verification of state. When selling a parent token the state of the parent token (all the children) can be checked using on-chain enumeration to verify it is all that is expected by the buyer. This verification can be done in the same transaction that the transfer is done. Without this verification it is possible for someone to remove children right before a transfer call and the buyer does not get what he/she paid for. So my question is this: How to securely do state verification of a parent ERC721 token when selling it if there is no on-chain enumeration? Perhaps the state verification can be done onchain by passing in an array of child contracts and child token Ids to check for after the transfer function is called. I think that would work. |
Related work, haven't dug into it but have seen this mentioned in relation to "composables".
https://setprotocol.com/pdf/set_protocol_whitepaper.pdf edit: They create an ERC20 which has a pre-defined set of addresses and quantities associated with it. |
@mryellow I believe that's the intention, the hat would keep on belonging to the same cat. Who can detach the item is the current owner of the cat.
@mudgen I think so too! If the item in case that is for purchase, has attached items that are important for the buyer then it would make sense that the purchase order would pass an array of address+ids to verify the items are indeed attached to the NFT. It would work with signed transactions (similar to 0x style) orders as well, if the seller at the time of the sales creation also bundle the promise of the items in the order. |
@flockonus @mudgen #1180 proposes a generic solution for validation when selling NFTs.
@mudgen I think your assumptions are wrong here. If I understand this bottom-up concept correctly, child token is never directly transferred to its parent, i.e. @jordanschalm @flockonus Are you going to join our next composable call on NFTy Magicians discord? I would like to discuss this concept with you in depth and believe we should schedule a call sooner than planned. Also @jordanschalm I asked you some questions there, but if you want I can move it to GitHub. |
@viltiki I totally agree with you! |
@viltiki I'm also interested in this development. my project's NFTs currently adhere to both ERC998ERC721 and ER998ERC20 but afaik wouldn't interoperate as-is with Enjin metaverse unfortunately. |
Hi, we developed a lightweight version working with the EIP1155 and the latest Open Zeppelin contracts. It would be very cool if you have time check it out. The repo is available here https://github.com/rocksideio/ERC998-ERC1155-TopDown. All feedbacks are welcome. |
@tclairet That's great. I'm doing something similar with Aavegotchi. |
Hi 998 people! We are also building out a 1155 flavour of this with a parent 721 which can hold 1155 tokens. However for us the token itself is the owner of the children, so move the parent 721, you also get ownership of the child 1155 tokens. I have looked at @tclairet repo which looks great, thanks for that its really good to see that. I have noticed that the method signatures for 998 topdown 721 are not really applicable for topdown 721 -> 1155 token. Is there a common understand of what a common interface for a 721 -> 1155 top down interface would look like? Or even a more standardised 721 -> 1155 EIP somewhere? |
@jamesmorgan I modeled the 721 -> 1155 top down interface functions from 998. The function signatures can be similar to what ERC998 uses. Like this: /// @notice Transfer tokens from owner address to a token
/// @param _from The owner address
/// @param _id ID of the token
/// @param _toContract The ERC721 contract of the receiving token
/// @param _toTokenId The receiving token
/// @param _value The amount of tokens to transfer
function transferToParent(
address _from,
address _toContract,
uint256 _toTokenId,
uint256 _id,
uint256 _value
) external;
/// @notice Transfer token from a token to an address
/// @param _fromContract The address of the owning contract
/// @param _fromTokenId The owning token
/// @param _to The address the token is transferred to
/// @param _id ID of the token
/// @param _value The amount of tokens to transfer
function transferFromParent(
address _fromContract,
uint256 _fromTokenId,
address _to,
uint256 _id,
uint256 _value
) external;
/// @notice Transfer a token from a token to another token
/// @param _fromContract The address of the owning contract
/// @param _fromTokenId The owning token
/// @param _toContract The ERC721 contract of the receiving token
/// @param _toTokenId The receiving token
/// @param _id ID of the token
/// @param _value The amount tokens to transfer
function transferAsChild(
address _fromContract,
uint256 _fromTokenId,
address _toContract,
uint256 _toTokenId,
uint256 _id,
uint256 _value
) external; @jamesmorgan remember there is EIP-2535 Diamond Standard if you start hitting the 24K max contract size limit. But it is also good for organizing a lot of functionality you want to keep at the same contract address. And for transparent upgrades. |
I'd like to use this token as our service POC. (Solidity version upgraded ERC998ERC721TopDown implementation is here: ERC998ERC721TopDown-solidity-v6) StepsFirst, mint 2 ERC721 token from a same contract. composable_contract.mint(wallet_address) => tokenID_1
composable_contract.mint(wallet_address) => tokenID_2 Second, safeTransfer 2nd token to a same ComposableTopDown contract. composable_contract.safeTransferFrom(wallet_address, composable_contract_address, tokenID_2, tokenID_1_bytes) => fail Don't you expect this usage? |
Hi @mattlockyer, nice work on this.
Why not just take that approach? It seems desirable to make NFTs first class citizens with addresses. They can then own and be owned by other contracts or tokens. They could also be implemented as minimal proxy contracts that point back to logic in the main contract, minimising gas and storage costs. On a related not, another capability that I think would improve composability and usability is separation of ownership and control/possession. Any ideas on that gratefully received. |
Just to chime in here, we have developed nested NFTs as part of RMRK and our set of art legos, and will take this EIP and its comprehensive discussion into consideration when porting our logic over to EVM. For a breakdown, please see Dawn of the Art Legos. We are also thinking hard how to slap royalties onto this, but that's a separate problem. |
That's great. If your contract bytecode exceeds the max contract size you might want to consider using EIP-2535 Diamonds. I understand the challenge of implementing a lot of functionality into an ERC721 contract. |
Hi. |
@mudgen
Still |
…e NFT. ERC998 for Solidity ^0.8.2: info here: ethereum/EIPs#998 Most of the functionality is there. feel free to use.
Are there any plans to further improve this EIP and update to the review status? Why has it been on draft status for so long? |
I verified this and you are correct. I fixed it in the standard. Thank you very much for pointing this out. Sorry for how long it took to get updated. |
@columbbus
I don't have plans. It's been draft status for so long because nobody is improving it, like updating the code to use a recent version of Solidity. And probably nobody is improving it because there isn't enough incentive vs other things. |
@mudgen That is very unfortunate, but I actually can't imagine that there is no need for such functionality for NFTs. Are there any other standards that try to accomplish the same, i. e. NFTs owning other NFTs that have become established? Or are there any other alternatives to achieve something similar to this ERC? I know there is the ERC 994 but it seems that this is even less regarded than this ERC. |
@columbbus I commonly run into projects that need, want or have implemented NFT composability (NFTs owning or controlling other NFTs). They implemented ERC998 or some modified version of it. ERC998 still works today and projects use it. A fully updated and finalized standard for NFT composability would be useful and helpful but is not required to implement the functionality. My most recently implementation is Aavegotchi, which uses the ideas of ERC998 to make ERC721s (Aavegotchis) own ERC1155 tokens (wearables and other items). Here: https://github.com/aavegotchi/aavegotchi-contracts |
I'm closing this issue for organizational purposes, but feel free to continue using it for discussion on EIP-998 for now. At some point before EIP-998 moves to review, the discussion link should be updated to point at an Ethereum Magicians thread. |
Just to be sure, we are not dropping this standard right? |
This is a pretty important standard for industrial and gamming applications and I'm wondering why not keep improving it. In industriall applications for example if you create an NFT for car parts like the ENGINE or the Wheels. and the NFT of the car is an 998 grouping all the 721's of individual parts. I'm also in charge of a project that is implementing a game character that is going to have different NFT for different uses. And is really important for us to have this kind of functionality. As a last question is possible to do this kind of work with other standard?. I have reviewed and have found nothing so far. |
Same question and need.
I have the same question also for you guys. I feel this standard is more important than it seems. |
I am happy to help drive the process from draft to final if existing authors want and there is interest of adoptions |
@xinbenlv I'm defently interested and our project. How can we help you?: |
Update
The latest code is in the EIP and reference implementation
EIP
eip-998.md
Reference Implementation
https://github.com/mattlockyer/composables-998
Reading
Original Medium post:
Introducing Crypto Composables
Crypto Composables - Use Cases and Applications
Abstract
A standard extension for any non-fungible token to own another non-fungible ERC-721 or fungible ERC-20 tokens. Transferring the token composition means transferring the entire hierarchy of items. For example, a cryptokitty may own a scratching post and a feeding dish; the dish may contain some amount of fungible “chow” tokens. If I sell the cryptokitty, I sell all of the belongings of the cryptokitty.
Specification
After several discussions with the community we settled on providing these 4 extensions to a standard ERC-721 NFT in order to support composable functionality:
ERC998ERC721 top-down composable tokens that receive, hold and transfer ERC721 tokens
ERC998ERC20 top-down composable tokens that receive, hold and transfer ERC20 tokens
ERC998ERC721 bottom-up composable tokens that attach themselves to other ERC721 tokens.
ERC998ERC20 bottom-up composable tokens that attach themselves to ERC721 tokens.
For more details on the reference implementation, visit the EIP.
Summary
I’ve presented a new standard for composable non-fungible tokens, ERC-998, that allows for the ownership of ERC-998, ERC-721 and ERC-20 tokens. This allows standard assets to be composed into complex compositions and traded using a single transfer. The additional functionality that can be added to a non-fungible token based on the presence of specific child tokens is open for exploration. I believe that standard interfaces for non-fungible and fungible assets to be composed and act upon one another is an exciting new area for the Ethereum community to explore and I look forward to seeing the work.
LATEST: EIP with reference implementation code
eip-998.md
The text was updated successfully, but these errors were encountered: