How to query USDT0 stablecoin data on Stable Chain using Ormi’s Subgraphs

Learn how to index USDT0 stablecoin data on Stable Chain using Ormi’s 0xGraph. This step-by-step guide shows you how to deploy a production-ready subgraph, track USDT0 transfers, mints, burns, etc, and query real-time and historical stablecoin data with GraphQL.

How to query USDT0 stablecoin data on Stable Chain using Ormi’s Subgraphs

Indexing and querying smart contract data on new chains can be challenging. Stable Chain introduced USDT0 stablecoin to support institutional stablecoin flows, but using this data in production requires fast, reliable indexing.

This guide shows how to deploy and query a USDT0 subgraph on Stable Chain using Ormi’s 0xGraph, a real-time and fully Graph-compatible indexing engine built for high-throughput chains. If you have used The Graph before, the workflow will feel familiar while running faster and with far less overhead.

You can follow along even if you are new to Ormi. Setup takes only a few minutes.

Want the quick version?
See the official USDT0 Subgraph tutorial in Ormi Docs

What is USDT0?

USDT0 is Tether’s omnichain token standard that brings USDT to new blockchains. It locks USDT on Ethereum and issues equivalent tokens on destination chains through LayerZero. Users can transfer USDT0 across supported chains without needing liquidity, and redeem it back 1:1 for USDT. USDT0 standardizes how Tether assets expand to new ecosystems and aims to make Tether the default asset issuer on every chain.

What you’ll build

By the end of this tutorial, you will:

  • Deploy a subgraph indexing USDT0 events on the Stable Chain
  • Track transfers, minting, burning, and more
  • Query live and historical USDT0 data through a GraphQL endpoint
  • Use Ormi’s dashboard to monitor sync health in real time

This setup is production-ready and free to use.

Prerequisites

Getting started

Step 1: Create an API key

Log in to the Ormi dashboard > API Keys > Create Key.

Keep this page open; you’ll need the key during deployment.

Step 2: Create a Project Folder

mkdir usdt0-stable
cd usdt0-stable

Initialize a Node project:

npm init -y

Step 3: Install dependencies

npm install --save-dev @graphprotocol/graph-ts
npm install --save-dev assemblyscript

Add the required files

usdt0-stable/
├─ abis/
│  └─ USDT0.json               
├─ mappings/
│  └─ USDT0.ts                  
├─ schema.graphql               
├─ subgraph.yaml                
├─ generated/                   
└─ build/                       

Below are the exact contents used for this USDT0 example.

subgraph.yaml

yaml
specVersion: 1.2.0
schema:
  file: ./schema.graphql

dataSources:
  - kind: ethereum
    name: USDT0
    network: stable 
    source:
      address: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736"
      abi: USDT0
      startBlock: 2443970
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      entities:
        - Token
        - Account
        - Transfer
        - Mint
        - Burn
        - BlocklistChange
        - Authorization
      abis:
        - name: USDT0
          file: ./abis/USDT0.json
      eventHandlers:
        - event: Transfer(indexed address,indexed address,uint256)
          handler: handleTransfer
        - event: Mint(indexed address,uint256)
          handler: handleMint
        - event: Burn(indexed address,uint256)
          handler: handleBurn
        - event: CrosschainMint(indexed address,uint256,indexed address)
          handler: handleCrosschainMint
        - event: CrosschainBurn(indexed address,uint256,indexed address)
          handler: handleCrosschainBurn
        - event: BlockPlaced(indexed address)
          handler: handleBlockPlaced
        - event: BlockReleased(indexed address)
          handler: handleBlockReleased
        - event: DestroyedBlockedFunds(indexed address,uint256)
          handler: handleDestroyedBlockedFunds
        - event: AuthorizationUsed(indexed address,indexed bytes32)
          handler: handleAuthorizationUsed
        - event: AuthorizationCanceled(indexed address,indexed bytes32)
          handler: handleAuthorizationCanceled
        - event: LogUpdateNameAndSymbol(string,string)
          handler: handleLogUpdateNameAndSymbol
        - event: OwnershipTransferred(indexed address,indexed address)
          handler: handleOwnershipTransferred
        - event: LogSetOFTContract(indexed address)
          handler: handleLogSetOFTContract
      file: ./mappings/USDT0.ts

schema.graphql

graphql
type Token @entity(immutable: false) {
  id: ID!                          # contract address
  name: String!
  symbol: String!
  decimals: Int!
  totalSupply: BigInt!             # derived from Mint/Burn/Crosschain*/DestroyedBlockedFunds
  createdAtBlock: BigInt!
  createdAtTimestamp: BigInt!

  transferCount: BigInt!
  mintCount: BigInt!
  burnCount: BigInt!
  crosschainMintCount: BigInt!
  crosschainBurnCount: BigInt!

  owner: Account
  oftContract: Bytes
}

type Account @entity(immutable: false) {
  id: ID!                          # address
  balance: BigInt!                 # derived solely from events
  isBlocked: Boolean!              # derived from BlockPlaced/BlockReleased
  createdAtBlock: BigInt!
  createdAtTimestamp: BigInt!
  transferCount: BigInt!
}

type Transfer @entity(immutable: true) {
  id: ID!                          # txHash-logIndex
  token: Token!
  from: Account                    
  to: Account                     
  value: BigInt!
  txHash: Bytes!
  blockNumber: BigInt!
  timestamp: BigInt!
}

type Mint @entity(immutable: true) {
  id: ID!
  token: Token!
  to: Account!
  amount: BigInt!
  isCrosschain: Boolean!
  sender: Account
  txHash: Bytes!
  blockNumber: BigInt!
  timestamp: BigInt!
}

type Burn @entity(immutable: true) {
  id: ID!
  token: Token!
  from: Account                    
  amount: BigInt!
  isCrosschain: Boolean!
  sender: Account
  txHash: Bytes!
  blockNumber: BigInt!
  timestamp: BigInt!
}

type BlocklistChange @entity(immutable: true) {
  id: ID!
  user: Account!
  isBlocked: Boolean!
  destroyedBalance: BigInt
  txHash: Bytes!
  blockNumber: BigInt!
  timestamp: BigInt!
}

type Authorization @entity(immutable: false) {
  id: ID!                          # `${authorizer}-${nonce}`
  authorizer: Account!
  nonce: Bytes!
  used: Boolean!
  canceled: Boolean!
  txHashUsed: Bytes
  txHashCanceled: Bytes
}

USDT0.ts Mapping file

typescript
// mappings/USDT0.ts

import {
  BigInt,
  Address,
  Bytes,
} from "@graphprotocol/graph-ts"

import {
  Transfer as TransferEvent,
  Mint as MintEvent,
  Burn as BurnEvent,
  CrosschainMint as CrosschainMintEvent,
  CrosschainBurn as CrosschainBurnEvent,
  BlockPlaced,
  BlockReleased,
  DestroyedBlockedFunds,
  AuthorizationUsed,
  AuthorizationCanceled,
  LogUpdateNameAndSymbol,
  OwnershipTransferred,
  LogSetOFTContract,
} from "../generated/USDT0/USDT0"

import {
  Token,
  Account,
  Transfer,
  Mint,
  Burn,
  BlocklistChange,
  Authorization,
} from "../generated/schema"

const USDT0_ADDRESS = "0x779Ded0c9e1022225f8E0630b35a9b54bE713736"
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"

// Helpers

function createEventId(txHash: Bytes, logIndex: BigInt): string {
  return txHash.toHex() + "-" + logIndex.toString()
}

function getToken(eventBlockNumber: BigInt, eventTimestamp: BigInt): Token {
  let token = Token.load(USDT0_ADDRESS)
  if (token == null) {
    token = new Token(USDT0_ADDRESS)
    token.name = "USDT0"
    token.symbol = "USDT0"
    token.decimals = 6
    token.totalSupply = BigInt.zero()
    token.createdAtBlock = eventBlockNumber
    token.createdAtTimestamp = eventTimestamp
    token.transferCount = BigInt.zero()
    token.mintCount = BigInt.zero()
    token.burnCount = BigInt.zero()
    token.crosschainMintCount = BigInt.zero()
    token.crosschainBurnCount = BigInt.zero()
  }
  return token as Token
}

function getAccount(
  addr: Address,
  blockNumber: BigInt,
  timestamp: BigInt
): Account {
  let id = addr.toHex()
  let account = Account.load(id)
  if (account == null) {
    account = new Account(id)
    account.balance = BigInt.zero()
    account.isBlocked = false
    account.createdAtBlock = blockNumber
    account.createdAtTimestamp = timestamp
    account.transferCount = BigInt.zero()
  }
  return account as Account
}

// Handlers

export function handleTransfer(event: TransferEvent): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let from = getAccount(event.params.from, event.block.number, event.block.timestamp)
  let to = getAccount(event.params.to, event.block.number, event.block.timestamp)

  let value = event.params.value

  // compare strings directly instead of using .equals()
  if (from.id != ZERO_ADDRESS) {
    from.balance = from.balance.minus(value)
    from.transferCount = from.transferCount.plus(BigInt.fromI32(1))
    from.save()
  }

  if (to.id != ZERO_ADDRESS) {
    to.balance = to.balance.plus(value)
    to.transferCount = to.transferCount.plus(BigInt.fromI32(1))
    to.save()
  }

  token.transferCount = token.transferCount.plus(BigInt.fromI32(1))
  token.save()

  let transfer = new Transfer(
    createEventId(event.transaction.hash, event.logIndex)
  )
  transfer.token = token.id
  transfer.from = from.id
  transfer.to = to.id
  transfer.value = value
  transfer.txHash = event.transaction.hash
  transfer.blockNumber = event.block.number
  transfer.timestamp = event.block.timestamp
  transfer.save()
}

export function handleMint(event: MintEvent): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let to = getAccount(event.params._destination, event.block.number, event.block.timestamp)
  let amount = event.params._amount

  to.balance = to.balance.plus(amount)
  to.save()

  token.totalSupply = token.totalSupply.plus(amount)
  token.mintCount = token.mintCount.plus(BigInt.fromI32(1))
  token.save()

  let mint = new Mint(
    createEventId(event.transaction.hash, event.logIndex)
  )
  mint.token = token.id
  mint.to = to.id
  mint.amount = amount
  mint.isCrosschain = false
  mint.sender = null
  mint.txHash = event.transaction.hash
  mint.blockNumber = event.block.number
  mint.timestamp = event.block.timestamp
  mint.save()
}

export function handleBurn(event: BurnEvent): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let from = getAccount(event.params.from, event.block.number, event.block.timestamp)
  let amount = event.params.amount

  from.balance = from.balance.minus(amount)
  from.save()

  token.totalSupply = token.totalSupply.minus(amount)
  token.burnCount = token.burnCount.plus(BigInt.fromI32(1))
  token.save()

  let burn = new Burn(
    createEventId(event.transaction.hash, event.logIndex)
  )
  burn.token = token.id
  burn.from = from.id
  burn.amount = amount
  burn.isCrosschain = false
  burn.sender = null
  burn.txHash = event.transaction.hash
  burn.blockNumber = event.block.number
  burn.timestamp = event.block.timestamp
  burn.save()
}

export function handleCrosschainMint(event: CrosschainMintEvent): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let to = getAccount(event.params.to, event.block.number, event.block.timestamp)
  let sender = getAccount(event.params.sender, event.block.number, event.block.timestamp)
  let amount = event.params.amount

  to.balance = to.balance.plus(amount)
  to.save()

  token.totalSupply = token.totalSupply.plus(amount)
  token.crosschainMintCount = token.crosschainMintCount.plus(BigInt.fromI32(1))
  token.save()

  let mint = new Mint(
    createEventId(event.transaction.hash, event.logIndex)
  )
  mint.token = token.id
  mint.to = to.id
  mint.amount = amount
  mint.isCrosschain = true
  mint.sender = sender.id
  mint.txHash = event.transaction.hash
  mint.blockNumber = event.block.number
  mint.timestamp = event.block.timestamp
  mint.save()
}

export function handleCrosschainBurn(event: CrosschainBurnEvent): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let from = getAccount(event.params.from, event.block.number, event.block.timestamp)
  let sender = getAccount(event.params.sender, event.block.number, event.block.timestamp)
  let amount = event.params.amount

  from.balance = from.balance.minus(amount)
  from.save()

  token.totalSupply = token.totalSupply.minus(amount)
  token.crosschainBurnCount = token.crosschainBurnCount.plus(BigInt.fromI32(1))
  token.save()

  let burn = new Burn(
    createEventId(event.transaction.hash, event.logIndex)
  )
  burn.token = token.id
  burn.from = from.id
  burn.amount = amount
  burn.isCrosschain = true
  burn.sender = sender.id
  burn.txHash = event.transaction.hash
  burn.blockNumber = event.block.number
  burn.timestamp = event.block.timestamp
  burn.save()
}

export function handleBlockPlaced(event: BlockPlaced): void {
  let user = getAccount(event.params._user, event.block.number, event.block.timestamp)
  user.isBlocked = true
  user.save()

  let change = new BlocklistChange(
    createEventId(event.transaction.hash, event.logIndex)
  )
  change.user = user.id
  change.isBlocked = true
  change.destroyedBalance = null
  change.txHash = event.transaction.hash
  change.blockNumber = event.block.number
  change.timestamp = event.block.timestamp
  change.save()
}

export function handleBlockReleased(event: BlockReleased): void {
  let user = getAccount(event.params._user, event.block.number, event.block.timestamp)
  user.isBlocked = false
  user.save()

  let change = new BlocklistChange(
    createEventId(event.transaction.hash, event.logIndex)
  )
  change.user = user.id
  change.isBlocked = false
  change.destroyedBalance = null
  change.txHash = event.transaction.hash
  change.blockNumber = event.block.number
  change.timestamp = event.block.timestamp
  change.save()
}

export function handleDestroyedBlockedFunds(event: DestroyedBlockedFunds): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let user = getAccount(event.params._blockedUser, event.block.number, event.block.timestamp)
  let amount = event.params._balance

  token.totalSupply = token.totalSupply.minus(amount)
  token.save()

  user.balance = BigInt.zero()
  user.save()

  let change = new BlocklistChange(
    createEventId(event.transaction.hash, event.logIndex)
  )
  change.user = user.id
  change.isBlocked = user.isBlocked
  change.destroyedBalance = amount
  change.txHash = event.transaction.hash
  change.blockNumber = event.block.number
  change.timestamp = event.block.timestamp
  change.save()
}

export function handleAuthorizationUsed(event: AuthorizationUsed): void {
  let authorizer = getAccount(event.params.authorizer, event.block.number, event.block.timestamp)
  let id = authorizer.id + "-" + event.params.nonce.toHex()
  let auth = Authorization.load(id)
  if (auth == null) {
    auth = new Authorization(id)
    auth.authorizer = authorizer.id
    auth.nonce = event.params.nonce
    auth.used = false
    auth.canceled = false
  }
  auth.used = true
  auth.txHashUsed = event.transaction.hash
  auth.save()
}

export function handleAuthorizationCanceled(event: AuthorizationCanceled): void {
  let authorizer = getAccount(event.params.authorizer, event.block.number, event.block.timestamp)
  let id = authorizer.id + "-" + event.params.nonce.toHex()
  let auth = Authorization.load(id)
  if (auth == null) {
    auth = new Authorization(id)
    auth.authorizer = authorizer.id
    auth.nonce = event.params.nonce
    auth.used = false
    auth.canceled = false
  }
  auth.canceled = true
  auth.txHashCanceled = event.transaction.hash
  auth.save()
}

export function handleLogUpdateNameAndSymbol(event: LogUpdateNameAndSymbol): void {
  let token = getToken(event.block.number, event.block.timestamp)
  token.name = event.params.name
  token.symbol = event.params.symbol
  token.save()
}

export function handleOwnershipTransferred(event: OwnershipTransferred): void {
  let token = getToken(event.block.number, event.block.timestamp)
  let newOwner = getAccount(event.params.newOwner, event.block.number, event.block.timestamp)
  token.owner = newOwner.id
  token.save()
}

export function handleLogSetOFTContract(event: LogSetOFTContract): void {
  let token = getToken(event.block.number, event.block.timestamp)
  token.oftContract = event.params.oftContract
  token.save()
}

USDT0 ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"}],"name":"BlockPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"}],"name":"BlockReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"CrosschainBurn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"CrosschainMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_blockedUser","type":"address"},{"indexed":false,"internalType":"uint256","name":"_balance","type":"uint256"}],"name":"DestroyedBlockedFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oftContract","type":"address"}],"name":"LogSetOFTContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"LogUpdateNameAndSymbol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_destination","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECEIVE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"addToBlockedList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"crosschainBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_destination","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"crosschainMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_blockedUser","type":"address"}],"name":"destroyBlockedFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBlocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTrusted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_destination","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_recipients","type":"address[]"},{"internalType":"uint256[]","name":"_values","type":"uint256[]"}],"name":"multiTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oftContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"removeFromBlockedList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oftContract","type":"address"}],"name":"setOFTContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"name":"updateNameAndSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Build the Subgraph

Generate types:

graph codegen

Build:

graph build

You should see .wasmfiles inside /build.

Deploy to Ormi Subgraph

Use your API key and choose a subgraph name

graph deploy <graph-name> \
--node https://subgraph.api.ormilabs.com/deploy \
--ipfs https://subgraph.api.ormilabs.com/ipfs \
--deploy-key <API key>

Once deployed, the subgraph will appear in your Ormi dashboard.

Check sync status

Go to Subgraphs > select your subgraph > view Sync Status.

Ormi indexes Stable Chain in real time, so syncing is much faster than legacy providers.

Query USDT0 data.

Open the built-in GraphQL Explorer from the dashboard.

Run this sample query to fetch the latest transfers:

{
  transfers(first: 10, orderBy: timestamp, orderDirection: desc) {
    id
    from { id }
    to { id }
    value
    blockNumber
    timestamp
    txHash
  }
}

You’ll immediately see live results from Stable Chain.

You’re done

You now have a live USDT0 subgraph indexing on Stable Chain using Ormi’s 0xGraph.

Use this foundation to build:

  • dashboards
  • wallets
  • compliance tooling
  • settlement systems
  • agent-based automation
  • analytics pipelines

For more subgraph tutorials and supported chains, visit:

About Ormi

Ormi is the next-generation data layer for Web3, purpose-built for real-time, high-throughput applications like DeFi, gaming, wallets, and on-chain infrastructure. Its hybrid architecture ensures sub-30ms latency and up to 4,000 RPS for live subgraph indexing.

With 99.9% uptime and deployments across ecosystems representing $50B+ in TVL and $100B+ in annual transaction volume, Ormi is trusted to power the most demanding production environments without throttling or delay.