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
Nonfungible ERC1155 implementation #4684
base: master
Are you sure you want to change the base?
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-contracts
as 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]
(inERC1155
grandparent), but the contract also needs to track the_totalSupply[id]
(inERC1155Supply
parent) in order to be aware of whether or not a token already exists.Moreover
ERC1155Supply
also tracks_totalSupplyAll
, which is not really required for a nonfungible ERC-1155 implementation. This can easily be remedied by deleting anytotalSupplyAll
-related code from theERC1155Supply
parent. Let’s call this implementationERC1155NonfungibleNaiveLite
.But
ERC1155NonfungibleNaiveLite
is 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.
ERC1155D
reimplements 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
,ERC1155Nonfungible
also cuts down gas usage significantly by tracking ownership instead of balances, butERC1155Nonfungible
focuses less on squeezing out up to the last drop of gas, and more on having a more concise implementation that fits better into thisopenzeppelin-contracts
library.To evaluate
ERC1155Nonfungible
, we create a simple test where we mint a token toaccount1
, then transfer it fromaccount1
toaccount2
, and then burn the token. The test was run with all the different ERC-1155 implementations mentioned above, and also with theERC721
contract, and the following gas usage was recorded:ERC1155Nonfungible
andERC1155D
outperform 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,ERC1155D
performs marginally better thanERC1155Nonfungible
, asERC1155Nonfungible
sacrifices some gas optimization, but in exchange for being ~5 times more compact and fitting better into theopenzeppelin-contracts
library architecture.As far as the
ERC1155Nonfungible
implementation is concerned, some arbitrary decisions were taken:ERC1155Nonfungible
with 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
ERC1155Nonfungible
into theopenzeppelin-contracts
library, I will be happy to develop this PR further, add tests, integrate feedback back into the code, etc. Thanks.