APTOS Token v2: What changed for indexers and marketplaces

Oleg AgafonovOleg Agafonov
Aug 24, 2023|4 min read

Not too long ago, APTOS introduced Token v2, marking a significant milestone in the world of NFTs. While only a handful of test NFT collections have been launched under this standard, it's worth highlighting that Mercato is at the forefront, pioneering the integration of complete support for Token v2.

But what exactly does Token v2 entail? You can gain a comprehensive understanding of its implications for users and collection creators through these resources: here and here.

As we delve into this discourse, our focus will shift away from reiterating the content found in those articles. Instead, we'll concentrate on delineating the distinct attributes of Token v2 concerning indexers and marketplaces.

Accessing Token's API from Your Smart Contracts

Let's explore the technical intricacies of interacting with the Token v2 standard within your smart contracts. To illustrate this, we'll compare the method signatures for listing tokens between Token v1 and Token v2.

For Token v1, a typical method signature for listing tokens may resemble the following:

public entry fun list_token(
        account: &signer,
        collection_creator: address,
        collection_name: String,
        token_name: String,
        property_version: u64,
	amount: u64,
        price: u64
    ) 

This signature reveals its complexity due to Token v1's resource nature. It calls for the inclusion of the collection creator's address, collection name, token name, and property version. The intricacy deepens with the inclusion of the amount parameter, suggesting the possibility of multiple tokens sharing the same TokenId. This compounds the intricacy of the smart contract's business logic.

Speaking of the TokenId, gaining access to the token API requires the consolidation of all these parameters within a TokenId structure, demonstrated here:

let token_id = token::create_token_id_raw(
            collection_creator,
            collection_name,
            token_name,
            property_version
        );

This process may indeed seem somewhat boilerplate-intensive. Now, let's contrast this with a similar method signature for Token v2:

public entry fun list_token(
        account: &signer,
        token: Object<Token>,
        price: u64
    )

And there you have it! 🤯🤯🤯 Token v2 simplifies this process by leveraging APTOS objects. Each token possesses a unique address, which you can provide as an argument. The APTOS virtual machine seamlessly employs the implicit object::address_to_object<Token>(addr) transformation, seamlessly converting the address into the desired object.

Understanding Token's State

While accessing Token’s API is way easier in the new standard, understanding token’s state might be tricky. When you list and then buy Token v1 in any marketplace, its ownership will change only once:

1. Alice calls `list_token` -> Token is withdrawn from Alice
2. Bob calls `buy_token` -> Token is deposited to Bob

In contrast, Token v2 doesn't have a withdrawn/deposit state machine. All changes happen via ownership transfer to new users or 🥁🥁🥁 supportive objects, which brings additional complexity to indexers:

1. Alive calls `list_token` -> New Listing object is created -> Token's ownership is transferred to the new object
2. Bob calls `buy_token` -> The listing object is destroyed -> Token's ownership is transferred to Bob

Accessing Listings Data

In the context of the Token v1 standard, listings take the form of structured entities that are defined and stored as resources within the smart contract. This approach lacks a standartized method for creating, defining, and interacting with listings data. Much like the complexity involved in accessing a token's API, gaining access to listings data in this framework entails intricate steps and involves a substantial amount of boilerplate code. A concrete example of this lies within the Token v1 contract employed by Mercato, where all listings are stored in a Table. This API, though pretty limited in nature, serves as a foundational component:

let listing_store = borrow_global_mut<ListingStore>(@mercato);

if (!table::contains<TokenId, Listing>(&mut listing_store.listings, token_id)) {
            let token = token::withdraw_token(account, token_id, 1);

            table::add<TokenId, Listing>(&mut listing_store.listings, token_id, Listing {
                token,
                price,
                seller
            });
            //...
        } else {
            let listing = table::borrow_mut<TokenId, Listing>(&mut listing_store.listings, token_id);
            // ...
        }

Conversely, within the Token v2 standard, Listings are presented as objects. This significant differentiation eradicates the need for storing listings within the smart contract itself. Instead, these listings find their home within the blockchain's object storage space, where they are efficiently stored and managed.

Supporting Bulk Operations

The hallmark of exceptional marketplaces often lies in their capability to execute bulk operations.

For a marketplace that also serves as an aggregator, like Mercato, accommodating bulk operations presents a formidable challenge. Complexity stems from the fact that APTOS restricts transactions to a single function call. Under these constraints, facilitating bulk operations necessitates the creation of an extensive and intricate aggregator module, where the method signature entails an array of vectors encompassing diverse permutations of arguments. With the advent of Token V2, marketplaces engaging in bulk operations must expand their method signatures significantly. Mercato's endeavor to support bulk list operations serves as an example:

public entry fun list_tokens(
        account: &signer,
        versions: vector<u64>,
        markets: vector<String>,
        collection_creators: vector<address>,
        collection_names: vector<String>,
        token_names: vector<String>,
        property_versions: vector<u64>,
        amounts: vector<u64>,
        prices: vector<u64>,
        listing_nonces: vector<u128>,
        listing_addresses: vector<address>,
        token_addresses: vector<address>
    )

Conclusions

Despite a few formidable challenges, such as Token state definition and bulk operation support, the Token v2 standard offers a plethora of advantages to smart contracts powering marketplaces. Considering its tangible benefits for users and creators alike, it stands as a remarkable accomplishment for the APTOS development team. For those who have yet to join, it serves as an incentive to become part of the vibrant APTOS community.