Ethereum 2.0 vs Symbol (Part 6): Non-Fungible Tokens

Ivy Fung
Coinmonks
9 min readMar 1, 2021

--

Source: https://www.cryptokitties.co/

You are one of many; you are one of a kind. You bear the common features of humankind, yet you are the one and only. You are non-fungible.

Blockchain allows parties to exchange assets without an intermediary. Sometimes, we exchange something fungible, like monies of different denominations. Sometimes, we exchange something fungible for something unique, like paying money to buy a house or exchange something unique for another unique thing, like a Pokemon trading card for another. Here, is where non-fungible token (NFT) plays its roles.

NonFungible.com, the trading volume of NFTs has increased 299% year-on-year to over USD250 million in 2020.
Source: NonFungible.com. Trade volumes of NFTs.

Though trading of NFTs is not as common as trading of cryptocurrencies, it is definitely catching up on fire. According to NonFungible.com, the trading volume of NFTs has increased 299% year-on-year to over USD250 million in 2020. There is a significant increase in the number of active wallets for NFTs as well. Seems the market is ready for the increasing number of players.

ERC-721

ERC-721 is the NFT standard for Ethereum. Most of the NFTs on Ethereum are ERC-721 compliant. CryptoPunks which has been popular recently is not an ERC-721 token as its creation predates ERC-721. However, owners of CryptoPunks can wrap the tokens in ERC-721 standard for easy swapping.

CryptoPunks

CryptoPunks was released in June 2017 as one of the first non-fungible token (NFT) on the Ethereum blockchain. … The crypto art blockchain project was an inspiration for the ERC-721 standard for NFTs and the modern crypto art movement. — Wikipedia.com

Like creating an ERC-20 token, you are not actually creating an ERC-721 token but creating a ledger/map/contract that records the ownership and changes of ownership of the tokens.

Creating an ERC-721 NFT is much more complicated than an ERC-20 as each ERC-721 is unique. Take CryptoKittie as an example, it involves 7 smart contracts. Depends on the purposes of the NFTs, the smart contracts can be very different. CryptoKittie smart contracts involve mixing of the kitties’ genes, while another popular NFT NBA Top Shot involves short clips, called the Moments. We will only focus on discussing the ERC-721 standard in this article.

Here are the functions of an ERC-721 standard: (Details about some exceptions or causes of transaction failure etc will not be discussed here to keep the writing simple, unless necessary. Refer to documentation.)

  1. balanceOf(address of owner): This function calls for the number of unique tokens that fall under the same ERC-721. E.g. It returns the number of Kitties the address owns under the Cryptpkittie ERC-721.
  2. ownerOf(token ID): Each token in an ERC-721 has its own unique ID to distinguish itself from the rest of its peers. This function returns the owner’s address of a particular token.
  3. safeTransferFrom(address of owner, address of receiver, token ID, data): This function transfers a specific token from the owner to the new owner. It can be called by the owner/operator/approved address (msg.sender). This function checks if the token belongs to the owner’s address stated and checks if the receiver is a contract address. If it is a contract address, it calls another interface (to be explained later) to make sure that contract handles the ERC-721. If not, the transfer will not take place. This function also allows extra data in no particular format to be attached.
  4. safeTransferFrom(address of owner, address of receiver, token ID): Same as the other safeTransferFrom function, but without extra data attached.
  5. transferFrom(address of owner, address of receiver, token ID): To transfer a token from the owner to the next owner. The sender (msg.sender) is responsible to ensure the receiver can receive the token. Else, the token could be lost forever. Less safe than using safeTransferFrom.
  6. approve(address approved by owner, token ID): The owner of the token calls this function to approve a third party to manage a specific token on behalf.
  7. setApprovalForAll(address of operator, boolean): The owner of the token calls this function to approve or to revoke any appointment to an operator to manage all the owner’s tokens.
  8. getApproved(token ID): This function returns the address approved to manage a specific token.
  9. isApprovedForAll(address of owner, address of operator): This function checks if an operator is approved by the owner.

There are 3 events:

  1. Transfer(address of owner, address of receiver, token ID): This event emits when the transfer of ownership happens, and when tokens are created and destroyed. Related to functions transferFrom() and both safeTransferFrom().
  2. Approval(address of owner, address approved by owner, token ID): This event emits when an address is approved or reaffirmed to manage a token. When the token ownership is transferred to a new owner, the approval is automatically revoked. Related to functions approve().
  3. ApprovalForAll(address of owner, address of operator, boolean): This event emits when an address is appointed or revokes as an operator to manage all the owner’s tokens. Related to functions setApprovalForAll().

You might have noticed that the name(), symbol(), and decimal() for the ERC-721 were not mentioned. First, as each token of an ERC-721 standard is unique, it doesn’t make sense to divide it, hence, the decimal() is irrelevant here. To make transactions of tokens more secure, ERC-165 is introduced to detect interfaces adopted by each contract. There are many interfaces under the ERC-165, one of them is “ERC721Metadata” in which the name(), symbol(), and tokenURI() are stated. “ERC721Enumerable” states the total supply, the token index, and the owner of each token. Each ERC-165 interface is represented by an identifier. For example, ERC721Metadata’s identifier is 0x5b5e139f while ERC721Enumerable’s is 0x780e9d63.The interface for a contract account to receive an ERC-721 token is “ERC721TokenReceiver” with 0x150b7a02 as identifier.

For the method to check if a contract implements any ERC-165 interface, please refer to here. ERC-165 applies to ERC-20 too.

Continue with the concert ticket use case from part 5, let’s look at how an ERC-720 works.

An ERC-721 contract is created, with the symbol “CTC”, and a total supply of 1,000 unique tickets. You, the organizer, will sell the ticket according to the ticket tiers and seat number, with different prices. Together with each ticket, the buyer can choose add-ons, like souvenirs and drinks (all souvenirs and drinks are the same). The add-ons selection will be handled by the ticketing apps and will be passed to the smart contract that creates a new ticket token. Says Alice bought a premium ticket with a souvenir as an add-on, the ticketing app will trigger the smart contract to create the unique CTC token, and the payment and ticket change hands. You call safeTransferFrom() to safely transfer the ticket to Alice. (Also involves ERC721Enumerable, ERC721Metadata)

You also decide to appoint a dealer. So you approve the dealer’s address to handle tokens created under the CTC ERC-721 by calling setApprovalForAll(). Some security and authority issues for other functions might need to be carefully thought out depending on the architecture of the whole ticketing app. The dealer can then sell the tickets and call safeTransferFrom() to transfer the tickets to buyers.

The other functions are mostly “GET” methods to query for tokens, owners, or operators' information. Refer to part 5 for similar function explanations.

If in any case, says Alice, wants to top-up with drinks, you may edit the token’s URI by calling another function. The dealer may have the authority to do so, depends on the access given. All these can be done in different ways either on-chain or off-chain depending on how decentralized you want your ticketing app to be.

Account and Mosaic

There isn’t any specific standard set for the non-fungible token in Symbol at this moment. There are many ways to do so, and I will approach this in two methods.

The first method, using an Account to represent something unique. An Account is made of private and public keypair and it is unique. Using the same ticketing example we have been using, tickets with different tiers of price are unique, hence, non-fungible. There are still some fungible elements here, the souvenir and the drinks, which we will represent with Mosaics.

You as the organizer of the concert will create 1,000 accounts representing 1,000 concert tickets. All these 1,000 accounts will need to be converted to Multisig Account (an Account with custodians) in order to transfer their ownership. You also create 1,000 tokens of souvenir and 1,000 tokens of drinks with Mosaics. (You may also use Metadata to replace Mosaic in this use case.)

When Alice wants to buy a ticket you will transfer the ownership by adding Alice as the cosigner of the Multisig Account and remove yourself as the owner. As Alice also wants to buy a drink as an add-on, you transfer a “drink” token to the Multisig Account Alice now owns. All these can be done in one Aggregate Transaction. Now, Alice owns the “ticket” that comes with a “drink”. If Alice wants to add-on a souvenir, you can transfer a “souvenir” token to Alice’s “ticket” Multisig Account.

Transfer of ticket ownership to Alice.

If you decide to appoint a dealer, add the dealer’s account as one of the custodians of the Multisig Accounts representing the “tickets”, and the one holding the “souvenir” and “drink” tokens. The rest of the procedure will be no different from the organizer. Authority and access need to be carefully thought over to avoid misuse of power.

The second method, representing the ticket with Mosaic, but makes every mosaic distinctive from one another using Metadata. First, create 1,000 Mosaics each representing one unique ticket. NOTE: Different from part 5 where we created 1 “CTC” Mosaic with 1,000 copies but one identifier, here we are creating 1,000 Mosaic each having its own identifier.

Comparing tickets as fungible and non-fungible tokens.
Comparing method 1 and method 2.

As a Mosaic cannot own another Mosaic, hence, we will be using Metadata to tag the Mosaic with add-ons. Each Mosaic can have multiple Metadata attached to it. Alice wants to buy a ticket with a souvenir as an add-on, you transfer the specific Mosaic representing the seat number she prefers and adds Metadata{ your public key, add-on, souvenir }to it. Metadata is in tuple form, which includes the Account adding the Metadata, and the key-value pair. When Alice wants to add drinks as an add-on, you will send another Metadata transaction to Alice’s ticket Mosaic.

Transfer of ticket ownership to Alice.

The handling of the dealer will be the same as method one.

NOTE: For both methods, you may create the Account and the Mosaic representing the tickets as and when needed. Control of the ticket’s total supply was not discussed here.

Update: For this example, Metadata is suitable. For some NFT where a token needs to be tied to a specific commodity, like digital art, Metadata will not be my choice. Please watch NFT on Symbol: Metadata or Not? for more details.

Every non-fungible token is indeed unique. It can have many ways to design it. I have come up with a proof-of-concept for a game on Symbol blockchain called “Train Your Dog” using the first method. I intended to look into different methods in designing it before starting to build it.

There are a few issue that are important and shall be considered before deciding:

  1. The ease of transfer. To attract users, the user interfaces need to be easy to use and intuitive. Though it sounds unrelated to the blockchain, however, in some way, they are intertwined.
  2. Every transaction on the blockchain costs transaction fees. Hence, the steps and flows of the application need to be carefully thought over in order to save on transaction fees.
  3. There could be some functions that are best to be handled off-chain and will not temper the trustworthiness of the application. Carefully consider this as it will either save you transaction fees or cost you reputation.
  4. And the most important one: what format is preferred by the exchanges. Only tradable NFT will have actual value.

There could be more concerns out there that are more important than what I have mentioned due to the nature of the assets to be tokenized. But, let’s not go down that rabbit hole.

Ready to start?

Special thanks to Anthony for reviewing this article.

--

--

Ivy Fung
Coinmonks

On a mission to talk to everyone about Blockchain.