Nonfungible ERC1155 implementation#4684
Conversation
|
48d09ce to
23f739b
Compare
|
Hey @dwardu, thanks for the PR, I think it's an interesting proposal and something worth considering. Recently we released 5.0 so we're deciding on what items should go next in the roadmap. Although this proposal makes sense, I'd like to discuss it first and then come back before developing further. The analysis you've provided makes sense (including the gas benchmarks), but I'd like to see what projects are already using this ERC1155 variant just to confirm demand, can you share some examples? (if any) On a side note, we'd also like to consider if ERC1155 is the best fit if gas optimization is the purpose. Perhaps an alternative could be a non-fungible ERC-6909. |
|
Thanks @ernestognw. First major contract that comes to mind that uses ERC-1155 for nonfungible tokens is the ENS NameWrapper. On OpenSea, if never more than 1 of a token-id ever existed on a contract, the contract seems to be treated as an nonfungible token contract (e.g. under token transfers the amount is omitted rather than being shown as “1”). This is not a use of ERC-1155 for NFTs per se, but rather what appears to be support from a major platform for nonfungible ERC-1155 tokens. I’ll see if can compile some stats using The Graph or Etherscan API. ERC-6909 may well turn out to be a popular standard in the future, whereas ERC-1155 is supported by Etherscan, OpenSea and other major tools and applications right now. |
|
Hey @ernestognw, I have compiled a list of ERC-1155 mainnet contracts being used for nonfungible tokens. The ENS NameWrapper contract seems to be the ERC-1155 nonfungible token contract with the largest token supply, but there are also many others. Inspecting some of the top contracts, some (e.g. this and this) seem to be explicitly using the contract for nonfungibile tokens (mint amount of 1 is hardcoded), while (many) others are able to handle amounts ≥ 1, but still seem to be being used exclusively for amounts of 1 (which is why they made it to the the list.) |
23f739b to
e204a77
Compare
e204a77 to
954ebe6
Compare
As the ERC-1155 multi-token standard gains wider adoption by web3 platforms, using ERC-1155 for nonfungible tokens is gaining appeal, despite ERC-721 being more of a natural fit for tokens that can only have one owner. This is mostly because of ERC-1155 being more gas-efficient than ERC-721 as it tracks less information on-chain, and also because of other dev-friendly features like the URI-template scheme standardized by ERC-1155.
An ERC-1155 contract that enforces nonfungibility could be implemented naively using
openzeppelin-contractsas follows:However this is gas-inefficient because on every transfer, not only is the contract internally modifying both
_balances[id][from]and_balances[id][to](inERC1155grandparent), but the contract also needs to track the_totalSupply[id](inERC1155Supplyparent) in order to be aware of whether or not a token already exists.Moreover
ERC1155Supplyalso tracks_totalSupplyAll, which is not really required for a nonfungible ERC-1155 implementation. This can easily be remedied by deleting anytotalSupplyAll-related code from theERC1155Supplyparent. Let’s call this implementationERC1155NonfungibleNaiveLite.But
ERC1155NonfungibleNaiveLiteis still wasteful, and it turns out to be much more efficient if the nonfungibility constraint is exploited to simplify the internal data model to a model that for each unique minted token tracks its single owner (as in ERC-721), rather than tracking multiple balances per token.Indeed such a nonfungible ERC-1155 implementation, that tracks ownership rather than balances, has already been developed in the ERC1155D project.
ERC1155Dreimplements ERC-1155 from scratch, optimizing heavily for gas efficiency. On the other hand, in this PR we are proposing an alternative implementation, namedERC1155Nonfungible, that is written as an extension ofopenzeppelin-contract’sERC1155. LikeERC1155D,ERC1155Nonfungiblealso cuts down gas usage significantly by tracking ownership instead of balances, butERC1155Nonfungiblefocuses less on squeezing out up to the last drop of gas, and more on having a more concise implementation that fits better into thisopenzeppelin-contractslibrary.To evaluate
ERC1155Nonfungible, we create a simple test where we mint a token toaccount1, then transfer it fromaccount1toaccount2, and then burn the token. The test was run with all the different ERC-1155 implementations mentioned above, and also with theERC721contract, and the following gas usage was recorded:ERC1155NonfungibleandERC1155Doutperform all the other implementations significantly, which demonstrates that for nonfungible tokens it is worthwhile to reimplement using an ownership data model, rather than just enforce the nonfungible constraint onto the balances model. Between the winners,ERC1155Dperforms marginally better thanERC1155Nonfungible, asERC1155Nonfungiblesacrifices some gas optimization, but in exchange for being ~5 times more compact and fitting better into theopenzeppelin-contractslibrary architecture.As far as the
ERC1155Nonfungibleimplementation is concerned, some arbitrary decisions were taken:ERC1155Nonfungiblewith an alternative storage implementation, e.g. to store more token information along with the owner (even within the same storage slot if it can fit in 96 bits)ownerOf()is exposed externally, and it returns zero-address for a non-existent token, rather than reverting (as in ERC-721)ERC1155NonfungibleDuplicate. If we are to followERC721’s lead, we would flag this error condition by reverting withERC1155InvalidSender(address(0)), but it seems more clear to create a dedicated error.If there is interest to integrate
ERC1155Nonfungibleinto theopenzeppelin-contractslibrary, I will be happy to develop this PR further, add tests, integrate feedback back into the code, etc. Thanks.