Skip to content
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

NEP: Non-fungible Token Standard #41

Open
wants to merge 7 commits into
base: master
from

Conversation

8 participants
@hal0x2328
Copy link
Contributor

hal0x2328 commented Apr 19, 2018

Initial NFT proposal from Splyse/HashPuppies team

hal0x2328 added some commits Apr 19, 2018

Merge pull request #1 from Splyse/NFT
Create nep-10.mediawiki
@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Apr 20, 2018

Now we have 3 different NFT Standard proposals: #30 , #37 , #41 .
We need to choose one to accept.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Apr 22, 2018

Have you considered a divisible non fungible token? Easier to explain with an example.

Let's see we want to put a house on the blockchain. There could be one NFT that represents this house, however due to the immense value it holds, houses are split up into pieces in some sort of real estate trust. So each person might own 10% of each house. This allows for high value objects to be tokenized more effectively.

The whole is non fungible even if the the individual pieces of aren't. This also works really well for tokenized art.

If an art piece is worth 50 million dollars, it is hard for own person to own as one non fungible token. However if it is tokenized into 100 fungible parts than it becomes much easier for collective ownership.

I bring this up because NEO seems to be an excellent place to put high value (singular )non fungible items as a single contract, and then tokenizing the individual non fungible entity into smaller (fungible or non fungible) parts. This leads into the process of digitizing real world assets

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Apr 22, 2018

@saltyskip This sounds interesting. Please make a specific proposal.

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented Apr 23, 2018

@saltyskip An NFT token could have multiple owners currently a using multi-signature address, in the same way a group of owners could own a batch of NEP-5 tokens together. But the percentage of ownership isn't part of the multi-sig standard so that data would not be taken into account.

However with our NFT proposal, the contract owner could serialize the information about owner percentages into the RWData storage and change it any time the token changes hands (i.e. M of N keyholders sign the token over to a new multi-sig address). It would require some coordination however to register new multi-sig ownership hashes during the transfer() event.

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Apr 23, 2018

A multi-sig address means multiple owners can own one NFT, doesn't mean someone can own a part of one NFT.

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented Apr 23, 2018

@erikzhang By virtue of the fact that they together own one NFT, doesn't that mean they each technically own a part of one NFT? The multi-sig method could serve most use cases except where you want to sell your share without the consent of the other owners and cooperation of the token contract (which is what I think you are trying to illustrate with your comment). Yes, that would require a different model where parts of the token have to exist as a unique entity with a single owner.

Perhaps a master meta-token type could be created that is just composed of a list of other tokens and the relative percentages of ownership they represent. But, now you have to consider divisibility of the sub-tokens - what if I want to sell my 10% share to two people, one of whom pays me 80% and the other and 20% of the share, giving them a resulting 8% and 2% stake in the master token? If the token share owner can't create new child tokens at will through the act of transferring them this scheme doesn't work.

Really interesting to think about, would love to add thoughts to the new proposal from @saltyskip when it is added.

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented May 10, 2018

@hal0x2328 I have some questions for you to answer.

  1. As a Non-fungible Token Standard, why we need circulation and balanceOf? Since each token is not the same, there should be no need to count the number of tokens in an account?

  2. In the case of a transfer, if we know the tokenid, we can get the owner of it. So there's no need to provide the from parameter for transfer?

  3. As a Non-fungible Token Standard, why we need decimals? Does this mean that each token is still divisible as @saltyskip has said? If so, then the balanceOf method should accept tow parameters: owner and tokenid.

  4. Do we really need RWData and URIData? I guess a lot of tokens may just need ROData. Maybe we can simplify the standard?

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented May 10, 2018

In addition, there is a description in the ERC721:

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 any constant contract function that is likely to be called on-chain).

But in Neo, we have great ways to return the complete list of NFTs. So we really don't need tokenOfOwnerByIndex.

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented May 10, 2018

As a Non-fungible Token Standard, why we need circulation and balanceOf? Since each token is not the same, there should be no need to count the number of tokens in an account?

circulation is a synonym for totalSupply. This was done to maintain compatibility with the NEX ICO template in case external code relies on that operation since it exists in other NEP-5 implementations.

balanceOf would be used in conjunction with tokenOfOwnerById - we need to know how many items exist to iterate over - see the reasoning for including tokenOfOwnerById below and see if it makes sense.

In the case of a transfer, if we know the tokenid, we can get the owner of it. So there's no need to provide the from parameter for transfer?

This parameter could be eliminated, true.

As a Non-fungible Token Standard, why we need decimals? Does this mean that each token is still divisible as @saltyskip has said? If so, then the balanceOf method should accept tow parameters: owner and tokenid.

This is just to maintain compatibility with existing wallets, explorers, etc that expect that method to exist as it does with NEP-5, and might even be a way they use to differentiate (at the time, the supportedStandards proposal was not yet in-the-works). However, since it always returns zero, eliminating would effectively have the same result as long as the NFT implementation returned a False value at the end of execution where no operations match. I've seen some developers returning -1 from contract errors, so keeping this function and forcing Decimals to always return 0 seemed reasonable.

Do we really need RWData and URIData? I guess a lot of tokens may just need ROData. Maybe we can simplify the standard?

We were trying to envision the most common use cases and develop a uniform way to satisfy all of them. In our game, non-fungible assets will not only have "genetics" but will also have transient properties that should be attached to the asset and carry over when the token is transferred. We figure once gaming or other NFT assets become more complex this would be a common requirement, and querying these properties should be done in a uniform way. And URIData would also likely be a pretty globally-needed feature too, we expect that NFT assets are going to need some sort of representation of their uniqueness in image form for human eyes.

But in Neo, we have great ways to return the complete list of NFTs. So we really don't need tokenOfOwnerByIndex.

It seemed to us you still would want a way for your off-chain code to request one token at a time from a subset in a predictable order from the contract instead of retrieving the entire list every time (which eventually might grow quite large), and maintaining the Ethereum method for doing so might make it easier for NFT-using Ethereum dApps to be ported to Neo.

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented May 10, 2018

circulation is a synonym for totalSupply. This was done to maintain compatibility with the NEX ICO template in case external code relies on that operation since it exists in other NEP-5 implementations.

We are not going to make NFT standard be compatible with NEP-5. Also, adding methods for compatibility with the NEX ICO template is not the right choice, and we should make this standard more focused on the needs of NFT itself. NEX can modify their templates for NFT standard.

However, since it always returns zero, eliminating would effectively have the same result as long as the NFT implementation returned a False value at the end of execution where no operations match. I've seen some developers returning -1 from contract errors, so keeping this function and forcing Decimals to always return 0 seemed reasonable.

If an error occurs in the contract, the exception should be thrown by the THROW opcode directly. If decimals always returns zero, then it is really not necessary to exist.

And URIData would also likely be a pretty globally-needed feature too, we expect that NFT assets are going to need some sort of representation of their uniqueness in image form for human eyes.

Agree with that. But I don't think RWData would be a common requirement. And perhaps RWData is more suitable for storage offchain.

It seemed to us you still would want a way for your off-chain code to request one token at a time from a subset in a predictable order from the contract instead of retrieving the entire list every time (which eventually might grow quite large)

Perhaps we can use the Iterator APIs to return the token iterator instead of the token list? And, if it's off-chain code, we don't have to care about the size of the data.

and maintaining the Ethereum method for doing so might make it easier for NFT-using Ethereum dApps to be ported to Neo.

I don't think we can port Ethereum dApps easily since there are many difference between NeoVM and EVM.

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented May 11, 2018

I've been thinking about @saltyskip proposed divisible non-fungible token. Here are some of my simple ideas:

Divisibility Extension For Non-fungible Token

Methods

  1. decimals()

Methods (if decimals() > 0)

  1. balanceOf(owner, tokenid)
  2. transferPartially(tokenid, to, amount)

Events (if decimals() > 0)

  1. transfer(tokenid, to, amount)
@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented May 22, 2018

@hal0x2328 @saltyskip @lightszero @RunLyo
I amended this standard to include optional divisibility and remove some non-essential methods.
Please review it.

@erikzhang erikzhang referenced this pull request May 23, 2018

Merged

add Enumerator APIs #244

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented May 31, 2018

Aren't we moving away from optional methods due to recent proposal of composite smart contracts? If that is the case then we should move the divisibility into a separate proposal in my opinion.

I'm a fan of this because it allows us to establish the simplest possible base layer for the non fungible tokens, and then move into more complex ideas via the composite smart contract

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented May 31, 2018

Aren't we moving away from optional methods due to recent proposal of composite smart contracts? If that is the case then we should move the divisibility into a separate proposal in my opinion.

I think this can be an exception because we can easily check decimals() > 0to know whether those divisibility methods are available.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Jun 1, 2018

It seems like we must pick one or the other.

Either
A) Proposals never have optional methods, and instead each one is represented as a composite smart contract.

For example NEP-5 would only consist of required methods. IF it had optional methods then the smart contract would return the supported proposals

[5, 5.1]

This would show that the optional methods are indeed present in the contract

The alternative is B

B) Contracts have optional methods. However in my opinion this defeats the purpose of having composite smart contracts in the first place.

@mwherman2000 maybe you have some thoughts on this matter

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Jun 1, 2018

I think contracts can have optional methods, so long as we can detect their existence in a certain way.

For NFT, we can detect it by checking decimals() > 0.

@mwherman2000

This comment has been minimized.

Copy link
Contributor

mwherman2000 commented Jun 1, 2018

@erikzhang I thought we just agreed that NEPs will no longer have optional operations/methods?

Discussion: #40 (comment)

Your agreement: #40 (comment)

There shouldn't be any "backdoors" such as testing for the number of decimals - else people will start creating all sorts of non-standard ways of detecting whether an optional operation/method exists ...which goes against what we've accomplished with NEP-10.

Where do we stand on this?

@mwherman2000

This comment has been minimized.

Copy link
Contributor

mwherman2000 commented Jun 1, 2018

RE: 5.1

I don't think we should start using decimals in the NEP numbering scheme. I've started a specific discussion here: #50

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Jun 2, 2018

I thought we just agreed that NEPs will no longer have optional operations/methods?

Any result requires a reason. What are the reasons why we do not allow optional methods? It is because it's difficult for us to know whether these methods exist by NEP numbers. For NFT, we have a very simple way to determine. In this case, splitting it into two NEPs is a very inefficient thing and creates unnecessary troubles in the future when it is used.

@mwherman2000

This comment has been minimized.

Copy link
Contributor

mwherman2000 commented Jun 2, 2018

RE: "It is because it's difficult for us to know whether these methods exist by NEP numbers."

@erikzhang What is difficult about it?

@jhwinter

This comment has been minimized.

Copy link

jhwinter commented Aug 15, 2018

I'm new to the NEO community, so I'm probably pretty ignorant on a good bit of what you guys are talking about. However, reading through these comments and the posted links, I'd say that composite smart contracts make more sense than optional operations/methods. Having special use cases or exceptions where we allow optional methods would defeat the purpose of having standards as well as make it less obvious how to interact with smart contracts.
If I query a smart contract and it tells me it supports standards [10, 5, 1234], then I have some base knowledge of what data I can ask from it. In my eyes, that makes things more consistent and robust.
I like @saltyskip's ideas for divisible NFT's. However, I agree with his earlier comment that divisible NFT should be in its own separate proposal and that this proposal should focus on establishing the base NFT implementation.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Oct 1, 2018

How can we move this proposal forward? What outstanding questions do we still need to answer?

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Oct 3, 2018

The problem now is that we don't know if this standard works. More projects are needed to use this standard to verify that it is perfect.

@erikzhang erikzhang added the accepted label Oct 3, 2018

@zlumer zlumer referenced this pull request Oct 9, 2018

Open

Multi-Token Contract #71

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Feb 28, 2019

Hello, I've started working on some implementations regarding NFT's. First off great work! 👏 👏 One question I have is about a tokens read only properties. I wonder if the admin should be able to modify these like they are able to modify the URI.

I can see the potential use case where a token "levels up" for instance you initially have a silver membership and then an admin levels you up to a gold membership upon some task completion.

If we are concerned about immutability, maybe there needs to be a readonly properties section and a write properties section so that the admin has modification access to the write portion of the NFT

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Feb 28, 2019

I think that each application has different business logic, they can define whether they need read-only data, and how to read and write such data.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 1, 2019

So is it preferred that all token information is located at the URI instead of inside of some storage area in the contract?

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented Mar 1, 2019

So is it preferred that all token information is located at the URI instead of inside of some storage area in the contract?

In our contract we are using the URI field for an image preview link, and that URI location can be updated by the contract owner (for instance if we need to migrate the image storage to another provider).

We're using the read-only properties field for storing all the read-only data associated with the token (in our case the DNA of a virtual puppy) which should never change over the life of the token.

We are considering adding a read-write field that is custom to our application to have more flexibility in terms of things like "leveling up". This didn't make it into the final NEP per Erik's feedback at the time but there's no reason dApps can't add it as needed, it's not likely their requirements for a read-write storage field format would be compatible with each other anyway.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 1, 2019

It would be very cumbersome for wallet to be able to access NFT if there is no agreed upon place where read/write properties are stored as a part of this standard. Additionally it would be good to have some basic format for the listed properties.

Is it a json object?

I like @hal0x2328 approach of having a storage area in the contract for read properties whereas the URI points elsewhere. Can we amend the proposal to have the read and write storage areas for the NFT attributes?

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented Mar 1, 2019

It would be very cumbersome for wallet to be able to access NFT if there is no agreed upon place where read/write properties are stored as a part of this standard.

I don't imagine wallets would need read or display this information, given the wide variety of use cases for NFT. This should be a storage area for for the dApp's code to read and write, and also for people who want to build specialized explorer add-ons because they can bring value to the dApp's users.

A wallet shouldn't want to display every parameter of a HashPuppy's state. A list of token IDs with an associated preview image should be all they are worried about including, otherwise it doesn't scale for the wallet developers once there are dozens of different NFTs in the ecosystem all representing different types of things.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 4, 2019

I don't expect it to be a fully fleshed out description, but more like some basic metadata details

Here is the metadata structure for erc-721

{
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents"
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents"
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
        }
    }
}

Here is the metadata for OEP-4

https://github.com/ontio/OEPs/blob/master/OEPS/OEP-5.mediawiki#tokenmetadata

I think it would be good to have some meta data construct for the related for each specific NFT. These properties could be optional or not

Having this type of metadata allows a standardized wallet for a wallet to actually display NFT's to the user in the most basic way.

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented Mar 4, 2019

Yeah, I like the metadata idea to describe what information is available. Another issue to tackle is - some people are storing/returning data in the serialized byte format used by Neo internally, and some are using JSON strings. So the metadata needs to know something about how the data will be encapsulated so the parser can accommodate it.

Personally I don't like the idea of the smart contract having to deal with JSON formatting when there are no built-in interop methods to serialize/deserialize objects to and from JSON, so I'm returning serialized byte arrays.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 5, 2019

EDIT: I have found the relevant function in NEON-js to parse using the internal NEO format and this concern is no longer relevant

I think Serialized Byte Array will be ok, but....there is currently no way to perform the serialization/deserialziation process in something like Neon-js.

I've filed the issue here

CityOfZion/neon-js#393

Without this it not feasible for average client to access smart contract methods easily

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 5, 2019

@erikzhang Can you confirm that preferred method of storage is serialized byte arrays NEO dictionaries and arrays as opposed to JSON representations?

I think it is important distinction for developers who are consuming data from smart contracts

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Mar 5, 2019

Which method will return the metadata?

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 5, 2019

In my ideal case it would be a new method, but perhaps @hal0x2328 has some thoughts

@lightszero

This comment has been minimized.

Copy link
Member

lightszero commented Mar 5, 2019

write metadata description is another job,i think you can write a new standard for that,do not need to change smartcontract.

@hal0x2328

This comment has been minimized.

Copy link
Contributor Author

hal0x2328 commented Mar 5, 2019

Yes, I'd think a new method would be best, something like tokenMetadata. And make it generic enough so that it works for NEP-5, NEP-11 or newer token types that get introduced after the standard is accepted.

@vncoelho
Copy link
Member

vncoelho left a comment

I think that the proposal is going in the precise direction.
Congratulations for the initiative and design.

I left some comments with an example that can be a good insight for thinking about possibilities.


==Motivation==

NFTs are required to track, exchange and enforce ownership of digital assets. A non-fungible token (NFT) can be thought of like a property deed - each one is unique and carries some non-mutable information (e.g. the physical address of the property). Other information, such as the owner of the property, can be changed. Also, we provide a built-in optional divisibility within each non-fungible asset. This allows for high value objects to be tokenized more effectively.

This comment has been minimized.

@vncoelho

vncoelho Mar 5, 2019

Member

This optional divisibility were a nice inclusion. Good insight.

====totalSupply====

<pre>
public static BigInteger totalSupply()

This comment has been minimized.

@vncoelho

vncoelho Mar 5, 2019

Member

For example, if we are talking about all cars of a state in Brazil, then, totalSupply would return that amount that can increase or decrease based on an entity that is registering that assets, right?

====decimals====

<pre>
public static byte decimals()

This comment has been minimized.

@vncoelho

vncoelho Mar 5, 2019

Member

In case that one car is divisible and another one is not, how would we track each corresponding part?

This comment has been minimized.

@erikzhang

erikzhang Mar 6, 2019

Member

A car is divisible, not that it really cuts the car. Dividable cars mean they are shared by many people. So why one car is divisible and another one is not?

This comment has been minimized.

@vncoelho

vncoelho Mar 16, 2019

Member

Let's change example. A divisible bike...aehauheauea

Let the NFT represent the right that the owner has with someone else.
This right can be divisible.
Thus, the same NFT can have both cases depending on the demand and offer of the market.

This comment has been minimized.

@vncoelho

vncoelho Mar 16, 2019

Member

I mean, shares of a NFT.

====tokens====

<pre>
public static enumerator tokens()

This comment has been minimized.

@vncoelho

vncoelho Mar 5, 2019

Member

In the case I am mentioning totalSupply and tokens would walk together? Or totalSupply would be a maximum physical limit of number of registered vehicles?

This comment has been minimized.

@erikzhang

erikzhang Mar 6, 2019

Member

totalSupply is not the maximum limit.

public static enumerator ownerOf(byte[] tokenid)
</pre>

Returns an <code>enumerator</code> that contains all the co-owners that own the specified token.

This comment has been minimized.

@vncoelho

vncoelho Mar 5, 2019

Member

Sounds precise.

@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 6, 2019

If it is reccomended that tokenMetaData be part of a seperate proposal can we at least adjust properties so that it has some basic reccommended structure?

eg.

Properties {
   //Reccommended Properties
   name: String?
   description: String?
   image: String?
   //Contract Specific Properties
   property_one: String?
   property_two: String?
}

I think URI should store something other than a simple imageurl, this can instead be encoded into the properties. The uri should point to a fuller representation of the object which may be contract specific. It could be a json object that is much bigger than the one stored in the contract which could be http, ipfs, or neofs in the future.

My main goal is to reduce some of the ambiguity around Properties and URI. Right now the expected data in both the properties and URI is very ambiguous. If we have some stricter rules around properties, then the URI can be more ambiguous

**Note: I expect the properties object to remain to be stored in the internal NEO serialized format **

@erikzhang

This comment has been minimized.

Copy link
Member

erikzhang commented Mar 6, 2019

I have two different suggestions:

  1. We keep the tokenURI() method and require the URI to return a json file containing the metadata.
  2. Remove the tokenURI method and add the tokenMetaData() method. Fields such as imageUrl are included in the returned data structure.
@saltyskip

This comment has been minimized.

Copy link
Contributor

saltyskip commented Mar 9, 2019

I need some time to create some application to try and consume NFT contract(eg NFT explorer) as it is written to think about best course of action, I think we will have better idea of how it will function with this infrastructure in place, and then we can tweak methods as needed. Will post something once I have it up

@ProDog

This comment has been minimized.

Copy link

ProDog commented Mar 19, 2019

With the development of blockchain games, many game assets need to be published and traded. I think the NFT standard needs to be determined as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.