Create Peluş Pepe #891 #547
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
A standard interface for non-fungible tokens.
Motivation
A standard interface will greatly simplify interaction and display of different entities representing right of ownership.
NFT standard describes:
Guide
Non-Fungible Token (NFT) represents an ownership over unique digital asset (kitten images, title deeds, artworks, etc). Each separate token is an NFT Item. It is also convenient to gather NFT Items into an NFT Collection. In TON, each NFT Item and NFT Collection are separate smart contracts.
NFT Metadata
Main article: TEP-64
Each NFT Item and NFT Collection itself has its own metadata (TEP-64). It contains some info about NFT, such as title and associated image. Metadata can be stored offchain (smart contract will contain only a link to json) or onchain (all data will be stored in smart contract).
Collection metadata example (offchain):
{ "image": "https://ton.org/_next/static/media/smart-challenge1.7210ca54.png", "name": "TON Smart Challenge #2", "description": "TON Smart Challenge #2 Winners Trophy", "social_links": [] }Item metadata example (offchain):
{ "name": "TON Smart Challenge #2 Winners Trophy", "description": "TON Smart Challenge #2 Winners Trophy 1 place out of 181", "image": "https://ton.org/_next/static/media/duck.d936efd9.png", "content_url": "https://ton.org/_next/static/media/dimond_1_VP9.29bcaf8e.webm", "attributes": [] }Offchain metadata is published for example on web.
Useful links
Specification
The NFT collection and each NFT item are separate smart contracts.
Example: if you release a collection that contains 10 000 items, then you will deploy 10 001 smart contracts.
NFT item smart contract
Must implement:
Internal message handlers
1.
transferRequest
TL-B schema of inbound message:
transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:(Maybe ^Cell) forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody;query_id- arbitrary request number.new_owner- address of the new owner of the NFT item.response_destination- address where to send a response with confirmation of a successful transfer and the rest of the incoming message coins.custom_payload- optional custom data.forward_amount- the amount of nanotons to be sent to the new owner.forward_payload- optional custom data that should be sent to the new owner.Should be rejected if:
forward_amount.in_msg_value - forward_amount - max_tx_gas_priceto theresponse_destinationaddress.If the contract cannot guarantee this, it must immediately stop executing the request and throw error.
max_tx_gas_priceis the price in Toncoins of maximum transaction gas limit of NFT habitat workchain. For the basechain it can be obtained fromConfigParam 21fromgas_limitfield.Otherwise should do:
new_owneraddress.forward_amount > 0send message tonew_owneraddress withforward_amountnanotons attached and with the following layout:TL-B schema:
ownership_assigned#05138d91 query_id:uint64 prev_owner:MsgAddress forward_payload:(Either Cell ^Cell) = InternalMsgBody;query_idshould be equal with request'squery_id.forward_payloadshould be equal with request'sforward_payload.prev_owneris address of the previous owner of this NFT item.If
forward_amountis equal to zero, notification message should not be sent.response_destinationwith the following layout:TL-B schema:
excesses#d53276db query_id:uint64 = InternalMsgBody;query_idshould be equal with request'squery_id.forward_payloadformatIf you want to send a simple comment in the
forward_payloadthen theforward_payloadmust starts with0x00000000(32-bits unsigned integer equals to zero) and the comment is contained in the remainder of theforward_payload.If comment does not begin with the byte
0xff, the comment is a text one; it can be displayed "as is" to the end user of a wallet (after filtering invalid and control characters and checking that it is a valid UTF-8 string).For instance, users may indicate the purpose ("for coffee") of a simple transfer from their wallet to the wallet of another user in this text field.
On the other hand, if the comment begins with the byte
0xff, the remainder is a "binary comment", which should not be displayed to the end user as text (only as hex dump if necessary).The intended use of "binary comments" is, e.g., to contain a purchase identifier for payments in a store, to be automatically generated and processed by the store's software.
If the
forward_payloadcontains a binary message for interacting with the destination smart contract (for example, with DEX), then there are no prefixes.These rules are the same with the payload format when simply sending Toncoins from a regular wallet (Smart Contract Guidelines: Internal Messages, 3).
2
get_static_dataRequest
TL-B schema of inbound message:
get_static_data#2fcb26a2 query_id:uint64 = InternalMsgBody;query_id- arbitrary request number.should do:
64(return msg amount except gas fees):TL-B schema:
report_static_data#8b771735 query_id:uint64 index:uint256 collection:MsgAddress = InternalMsgBody;query_idshould be equal with request'squery_id.index- numerical index of this NFT in the collection, usually serial number of deployment.collection- address of the smart contract of the collection to which this NFT belongs.Get-methods
get_nft_data()returns(int init?, int index, slice collection_address, slice owner_address, cell individual_content)init?- if not zero, then this NFT is fully initialized and ready for interaction.index- numerical index of this NFT in the collection. For collection-less NFT - arbitrary but constant value.collection_address- (MsgAddress) address of the smart contract of the collection to which this NFT belongs. For collection-less NFT this parameter should be addr_none;owner_address- (MsgAddress) address of the current owner of this NFT.individual_content- if NFT has collection - individual NFT content in any format;if NFT has no collection - NFT content in format that complies with standard TEP-64.
NFT Collection smart contract
It is assumed that the smart contract of the collection deploys smart contracts of NFT items of this collection.
Must implement:
Get-methods
get_collection_data()returns(int next_item_index, cell collection_content, slice owner_address)next_item_index- the count of currently deployed NFT items in collection. Generally, collection should issue NFT with sequential indexes (see Rationale(2) ).-1value ofnext_item_indexis used to indicate non-sequential collections, such collections should provide their own way for index generation / item enumeration.collection_content- collection content in a format that complies with standard TEP-64.owner_address- collection owner address, zero address if no owner.get_nft_address_by_index(int index)returnsslice addressGets the serial number of the NFT item of this collection and returns the address (MsgAddress) of this NFT item smart contract.
get_nft_content(int index, cell individual_content)returnscell full_contentGets the serial number of the NFT item of this collection and the individual content of this NFT item and returns the full content of the NFT item in format that complies with standard TEP-64.
As an example, if an NFT item stores a metadata URI in its content, then a collection smart contract can store a domain (e.g. "https://site.org/"), and an NFT item smart contract in its content will store only the individual part of the link (e.g "kind-cobra").
In this example the
get_nft_contentmethod concatenates them and return "https://site.org/kind-cobra".Drawbacks
There is no way to get current owner of NFT onchain because TON is an asynchronous blockchain. When the message with info about NFT owner will be delivered, this info may become irrelevant, so we can't guarantee that current owner hasn't changed.
Rationale and alternatives
Why not a single smart contract with a token_id -> owner_address dictionary?
In TON, gas consumption for dictionary operations depends on exact set of keys.
Also, TON is an asynchronous blockchain. This means that if you send a message to a smart contract, then you do not know how many messages from other users will reach the smart contract before your message.
Thus, you do not know what the size of the dictionary will be at the moment when your message reaches the smart contract.
This is OK with a simple wallet -> NFT smart contract interaction, but not acceptable with smart contract chains, e.g. wallet -> NFT smart contract -> auction -> NFT smart contract.
If we cannot predict gas consumption, then a situation may occur like that the owner has changed on the NFT smart contract, but there were no enough Toncoins for the auction operation.
Using smart contracts without dictionaries gives deterministic gas consumption.
Scaling in TON is based on the concept of sharding, i.e. automatic partitioning of the network into shardchains under load.
The single big smart contract of the popular NFT contradicts this concept. In this case, many transactions will refer to one single smart contract.
The TON architecture provides for sharded smart contracts(see whitepaper), but at the moment they are not implemented.
Why are there no "Approvals"?
TON is an asynchronous blockchain, so some synchronous blockchain approaches are not suitable.
You cannot send the message "is there an approval?" because the response may become irrelevant while the response message is getting to you.
If a synchronous blockchain can check
alowanceand if everything is OK dotransferFromin one transaction, then in an asynchronous blockchain you will always need to send atransferFrommessage at random, and in case of an error, catch the response message and perform rollback actions.This is a complex and inappropriate approach.
Fortunately, all cases that arose during the discussion can be implemented by a regular transfer with notification of the new owner. In some cases, this will require an additional smart contract.
The case when you want to place NFT on several marketplaces at the same time is solved by creating auction smart contracts that first accept payment, and then NFT is sent to one of auction smart contracts.
Why are there no obligatory royalties to the author from all sales?
In the process of developing this idea, we came to the conclusion that it is possible to guarantee royalties to the author from absolutely any sale on the blockchain only in 1 case:
All transfers must be carried out through an open long-term auction, and other types of transfers are prohibited.
If you want to transfer NFT to yourself to another wallet, then you need to start an auction and win it.
Another variation of this scheme is to make all transfers chargeable.
By prohibiting the free transfer of tokens, we make tokens inconvenient in many cases - the user simply updated the wallet, the user wants to donate NFT, the user wants to send NFT to some smart contract.
Given the poor usability and that NFTs are a general concept and not all of them are created for sale - this approach was rejected.
Prior art
Unresolved questions
Future possibilities
None
Standard extensions
The functionality of the basic NFT standard can be extended:
TL-B schema
Tags were calculated via tlbc as follows (request_flag is equal to
0x7fffffffand response flag is equal to0x80000000):crc32('transfer query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:Maybe ^Cell forward_amount:VarUInteger 16 forward_payload:Either Cell ^Cell = InternalMsgBody') = 0x5fcc3d14 & 0x7fffffff = 0x5fcc3d14crc32('ownership_assigned query_id:uint64 prev_owner:MsgAddress forward_payload:Either Cell ^Cell = InternalMsgBody') = 0x85138d91 & 0x7fffffff = 0x05138d91crc32('excesses query_id:uint64 = InternalMsgBody') = 0x553276db | 0x80000000 = 0xd53276dbcrc32('get_static_data query_id:uint64 = InternalMsgBody') = 0x2fcb26a2 & 0x7fffffff = 0x2fcb26a2crc32('report_static_data query_id:uint64 index:uint256 collection:MsgAddress = InternalMsgBody') = 0xb771735 | 0x80000000 = 0x8b771735Acknowledgements
We are grateful to the Tonwhales developers for collaborating on the current draft of the standard 🤝
Changelog
01 Feb 2022
02 Feb 2022
04 Feb 2022
08 Feb 2022
11 Feb 2022
30 Jul 2022
31 Aug 2022 - Added
forward_payloadformat.