-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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: Non-fungible Token Standard #721
Comments
You could consider using an ipfs multiaddr as the return value for the token metadata. This would allow for a self describing IPFS hash, http url, or swarm addr in the future. |
Great point, @dob. Thanks for the suggestion! I've updated the draft above. (For reference, my original posting had suggested using the format |
wow! awesome integration! This is better than sending the raw data over the wire. :) |
Nice proposal! For the metadata to be useful, though, I think you need to mandate its format, rather than just recommend it. |
A much needed EIP! 👏 |
I would propose to use term "asset" instead of "nun-fungible token" to separate both terms. |
@Arachnid: The whole metadata method is optional, but if someone is going to implement it, they should probably implement it in a way that everyone else is expecting. I anticipate using "SHOULD" language for the full specification. @dip239: Ah naming things. Every programmer's favourite bikeshedding vortex. 😁 I bounced between three separate ideas for naming: "token" as seen above, "asset", following @dob's lead (and your suggestion!), and "NFT" which I used in the prose, but which is incredibly awkward in the API calls: "Asset" does seem like a decent choice, and my first personal draft used it. I discarded it for a two key reasons:
I did spend some time trying out other alternatives ("item", "object", "thing", etc.), but none seemed right. I am happy to hear other suggestions because I am not entirely content with "token". It just seems like the best of some bad options. |
@dete your justification for NFT sounds rational to me, but allowing myself to plunge into the vortex for a moment. Sometimes finding a word that is relatively unreserved in modern usage but means the same thing works. 'Tesserae' were ancient tokens or tiles used as theatre tickets, forms of religious authentication, etc (see: https://www2.warwick.ac.uk/fac/arts/classics/research/dept_projects/tcam/about/, https://www2.warwick.ac.uk/fac/arts/classics/research/dept_projects/tcam/blog/, https://en.wikipedia.org/wiki/Tessera_(commerce)). Seems like they did function as a kind of non-fungible potentially value-holding token, but were quite varied in their specific application, which might suit the present case. Then again maybe it is just trying a bit too hard... |
@dete I think you SHOULD use MUST instead. Otherwise, callers have no way to know how to interpret the return value correctly. |
Possible (but unlikely) names: ticket, badge, wafer, tile, marker |
@silasdavis: If this were a whole project, and not a single interface that is part of something larger, I'd jump on Tessera in a heartbeat. In that situation, a bit of an unusual name – with some history and context behind it – is pretty compelling. For something like an interface, tho, it's probably much better to stick with a term that people are already familiar with; even if it's imperfect. @tjayrush: Thanks for the suggestions! I don't think most of them work well, but "marker" might. I'm definitely going to stew on that one a bit more. If it weren't overloaded in such common usage as a writing implement, it would probably be just about perfect. |
@Arachnid: Happy to follow the community lead here, but if you look at ERC-20, they use "SHOULD" language for something as foundational as emitting |
Do not use ERC20 as an example of a good standard. A ton of people went and implemented the draft and then we had to finalize a "standard" that basically just listed what other people were doing. I don't believe anyone sees ERC20 as a good standard and everyone I have spoken to would like to see new better standards (hence why ERC223 exists). Specifically, ERC20 used SHOULD because the standard came after a bunch of implementations and not all implementations did the same thing, so the standard couldn't say MUST without causing a bunch of ERC20-like tokens to not be ERC20. |
Great context, @MicahZoltu. Thank you! |
@dete I'd also argue that the format of something is more foundational than whether you emit it or not. If you don't emit a transfer event, others can't track token transfers for your token - but if you don't require the format of a field, then they can't parse it anywhere. |
Hey @dete! I went ahead and coded a first draft of this. Names are slightly different. |
So, I was talking to @flockonus (another Solidity engineer here) and I was worrying about the "lost token" problem that ERC-223 tries to solve. (Essentially, ERC-20 has no mechanism to keep users from sending their tokens to contracts that don't know how to handle them, resulting in entirely unrecoverable coins. @Dexaran did some analysis estimating that something like $400k has been lost in this way!) The solution proposed by ERC-223 is to only allow transfers to contract addresses that implement the I talked to @flockonus because I was hesitant to add more complexity to NFTs, and he kind of floored me with a suggestion that seemed crazy at first, but is really growing on me: Just get rid of the So hear me out: What if the only way to transfer an NFT was for the owner to This solves three problems, two of which are shared with fungible tokens (and addressed by ERC-223), one that is specific to NFTs:
It introduces a new problem, of course: The simplest conceivable operation (transferring ownership from user to user) now requires two transactions instead of one. But how much of a problem is this, really? As time goes on, we'll see more and more transactions mediated by exchanges, smart contracts, UIs, and automated agents. One possible option would be to include I'd love to hear other folks thoughts! |
@dete It's an interesting idea, but I think that the problems of this approach outweigh its advantages because:
|
I thought about this some, and is it really that big of an issue? With the current ERC, each spam item would require its own transaction, so it would be a rather expensive attack, right? That said, what if there were two new optional methods:
These would allow "high volume" NFT contracts (like your MMO item example) to do "approved" bulk transfers, while the lower volume contracts would be naturally protected from spam via the requirement to send a single tokenId per transaction using the standard These methods would also allow one to merge/move wallets w/ 2 transactions instead of the |
About the point of removing the To start off, we have to understand that while a similar API to ERC20 is desirable, the case it aims to solve is different. In ERC20 the abstraction is the more you have the better because the tokens have positive monetary value, and go into an indistinguishable sum. That's not the case with non-fungible, either with a smart contract that tracks property ownership such as the one Dubai is implementing, or a game with relatively lower value assets, they might not always carry positive value or be desirable. So even with due consideration about it being an expensive attack, when designing a mechanism we should take into account that some users will have significant more purchase power than others, and still they shouldn't be able to harm others with less. My point is, if |
Upon further reflection and discussion with @flockonus, we are proposing to keep a straightforward (and therefore "unsafe") Our reasoning is as follows:
Similarly, as @flockonus mentioned about the potential "spam" problem: This is not a problem that the community standard needs to solve. If an implementor of an ERC-721 contract feels like spam is likely to be a problem with their NFT, they are welcome to include additional functionality to make it easy for users to mark their accounts as not accepting unsolicited transfers. |
Is Say I decide to sell a large portion of my NFT collection to someone else, and it contains hundreds of NFTs (or more). Shouldn't there be a standard way to transfer I can see the point of trying to keep the ERC simple, but am concerned that w/o a solution for bulk transfers the scope of what NFTs the ERC can be viable for is limited. |
Here is the new draft, which is basically the first "complete" draft. Any and all comments are welcome! Preamble
Simple SummaryA standard interface for non-fungible tokens. AbstractThis standard allows for the implementation of a standard API for non-fungible tokens (henceforth referred to as "NFTs") within smart contracts. This standard provides basic functionality to track and transfer ownership of NFTs. MotivationA standard interface allows any NFTs on Ethereum to be handled by general-purpose applications. In particular, it will allow for NFTs to be tracked in standardized wallets and traded on exchanges. SpecificationERC-20 Compatibilitynamefunction name() constant returns (string name) OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method. Returns the name of the collection of NFTs managed by this contract. - e.g. symbolfunction symbol() constant returns (string symbol) OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method. Returns a short string symbol referencing the entire collection of NFTs managed in this contract. e.g. "MNFT". This symbol SHOULD be short (3-8 characters is recommended), with no whitespace characters or new-lines and SHOULD be limited to the uppercase latin alphabet (i.e. the 26 letters used in English). totalSupplyfunction totalSupply() constant returns (uint256 totalSupply) Returns the total number of NFTs currently tracked by this contract. balanceOffunction balanceOf(address _owner) constant returns (uint256 balance) Returns the number of NFTs assigned to address Basic OwnershipownerOffunction ownerOf(uint256 _tokenId) constant returns (address owner) Returns the address currently marked as the owner of approvefunction approve(address _to, uint256 _tokenId) Grants approval for address Only one address can "have approval" at any given time; calling Successful completion of this method MUST emit an
Note: ANY change of ownership of an NFT – whether directly through the takeOwnershipfunction takeOwnership(uint256 _tokenId) Assigns the ownership of the NFT with ID This method MUST transfer ownership to
Important: Please refer to the Note in the transferfunction transfer(address _to, uint256 _tokenId) Assigns the ownership of the NFT with ID This method MUST transfer ownership to
A conforming contract MUST allow the current owner to "transfer" a token to themselves, as a way of affirming ownership in the event stream. (i.e. it is valid for Important: Please refer to the Note in the tokenOfOwnerByIndexfunction tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId) OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method. Returns the nth NFT assigned to the address Recommended usage is as follows: uint256 ownerBalance = nonFungibleContract.balanceOf(owner);
uint256[] memory ownerTokens = new uint256[](ownerBalance);
for (uint256 i = 0; i < ownerBalance; i++) {
ownerTokens[i] = nonFungibleContract.tokenOfOwnerByIndex(owner, i);
} Implementations MUST NOT assume that NFTs are accessed in any particular order by their callers (In particular, don't assume this method is called in a monotonically ascending loop.), and MUST ensure that calls to Callers of NOTE: Current limitations in Solidity mean that there is no efficient way to return a complete list of an address's NFTs with a single function call. Callers should not assume this method is implemented efficiently (from a gas standpoint) and should strenuously avoid calling this method "on-chain" (i.e. from any non- NFT MetadatatokenMetadatafunction tokenMetadata(uint256 _tokenId) constant returns (string infoUrl) OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method. Returns a multiaddress string referencing an external resource bundle that contains (optionally localized) metadata about the NFT associated with Standard sub-paths:
Each metadata subpath (including subpaths not defined in this standard) MUST contain a sub-path You can explore the metadata package referenced in this example here. EventsTransferThis event MUST trigger when NFT ownership is transferred via any mechanism. Additionally, the creation of new NFTs MUST trigger a Transfer event for each newly created NFTs, with a NOTE: A Transfer event with event Transfer(address indexed _from, address indexed _to, uint256 _tokenId) ApprovalThis event MUST trigger on any successful call to See the documentation for the event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId) RationaleUtilityThere are many proposed uses of Ethereum smart contracts that depend on tracking individual, non-fungible tokens (NFTs). Examples of existing or planned NFTs are LAND in Decentraland, the eponymous punks in CryptoPunks, and in-game items using systems like Dmarket or EnjinCoin. Future uses include tracking real-world non-fungible assets, like real-estate (as envisioned by companies like Ubitquity or Propy). It is critical in each of these cases that these items are not "lumped together" as numbers in a ledger, but instead, each token must have its ownership individually and atomically tracked. Regardless of the nature of these items, the ecosystem will be stronger if we have a standardized interface that allows for cross-functional non-fungible token management and sales platforms. NTF IDsThe basis of this standard is that every NFT is identified by a unique, 256-bit unsigned integer within its tracking contract. This ID number MUST NOT change for the life of the contract. The pair Backwards CompatibilityThis standard follows the semantics of ERC-20 as closely as possible, but can't be entirely compatible with it due to the fundamental differences between fungible and non-fungible tokens. Example non-fungible implementations as of September 2017:
(It should be noted that "limited edition, collectable tokens" like Curio Cards and Rare Pepe are not non-fungible tokens. They're actually a collection of individual fungible tokens, each of which is tracked by its own smart contract with its own total supply (which may be ImplementationReference implementation forthcoming... CopyrightCopyright and related rights waived via CC0. |
@dete You should update the initial issue with this draft, so people coming new to it don't need to scroll through all the comments. |
Thanks @Arachnid: I put a link in the first comment for now, I don't want to lose the older version for anyone who wants to follow the conversation. |
Does anyone have any thoughts on whether the
That seems... problematic... |
I don't believe that's quite accurate; you should be able to retrieve the contents of an indexed event (for a fixed length type) or its hash (for string/bytes/arrays). |
Any thoughts on transferring multiple tokens in a single transfer? We ran into this needs recently and the gas costs are making many options unfeasible. |
For metadata, I would strongly recommend requiring a standard URI. Perhaps consider a way to allow for linkage to the same metadata hosted on different storage networks (i.e. additional parameter specifying 'ipfs", 'swarm', or 'https'). This way the metadata has a better chance of persisting. Additionally, instead of an external resource bundle, I would recommend using JSON-LD. This format allows for more complex data, has wide use and tooling (being JSON), and provides context to that data |
First -- greed with @jpitts on creating redundant storage of metadata on different storage networks and, more importantly IMO, being more unopinionated with respect to the data storage network. I imagine a URI format along the lines of "swarm://XYZ" / "ipfs://XYZ" with the URI prefix specifying the specific storage network queried. Secondly, I'm working on tokenized debt issuance protocol (Dharma) and a big feature that would be extremely helpful to us is having built-in optional fungibility within each non-fungible asset. This may sound at odds with the purpose of NFTs, but I'll use Dharma as a tangible example: Alice wants to issue a bond token using Dharma, and she wants to be able to sell individual shares in that bond to different creditors. She could ostensibly have an NFT representing the debt asset as a whole, wrap that NFT into another fungible ERC20 token contract, and sell tokens from that contract to creditors, but that would require her to incur the gas associated with deploying an entirely new token contract for a set of very generic token transfer functionality. It would be much simpler and cheaper to be able to ask the contract to mint an NFT with its own fractional supply of X tokens. In a sense, this would mean that individual debts would be non-fungible with one another, but within each debt there would be a fractional supply of tokens that are fungible with one another. As an example, the function interface for
The standard could easily be made compatible with NFTs that have no fractional supply -- their issuance would simply have a fractional supply of 1. This is an advantageous arrangement for classes of digital assets that have (1) highly generic functionality and (2) require some sort of fractional fungibility and (2) don't merit the deployment of a smart contract for each issuance event. With all the above being said, I would definitely not say this is a must have -- this arguably extends beyond the definition of non-fungibility to a certain degree. If, however, this is a very common need among projects that have similar dynamics to the tokens they're issuing, I think this would be the appropriate standard in which to include this functionality. |
It seems totally logical that it should be mandatory for the SHA256-Hash (or other equivalent hash algo) of any file that is being created as an NFT to be included in the metadata of said NFT. Why isn't this a prerequisite, even if it is only enforced on the software level? I am surprised more people aren't talking about this shortfall, as many NFT sites allow you to upload the same image twice and create two distinct NFTs (I've replicated that test on mintable.app). In a perfect world, shouldn't there be error checking such as warning users "a file with that hash already exists on the blockchain" in order to prevent a collision? Also, for proving provenance later, the hash would be the ultimate proof, not the website URL it was hosted on and file name. I appreciate any feedback and suggestions on where this conversation could be best suited or if it is already being discussed in a subsequent EIP. |
It's really hard to mandate this, as isn't used right now, so sites would
still have to support non-compliant NFTs. It's also not nearly as simple as
you might think. NFTs aren't one size fits all. Some evolve over time. Some
are programmatically generated. Some have more than one piece of content
attached to them. Some aren't 1-of-1 unique. Some "wrap" other assets.
I do think that a feature such as this could be a good addition to NFT
contracts or the NFT ecosystem. For example, you could build an opt-in "NFT
registration" service that ensures uniqueness across all registered NFTs.
That might get enough uptake to make it become a de-facto standard, and it
would prevent some casual plagiarism. But most likely, it becomes a more
sophisticated avenue of attack - I can change one pixel in your art, and
upload it, and I've got a new NFT that the registration service confirms is
100% unique.
So most likely, this is something that would be limited to individual
contracts, so that the contract can prove the file is what the user says it
is. But that can already be done by storing the file on IPFS.
…On Wed, Apr 14, 2021 at 7:22 AM Steven Hatzakis ***@***.***> wrote:
It seems totally logical that it should be mandatory for the SHA256-Hash
(or other equivalent hash algo) of any file that is being created as an NFT
to be included in the metadata of said NFT. Why isn't this a prerequisite,
even if it is only enforced on the software level?
I am surprised more people aren't talking about this shortfall
<https://ownerfy.medium.com/the-importance-of-image-hashes-for-authenticating-nfts-7ad8ecc4f4d7>,
as many NFT sites allow you to upload the same image twice and create two
distinct NFTs (I've replicated that test on mintable.app).
In a perfect world, shouldn't there be error checking such as warning
users "a file with that hash already exists on the blockchain" in order to
prevent a collision?
Also, for proving provenance later, the hash would be the ultimate proof,
not the website URL it was hosted on and file name. I appreciate any
feedback and suggestions on where this conversation could be best suited or
if it is already being discussed in a subsequent EIP.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#721 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADDLHFKP6F3GDVLUGNUEMU3TIWQJPANCNFSM4D4EJ3HQ>
.
|
No backwards incompatible changes are being considered for ERC-721, so this issue is closed. Please ask questions like these elsewhere. You can ping me, I'm on most other places you might go. I don't have a specific one to recommend. |
now we are here |
Given that the ERC is finalised, what would be the case for proposing optional (no backwards-incompatible changes) extension to ERC721? Would it be better to propose a PR with the addition of the extension or would it be better to propose it as a separate EIP? |
@Daniel-K-Ivanov Create a new EIP that has a dependency on EIP-721. |
@Daniel-K-Ivanov create a derivative of EIP-721 where you could rent the EIP-721 itself. For ex. if you can slide the EIP-721 space in 100 small squares, you could rent the some of the square for a give time frame. It would the base to build a rent income stream on the EIP-721. |
@MicahZoltu created the EIP with dependency on EIP-721 |
@luforfor I think that you will be interested as-well |
@Daniel-K-Ivanov <https://github.com/Daniel-K-Ivanov> Yes I think so
…On Sat, 30 Oct 2021 at 18:26, Daniel Ivanov ***@***.***> wrote:
@Daniel-K-Ivanov <https://github.com/Daniel-K-Ivanov> create a derivative
of EIP-721 where you could rent the EIP-721 itself. For ex. if you can
slide the EIP-721 space in 100 small squares, you could rent the some of
the square for a give time frame. It would the base to build a rent income
stream on the EIP-721.
@luforfor <https://github.com/luforfor> I think that you will be
interested as-well
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#721 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACML5EJ6K5HPXO7KMGOJMOTUJQTETANCNFSM4D4EJ3HQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
I will keep this short and simple. Basically, when an event log for Transfer is emitted from a contract that is ERC20, it has the Topics[0] ID of 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, which is ultimately derived from Transfer(address,address,uint256). The problem is that ERC721 also has the exact same Topics[0] ID, but with a different use case. Namely, the uint256 is for TokenID and not for Amount. Something should be added to the Topics ID such that it can be determined that the events are different. |
@daweth We are quite too late to consider changing this. But you can differentiate an ERC-721 contract from an ERC-20 contract by performing ERC-165 introspection. This is discussed under "ERC-165 Interface" in the EIP. |
I'm currently researching EIP-2981 NFT Royalty Standard and was wondering if the subject of royalties was discussed during the development of EIP-721/ERC-721. Is there anything to be found on this matter? Any discussion on the scope of EIP-721? |
As far as I know there was not discussion on that
…On Mon, 29 Nov 2021 at 17:37, Stefan de Jong ***@***.***> wrote:
I'm currently researching EIP-2981 NFT Royalty Standard and was wondering
if the subject of royalties was discussed during the development of
EIP-721/ERC-721.
Is there anything to be found on this matter? Any discussion on the scope
of EIP-721?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#721 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACML5EJPULXNYSLDQBDAAULUOOT5XANCNFSM4D4EJ3HQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
Standards ERC-20 Token Standard. ERC-165 Standard Interface Detection. ERC-173 Owned Standard. ERC-223 Token Standard. ERC-677 transferAndCall Token Standard. ERC-827 Token Standard. Ethereum Name Service (ENS). https://ens.domains Instagram – What’s the Image Resolution? https://help.instagram.com/1631821640426723 JSON Schema. https://json-schema.org/ Multiaddr. https://github.com/multiformats/multiaddr RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt Issues The Original ERC-721 Issue. ethereum/EIPs#721 Solidity Issue OpenZeppelin#2330 – Interface Functions are External. ethereum/solidity#2330 Solidity Issue OpenZeppelin#3412 – Implement Interface: Allow Stricter Mutability. ethereum/solidity#3412 Solidity Issue OpenZeppelin#3419 – Interfaces Can’t Inherit. ethereum/solidity#3419 Solidity Issue OpenZeppelin#3494 – Compiler Incorrectly Reasons About the selector Function. ethereum/solidity#3494 Solidity Issue OpenZeppelin#3544 – Cannot Calculate Selector of Function Named transfer. ethereum/solidity#3544 CryptoKitties Bounty Issue OpenZeppelin#4 – Listing all Kitties Owned by a User is O(n^2). dapperlabs/cryptokitties-bounty#4 OpenZeppelin Issue OpenZeppelin#438 – Implementation of approve method violates ERC20 standard. OpenZeppelin#438 Solidity DelegateCallReturnValue Bug. https://solidity.readthedocs.io/en/develop/bugs.html#DelegateCallReturnValue Discussions Reddit (announcement of first live discussion). https://www.reddit.com/r/ethereum/comments/7r2ena/friday_119_live_discussion_on_erc_nonfungible/ Gitter #EIPs (announcement of first live discussion). https://gitter.im/ethereum/EIPs?at=5a5f823fb48e8c3566f0a5e7 ERC-721 (announcement of first live discussion). ethereum/EIPs#721 (comment) ETHDenver 2018. https://ethdenver.com NFT Implementations and Other Projects CryptoKitties. https://www.cryptokitties.co 0xcert ERC-721 Token. https://github.com/0xcert/ethereum-erc721 Su Squares. https://tenthousandsu.com Decentraland. https://decentraland.org CryptoPunks. https://www.larvalabs.com/cryptopunks DMarket. https://www.dmarket.io Enjin Coin. https://enjincoin.io Ubitquity. https://www.ubitquity.io Propy. https://tokensale.propy.com CryptoKitties Deployed Contract. https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code Su Squares Bug Bounty Program. https://github.com/fulldecent/su-squares-bounty XXXXERC721. https://github.com/fulldecent/erc721-example ERC721ExampleDeed. https://github.com/nastassiasachs/ERC721ExampleDeed Curio Cards. https://mycuriocards.com Rare Pepe. https://rarepepewallet.com Auctionhouse Asset Interface. https://github.com/dob/auctionhouse/blob/master/contracts/Asset.sol OpenZeppelin SafeERC20.sol Implementation. https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/SafeERC20.sol
just curios about why the standard define these mothods with
|
diet's draft in comment #721 (comment) was never accepted and is not canonical, is that right? |
@lucasgonze Correct. The official version is published, please see link at top of top comment. Additionally, it is at: https://eips.ethereum.org/EIPS/eip-721 |
Sorry if this has been addressed but I'm curious what the function is for web3 urls such as .eth or .bitcoin etc and will this include the ipfs://XYZ url call. I disagree with any attempt to rename or split nfts (as some form of property/asset). An NFT is by it's nature a creation or collection of 1s and 0s if you will. It is by its' organization a complex compilation of these digits into an electronic facsimile of a real thing, but not the thing itself. I think standards EIP-#721 and #20 reflect and embody this concept well and can and will evolve where there is need. |
Hello @freegold, please direction all questions about using ERC-721 NFTs, to a developer forum, such as Ethereum Stack Exchange. Also I have a call every Tuesday at 6pm New York for Q&A ("Community Service Hour") that you are welcome to join. Please see https://phor.net/#hour if interested. |
. |
This proposal has been accepted and merged as a draft standard, please see the officially tracked version for the current draft.
Please see PR #841 for the discussions leading up to this draft, and use this thread (#721) for further discussion. (Or, if you have a concrete proposal, consider opening a new PR with your proposed changes.)
Original Draft (Sep 20, 2017)
Preamble
Simple Summary
A standard interface for non-fungible tokens.
Abstract
The following standard allows for the implementation of a standard API for non-fungible tokens (henceforth referred to as "NFTs") within smart contracts. This standard provides basic functionality to track and transfer ownership of NFTs.
Motivation
A standard interface allows any NFTs on Ethereum to be handled by general-purpose applications. In particular, it will allow for NFTs to be tracked in standardized wallets and traded on exchanges.
Specification
I wanted to get the community's "first impression" before spending a bunch of time detailing out these end-points; expect this section to be significantly expanded after the first round of feedback. I've left out "obvious" return values for skimmability, and included a few notes where the functionality warrants special interest.
name()
optionalsymbol()
optionaltotalSupply()
- Number of NFTs tracked by this contractbalanceOf(address _owner)
- Number of NFTs owned by a particular addresstokensOfOwnerByIndex(address _owner, uint _index) constant returns (uint tokenId)
- There's really no good way to return a list of NFTs by owner, but it's valuable functionality. You should strenuously avoid calling this method "on-chain" (i.e. from a non-constant
contract function).ownerOf(uint _tokenId) constant returns (address owner)
transfer(address _to, uint _tokenId)
approve(address _to, uint _tokenId)
– SHOULD be cleared by anytransfer()
operationtransferFrom(address _from, address _to, unit _tokenId)
- the sender must have been previously authorized byapprove()
. (Note: Technically, the_from
address here can be inferred by callingownerOf(_tokenId)
. I've left it in for symmetry with the corresponding ERC-20 method, and to forestall the (somewhat subtle) bug that could result from not clearing theapprove
authorization inside a successfultransfer
call.)tokenMetadata(uint _tokenId) returns (string infoUrl)
- recommended format is IPFS or HTTP multiaddress withname
,image
, anddescription
sub-paths. IPFS is the preferred mechanism (immutable and more durable). Example: IftokenMetadata()
returns/ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
, the object description would be accessible viaipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/description
.Rationale
There are many proposed uses of Ethereum smart contracts that depend on tracking individual, non-fungible tokens (NFTs). Examples of existing or planned NFTs are LAND in Decentraland, the eponymous punks in CryptoPunks, and in-game items using systems like Dmarket or EnjinCoin. Future uses include tracking real-world non-fungible assets, like real-estate (as envisioned by companies like Ubitquity or Propy). It is critical in each of these cases that these items are not "lumped together" as numbers in a ledger, but instead each token must have its ownership individually and atomically tracked. Regardless of the nature of these items, the ecosystem will be stronger if we create a standardized interface that allows for cross-functional non-fungible token management and sales platforms.
The basis of this standard is that every NFT is identified by a unique, 256-bit unsigned integer within its tracking contract. The pair
(contract address, asset ID)
will then be globally unique within the Ethereum ecosystem.This standard has followed the model of ERC-20 as much as possible to minimize the effort required for wallets (in particular) to track non-fungible tokens, while echoing a well-understood standard.
Backwards Compatibility
This standard follows the semantics of ERC-20 as closely as possible, but can't be entirely compatible with it due to the fundamental differences between fungible and non-fungible tokens.
Example non-fungible implementations as of September, 2017:
(It should be noted that "limited edition, collectable tokens" like Curio Cards and Rare Pepe are not non-fungible tokens. They're actually a collection of individual fungible tokens, each of which is tracked by its own smart contract with its own total supply (which may be
1
in extreme cases).)Implementation
Reference implementation forthcoming...
Copyright
Copyright and related rights waived via CC0.
Second Draft (Nov 9, 2017)
Preamble
Simple Summary
A standard interface for non-fungible tokens.
Abstract
This standard allows for the implementation of a standard API for non-fungible tokens (henceforth referred to as "NFTs") within smart contracts. This standard provides basic functionality to track and transfer ownership of NFTs.
Motivation
A standard interface allows any NFTs on Ethereum to be handled by general-purpose applications. In particular, it will allow for NFTs to be tracked in standardized wallets and traded on exchanges.
Specification
ERC-20 Compatibility
name
OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.
Returns the name of the collection of NFTs managed by this contract. - e.g.
"My Non-Fungibles"
.symbol
OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.
Returns a short string symbol referencing the entire collection of NFTs managed in this contract. e.g. "MNFT". This symbol SHOULD be short (3-8 characters is recommended), with no whitespace characters or new-lines and SHOULD be limited to the uppercase latin alphabet (i.e. the 26 letters used in English).
totalSupply
Returns the total number of NFTs currently tracked by this contract.
balanceOf
Returns the number of NFTs assigned to address
_owner
.Basic Ownership
ownerOf
Returns the address currently marked as the owner of
_tokenID
. This method MUSTthrow
if_tokenID
does not represent an NFT currently tracked by this contract. This method MUST NOT return 0 (NFTs assigned to the zero address are considered destroyed, and queries about them shouldthrow
).approve
Grants approval for address
_to
to take possession of the NFT with ID_tokenId
. This method MUSTthrow
ifmsg.sender != ownerOf(_tokenId)
, or if_tokenID
does not represent an NFT currently tracked by this contract, or ifmsg.sender == _to
.Only one address can "have approval" at any given time; calling
approveTransfer
with a new address revokes approval for the previous address. Calling this method with 0 as the_to
argument clears approval for any address.Successful completion of this method MUST emit an
Approval
event (defined below) unless the caller is attempting to clear approval when there is no pending approval. In particular, an Approval event MUST be fired if the_to
address is zero and there is some outstanding approval. Additionally, an Approval event MUST be fired if_to
is already the currently approved address and this call otherwise has no effect. (i.e. Anapprove()
call that "reaffirms" an existing approval MUST fire an event.)Note: ANY change of ownership of an NFT – whether directly through the
transfer
andtransferFrom
methods defined in this interface, or through any other mechanism defined in the conforming contract – MUST clear any and all approvals for the transferred NFT. The implicit clearing of approval via ownership transfer MUST also fire the eventApproval(0, _tokenId)
if there was an outstanding approval. (i.e. All actions that transfer ownership must emit the same Approval event, if any, as would emitted by callingapprove(0, _tokenID)
.)takeOwnership
Assigns the ownership of the NFT with ID
_tokenId
tomsg.sender
if and only ifmsg.sender
currently has approval (via a previous call toapproveTransfer
). A successful transfer MUST fire theTransfer
event (defined below).This method MUST transfer ownership to
msg.sender
orthrow
, no other outcomes can be possible. Reasons for failure include (but are not limited to):msg.sender
does not have approval for_tokenId
_tokenID
does not represent an NFT currently tracked by this contractmsg.sender
already has ownership of_tokenId
Important: Please refer to the Note in the
approveTransfer
method description; a successful transfer MUST clear pending approval.transfer
Assigns the ownership of the NFT with ID
_tokenId
to_to
if and only ifmsg.sender == ownerOf(_tokenId)
. A successful transfer MUST fire theTransfer
event (defined below).This method MUST transfer ownership to
_to
orthrow
, no other outcomes can be possible. Reasons for failure include (but are not limited to):msg.sender
is not the owner of_tokenId
_tokenID
does not represent an NFT currently tracked by this contract_to
is 0 (Conforming contracts MAY have other methods to destroy or burn NFTs, which are conceptually "transfers to 0" and will emitTransfer
events reflecting this. However,transfer(0, tokenID)
MUST be treated as an error.)A conforming contract MUST allow the current owner to "transfer" a token to themselves, as a way of affirming ownership in the event stream. (i.e. it is valid for
_to == ownerOf(_tokenID)
.) This "no-op transfer" MUST be considered a successful transfer, and therefore MUST fire aTransfer
event (with the same address for_from
and_to
).Important: Please refer to the Note in the
approveTransfer
method description; a successful transfer MUST clear pending approval. This includes no-op transfers to the current owner!tokenOfOwnerByIndex
OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.
Returns the nth NFT assigned to the address
_owner
, with n specified by the_index
argument. This method MUSTthrow
if_index >= balanceOf(_owner)
.Recommended usage is as follows:
Implementations MUST NOT assume that NFTs are accessed in any particular order by their callers (In particular, don't assume this method is called in a monotonically ascending loop.), and MUST ensure that calls to
tokenOfOwnerByIndex
are fully idempotent unless and until some non-constant
function is called on this contract.Callers of
tokenOfOwnerByIndex
MUST never assume that the order of NFTs is maintained outside of a single operation, or through the invocation (direct or indirect) of any non-constant
contract method.NOTE: Current limitations in Solidity mean that there is no efficient way to return a complete list of an address's NFTs with a single function call. Callers should not assume this method is implemented efficiently (from a gas standpoint) and should strenuously avoid calling this method "on-chain" (i.e. from any non-
constant
contract function, or from anyconstant
contract function that is likely to be called on-chain).NFT Metadata
tokenMetadata
OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.
Returns a multiaddress string referencing an external resource bundle that contains (optionally localized) metadata about the NFT associated with
_tokenId
. The string MUST be an IPFS or HTTP(S) base path (without a trailing slash) to which specific subpaths are obtained through concatenation. (IPFS is the preferred format due to better scalability, persistence, and immutability.)Standard sub-paths:
name
sub-path MUST contain the UTF-8 encoded name of the specific NFT (i.e. distinct from the name of the collection, as returned by the contract'sname
method). A name SHOULD be 50 characters or less, and unique amongst all NFTs tracked by this contract. A name MAY contain white space characters, but MUST NOT include new-line or carriage-return characters. A name MAY include a numeric component to differentiate from similar NFTs in the same contract. For example: "Happy Token Dynamic gas pricing for opcodes (miner decided opcode pricing) #157".image
sub-path exists, it MUST contain a PNG, JPEG, or SVG image with at least 300 pixels of detail in each dimension. The image aspect ratio SHOULD be between 16:9 (landscape mode) and 2:3 (portrait mode). The image SHOULD be structured with a "safe zone" such that cropping the image to a maximal, central square doesn't remove any critical information. (The easiest way to meet this requirement is simply to use a 1:1 image aspect ratio.)description
sub-path exists, it MUST contain a UTF-8 encoded textual description of the asset. This description MAY contain multiple lines and SHOULD use a single new-line character to delimit explicit line-breaks, and two new-line characters to delimit paragraphs. The description MAY include CommonMark-compatible Markdown annotations for styling. The description SHOULD be 1500 characters or less.Each metadata subpath (including subpaths not defined in this standard) MUST contain a sub-path
default
leading to a file containing the default (i.e. unlocalized) version of the data for that metadata element. For example, an NFT with the metadata path/ipfs/QmZU8bKEG8fhcQwKoLHfjtJoKBzvUT5LFR3f8dEz86WdVe
MUST contain the NFT's name as a UTF-8 encoded string available at the full path/ipfs/QmZU8bKEG8fhcQwKoLHfjtJoKBzvUT5LFR3f8dEz86WdVe/name/default
. Additionally, each metadata subpath MAY have one or more localizations at a subpath of an ISO 639-1 language code (the same language codes used for HTML). For example,/ipfs/QmZU8bKEG8fhcQwKoLHfjtJoKBzvUT5LFR3f8dEz86WdVe/name/en
would have the name in English, and/ipfs/QmZU8bKEG8fhcQwKoLHfjtJoKBzvUT5LFR3f8dEz86WdVe/name/fr
would have the name in French (note that even localized values need to have adefault
entry). Consumers of NFT metadata SHOULD look for a localized value before falling back to thedefault
value. Consumers MUST NOT assume that all metadata subpaths for a particular NFT are localized similarly. For example, it will be common for thename
andimage
objects to not be localized even when thedescription
is.You can explore the metadata package referenced in this example here.
Events
Transfer
This event MUST trigger when NFT ownership is transferred via any mechanism.
Additionally, the creation of new NFTs MUST trigger a Transfer event for each newly created NFTs, with a
_from
address of 0 and a_to
address matching the owner of the new NFT (possibly the smart contract itself). The deletion (or burn) of any NFT MUST trigger a Transfer event with a_to
address of 0 and a_from
address of the owner of the NFT (now former owner!).NOTE: A Transfer event with
_from == _to
is valid. See thetransfer()
documentation for details.Approval
This event MUST trigger on any successful call to
approve(address _spender, uint256 _value)
(unless the caller is attempting to clear approval when there is no pending approval).See the documentation for the
approve()
method above for further detail.Rationale
Utility
There are many proposed uses of Ethereum smart contracts that depend on tracking individual, non-fungible tokens (NFTs). Examples of existing or planned NFTs are LAND in Decentraland, the eponymous punks in CryptoPunks, and in-game items using systems like Dmarket or EnjinCoin. Future uses include tracking real-world non-fungible assets, like real-estate (as envisioned by companies like Ubitquity or Propy). It is critical in each of these cases that these items are not "lumped together" as numbers in a ledger, but instead, each token must have its ownership individually and atomically tracked. Regardless of the nature of these items, the ecosystem will be stronger if we have a standardized interface that allows for cross-functional non-fungible token management and sales platforms.
NTF IDs
The basis of this standard is that every NFT is identified by a unique, 256-bit unsigned integer within its tracking contract. This ID number MUST NOT change for the life of the contract. The pair
(contract address, asset ID)
will then be a globally unique and fully-qualified identifier for a specific NFT within the Ethereum ecosystem. While some contracts may find it convenient to start with ID 0 and simply increment by one for each new NFT, callers MUST NOT assume that ID numbers have any specific pattern to them, and should treat the ID as a "black box".Backwards Compatibility
This standard follows the semantics of ERC-20 as closely as possible, but can't be entirely compatible with it due to the fundamental differences between fungible and non-fungible tokens.
Example non-fungible implementations as of September 2017:
(It should be noted that "limited edition, collectable tokens" like Curio Cards and Rare Pepe are not non-fungible tokens. They're actually a collection of individual fungible tokens, each of which is tracked by its own smart contract with its own total supply (which may be
1
in extreme cases).)Implementation
Reference implementation forthcoming...
Copyright
Copyright and related rights waived via CC0.
The text was updated successfully, but these errors were encountered: