5.5 Non-Fungible Token Standard - ARC721
NFTs are non-fungible tokens. They are tokens that are distinguishable from one another by including different ids or data.
Compared to NFTs on public ledgers like Ethereum, Aleo NFTs can independently have:
Private or public token owner visibility.
Private or public data associated the token.
Both of those features have implications on feasibility of building applications involving NFTs as a marketplaces, escrow programs…
Example:
Domain Names - Human-readable names that resolve addresses in the Aleo Name Service contract.
Royalty NFTs - Tradable assets which utility is to claim creator or marketplace royalty fees.
IOU NFTs - Tradable assets allowing to claim due fungible tokens in a lending agreement, including private loan data.
The complete code for the standard is available here.
Motivations
On-chain vs Off-chain data
The NFT standard on Aleo allows for NFT data to be on-chain, off-chain or a combination of both. Off-chain to reduce the storage fees on the network. On-chain to leverage the possibility of using this data as input/outputs of zk-circuits.
Remark: On-chain data can either consists of a hash of some data or be the data itself directly depending on the use case’s requirement of guarantying access to the data transactionally.
Specification
A NFT collection is defined as a program implementing the following specifications.
Data Structure
The data stored within a NFT has the following structure:
An example of such an off-chain metadata JSON can be found here.
Name of the structs don’t necessarily have to match ‘data’ and ’attribute’, allowing to import several NFT collection program without shadowing. Although, the name of each struct attribute is enforced by the standard. Aleo reserved keywords should be prefixed with an underscore character (as for ‘_value’).
To get the complete data for a NFT, off-chain and on-chain data can simply be merged, for instance in javascript:
Private Data and Ownership
As for ARC20-21 tokens, privacy of NFT owner is achieved by representing the token as an Aleo record. It also allows the data of an NFT to remain private, by including it as a private attribute of the record.
Although, to enforce uniqueness, a public identifier for the NFT is necessary. Simply using a hash of the data would introduce 2 problems:
Two NFTs could not share the same data.
It would disclose some knowledge about the data: verifying that some data matches would be an instantaneous operation.
For these reasons, we include in the NFT record a scalar element, called edition:
We define the public identifier for the NFT as the commit of its edition to the hash of its data:
This “NFT commit” does not disclose any information on the NFT data as long as the edition obfuscator is chosen uniformly randomly in the scalar field.
The following mapping serves to enforce the uniqueness of “NFT commit” identifiers:
Public Ownership
Apart from guarantying non-fungibility, NFT commit also allows to make the owner of the NFT public, while keeping its data private.
This is a key feature on Aleo because it allows program to own NFTs without revealing their data, as programs cannot spend records.
The same as for ARC20-21 tokens, an NFT owner can be made public by mapping NFT commit to said owner:
Although it raises a challenging question, if user A is the private owner of a NFT, and transfers ownership to a public owner user B, how can user B know the actual data behind the public NFT commit.
The proposed solution is to include another record, NFTView, defined as:
This record doesn’t represent private ownership of the NFT, but is a vehicle for the NFT data and is minted to the public receiver of transfers along with NFT ownership.
The conversion from private to public owner can then be implemented as follow:
Remark: is_view is always true, and is here just for differentiating NFTView from NFT in plaintext representations of records.
An ultimate problem remains: what if the public receiver of a transfer is a program? Then NFTView doesn’t help. To illustrate this problem, let’s imagine we are trying to create a marketplace program. A seller lists the NFT using transfer_private_to_public. A buyer then accepts the listing and should receive automatically receive the data corresponding to the NFT. One way to do this, could be to have the seller come back, to disclose the private data to withdraw payment.
This back and forth between the buyer and the seller makes the user experience a lot worse than on traditional NFT marketplaces. Hence the need for a mechanism allowing Aleo programs to store private data and disclose it programmatically. An attempt at contributing solving this problem is Aleo DCP, where data is splitted according to a MPC protocol. Here is an example NFT marketplace program with the same “one click buy” user experience as with traditional marketplaces, by leveraging Aleo DCP.
Remark: These last considerations are only relevant to NFTs data that should always remain private.
Public Data
For collections where data can become public, “publishable collections”, the following mapping and function should be included to tackle the problem stated in last section:
publish_nft_content can then be called along with transfers to a program, for instace in marketplace program on listing.
Re-obfuscation
If a NFT content has been published once, the only way to re-obfuscate it is to transfer it to private again, then use the following function to update the edition, hence the commit of the NFT.
Previous commit is not removed from nft_commits mapping, as it would reveal the previous commit and the new one represent the same data.
Approvals
As for ARC20 tokens, the standard features an approval mechanism allowing accounts to approve another account to spend their token. It can be a specific asset, or any asset from the collection.
Once approved, the transfer_from_public function can be called by the spender, to transfer a NFT from the approver to a recipient address.
Settings
A mapping is responsible for the collection level settings:
Available settings:
0u8- Amount of mintable NFTs (all editions).1u8- Number of total NFTs (first-editions) that can be minted.2u8- Symbol for the NFT.3u8- Base URI for NFT, part 1.4u8- Base URI for NFT, part 2.5u8- Base URI for NFT, part 3.6u8- Base URI for NFT, part 4.7u8- Admin address hash.
Last updated