Back to blog

The Complete Developer’s Guide to Indexing Assets on Supra & Aptos (Coins, FAs, NFTs)

Nov 20, 2025
8 min read

The Complete Developer’s Guide to Indexing Assets on Supra & Aptos (Coins, FAs, NFTs)

As a developer building on a high-performance blockchain like Supra, you’ll soon encounter a critical challenge: indexing and understanding…


The Complete Developer’s Guide to Indexing Assets on Supra & Aptos (Coins, FAs, NFTs)

As a developer building on a high-performance blockchain like Supra, you’ll soon encounter a critical challenge: indexing and understanding digital assets. The chain’s history is split into two distinct eras: the legacy standards (like 0x1::coin and 0x3::token) and the modern, object-based standards (like 0x1::fungible_asset and 0x4::digital_asset).

These standards are not interchangeable. They have fundamentally different data models, storage locations, and require completely different indexing strategies. One is simple; the other is a minefield of potential data corruption that will keep you up at night.

For the past 2.5 years, our team at Supra has been building the SupraScan explorer. This is the guide we wish we had. We’ll cover everything from the core data models to the exact RPC calls and, most importantly, the hard-won “corner cases” you will hit.

Part 1: The Move Data Model

It’s built on the Move smart contract language, which organizes all its on-chain data around three foundational principles. The purpose of this model is Safety, Scarcity, and Flexibility.

Modules (The Logic) A smart contract that contains all the logic (functions and procedures) for a program. Modules have no inherent storage; they define the rules for interacting with Resources and Objects. Modules can be updated and iterated upon. Module functions can only access the internal fields of Resources and Objects they define, preventing outside tampering.

  • Use Case: The Bank Vault’s Blueprint. Defines the rules for depositing, withdrawing, and securing funds, but doesn’t hold the money itself.

Resources (The Scarcity Primitive) A special data structure that holds state/data. Resources have Move Semantics, meaning they cannot be copied, duplicated, or implicitly dropped/destroyed (like a Rust ownership model). This ensures digital assets behave like physical assets and is fundamental to preventing double-spending.

  • Use Case: The Token Balance/Deed. Guarantees that an asset’s data (like a balance of 100 APT) can only exist in one place. It is stored inside an Account’s address or, more commonly now, inside an Object.

Objects (The Container) A user-defined, composable container that has its own unique address on the blockchain. An Object can hold one or more Resources and even other Objects. This is the foundation of the modern asset standards.

  • Use Case: The House/Car. A single, unique entity (the Object) with a unique address that contains multiple resources (e.g., the Deed, the Vehicle ID, the Insurance Policy).

Part 2: Fungible Tokens (The “Money”) — FA vs. Coin

This covers standard, interchangeable tokens like USDC or the native SRA/APT coin.

Fungible Assets (FA): The New 0x1::fungible_asset Standard

  • Philosophy: Object-Based.
  • How it Works: The FA standard uses Objects for metadata and storage. The asset’s metadata (name, symbol) is an Object, and a user’s balance is stored in a FungibleStore resource on an object owned by the user.
  • Use Case: Creating a stablecoin ($USDC). The FA model ensures all metadata is stored on-chain in an Object, simplifying integration for wallets and exchanges. It’s flexible and allows for advanced features like custom transfer logic.

Coin Legacy: The Old 0x1::coin Standard

  • Philosophy: Resource-Based.
  • How it Works: To hold a “Coin,” a user must have a CoinStore<CoinType> resource inside their account. This resource contains a simple value field, which is their balance.
  • The Indexer’s Challenge: You cannot ask an account, “Show me all coins you own.” You must know the CoinType in advance to query the specific resource.
  • Explorer Solution: An explorer must first build a master list of all CoinInfo resources on the chain, then query a user’s account for every single coin in your master list to build their portfolio.
  • Events: Track 0x1::coin::DepositEvent and 0x1::coin::WithdrawEvent.

Part 3: Non-Fungible Tokens (The “Collectibles”)

This is the most complex part of indexing, as the two standards are radically different.

Part 4: The 0x3 Legacy Token Standard (The “Hard Mode”)

PHILOSOPHY: THE “BANK VAULT” MODEL The asset’s metadata (TokenData) is stored in the creator’s account. The asset’s instance (Token) is stored in the owner’s account. The data is completely separate and spread across the chain.

KEY STRUCTS (THE METADATA)

  • CollectionData: Contains metadata for an entire collection (name, description, uri, maximum).
  • TokenData: Contains metadata for a specific token (name, uri, royalty, properties).
  • Royalty: Defined per-token in this standard.
  • Properties (PropertyMap): On-chain key-value traits
  • Mutability: Booleans for which fields can be edited (e.g., uri: false). An explorer MUST show this.

KEY IDENTIFIERS (THE “HOW”)

  • TokenDataId: The unique key for the token’s metadata. It’s a 3-part struct: creator (address), collection (String), and name (String). This is your primary key.
  • TokenId: The unique key for the instance a user owns. It’s a struct containing the TokenDataId and a property_version (usually “0”).

ON-CHAIN STORAGE (THE “WHERE”)

  • Collections Resource (On Creator’s Account): This resource (0x3::token::Collections) is the key for backtracking. It contains two Table HANDLES (pointers) to the CollectionData and TokenData tables.
  • TokenStore Resource (On Owner’s Account): This resource (0x3::token::TokenStore) exists in every owner’s account. It contains a tokens handle, which points to a table of Token structs that this specific wallet owns.

THE PROBLEM: FINDING THE 0x3 OWNER You cannot ask the blockchain, “Who owns this token?” There is no central owner field. To find the owner, you would have to scan every TokenStore on every account, which is impossible.

THE SOLUTION: A TWO-SYSTEM INDEXER

You MUST build two systems to handle this.

System 1: The “Fast” Event Tracker To listen to all events, from genesis, and build its own database of ownership.

  • Key Events: CreateCollectionEvent, CreateTokenDataEvent, DepositEvent (Ownership Gained), WithdrawEvent (Ownership Lost).
  • The Flaw: If your indexer’s RPC call fails, misses a single block, and that block lands in the dead-letter queue, your database will be permanently stale.

System 2: The “Slow” Reconciliation Validator This is the only way to solve the stale data problem. It’s a background worker that constantly health-checks your database.

  1. Job: It runs in a loop, querying your own DB for 0x3 tokens.
  2. Verify Owner: It takes the ownerId you have on file and runs the 2-step RPC check to ask the blockchain: “Does Owner A still own this token?” (Get TokenStore handle -> Query Table Item).
  3. The Result: If the RPC returns 404 Not Found, you have detected an error. Your data is stale. You cannot find the new owner, but you can fix your database by setting the ownerId to UNKNOWN.

Part 5: The 0x4 Digital Asset (DA) Standard (The “Easy Mode”)

PHILOSOPHY: THE “ASSET IS THE ACCOUNT” MODEL This standard is a dream for indexers. Every NFT and every Collection is a standalone Object with its own unique address.

KEY STRUCTS (RESOURCES ON THE OBJECT)

  • 0x1::object::ObjectCore: The most important resource.
  • owner (address): THIS IS IT. The single, authoritative field for the current owner.
  • 0x4::collection::Collection: Holds name and URI.
  • 0x4::royalty::Royalty: Defines the royalty per-collection.
  • 0x4::token::Token: Holds the link to the collection and the URI.
  • Warning: The ‘name’ field here is often empty (“”).
  • 0x4::token::TokenIdentifiers: Holds the real display name (e.g., “Supra Spike #1”).

THE SOLUTION: FINDING THE 0x4 OWNER It’s one simple RPC call: GET /accounts/{NFT_OBJECT_ADDRESS}/resource/0x1::object::ObjectCore ...then just read the owner field.

KEY 0x4 EVENTS

  • 0x4::collection::Mint (Token Created)
  • 0x1::object::TransferEvent (Ownership Changed - The ONLY event needed).

Part 6: The Universal Challenge — The Metadata URI

Both 0x3 and 0x4 use a uri field to point to metadata. An indexer MUST fetch this uri to get the real image, traits, and other details.

THE PROBLEM: The uri is just a string. It can be a JSON link, a direct image link, an on-chain Data URI, or even a website. Fetching a 2GB video file will crash your indexer.

THE ROBUST FETCHING LOGIC (FOR YOUR BACKEND)

Your indexer cannot just GET every uri. This is the only safe way to process them:

  1. Check Prefix:
  • If data: -> It's an on-chain SVG. No network call needed.
  • If http/ipfs -> Proceed to Step 2.
  1. Resolve URI: Convert ipfs:// to a private gateway link (e.g., Pinata). Public gateways will block you.
  2. Make HEAD Request: This is the key optimization. Make a fast HEAD request to get the Content-Type header only.
  3. Check Content-Type:
  • If application/json -> Make a full GET request, download, and parse the metadata.
  • If image/png, video/mp4 -> STOP. Do not download. You know the URI is the media itself.
  • If HEAD fails -> The link is broken.

Conclusion

Indexing assets on Supra/Aptos is a tale of two eras. The modern 0x4 and FA standards are robust and easy to query. The legacy 0x1 and 0x3 standards are complex and require a dedicated two-system indexer to ensure data accuracy. By handling these standards correctly, and by building a robust URI processor, you can create a fast, reliable explorer for the entire ecosystem.

By Jatin Jain Saraf on November 20, 2025.