Skip to main content

Token & NFT balances

The native addressBalance() only returns the chain's native coin (ETH, AVAX, etc.). To list every ERC-20, ERC-721, and ERC-1155 an address holds — without having to know the contract addresses up front — connect to an EVM network and call .addressTokenBalances(). ChainGate discovers the contracts the address has interacted with and returns each holding enriched with token metadata (name, symbol, decimals, logo) and, for NFTs, the list of owned token IDs.

Native EVM networks only

addressTokenBalances() is available on Ethereum and Avalanche connectors. It is not available on UTXO networks (Bitcoin, Litecoin, Dogecoin, Bitcoin Cash) or on custom EVM RPC networks (cg.networks.evmRpc(...)), since contract discovery requires ChainGate's indexer.

Example usage

import { importWallet, ChainGate } from 'chaingate'

const wallet = importWallet({ phrase: 'some bip39 phrase ...' })
const cg = new ChainGate()

const ethereum = cg.connect(cg.networks.ethereum, wallet)
const tokens = await ethereum.addressTokenBalances()

for (const token of tokens) {
console.log(token.standard, token.symbol, token.base().toString())
}

What each entry contains

Every item in the returned array is an Amount enriched with token-specific fields:

  • .standard'ERC-20', 'ERC-721', 'ERC-1155', or 'unknown'.
  • .contractAddress — Token contract address (with 0x prefix).
  • .symbol — Token symbol (e.g., 'USDC', 'BAYC').
  • .name — Human-readable token name (e.g., 'USD Coin').
  • .base() — Balance in base units as a Decimal (for ERC-20: divided by 10^decimals; for ERC-721/1155: token count).
  • .min() — Balance in minimal units as a bigint.
  • .logoUrl — Token logo URL (when available).
  • .ownedTokens(ERC-721 / ERC-1155 only) Array of { id, uri } for each owned token. The raw tokenURI is included; use getNftMetadata() to decode it.

Filter by token standard

A common pattern is to split holdings into fungible tokens and NFTs:

const tokens = await ethereum.addressTokenBalances()

const erc20s = tokens.filter(t => t.standard === 'ERC-20')
const nfts = tokens.filter(t => t.standard === 'ERC-721' || t.standard === 'ERC-1155')

console.log('Fungible holdings:', erc20s.length)
console.log('NFT collections:', nfts.length)

NFT holdings — list owned token IDs

For ERC-721 / ERC-1155 entries, ownedTokens lists the token IDs the address holds, each with the raw tokenURI string from the contract:

const nfts = (await ethereum.addressTokenBalances())
.filter(t => t.standard === 'ERC-721' || t.standard === 'ERC-1155')

for (const collection of nfts) {
console.log(collection.name, '×', collection.base().toString())

for (const owned of collection.ownedTokens ?? []) {
console.log(' token', owned.id, '→', owned.uri)
}
}

For ERC-721 contracts that don't implement ERC721Enumerable, the balance count is still accurate but ownedTokens may come back empty — the contract simply doesn't expose a way to list IDs.

Decode NFT metadata

ownedTokens[].uri is the raw tokenURI — often an IPFS or data: URI. To get the decoded JSON metadata (name, image, attributes), use the explorer:

const eth = cg.explore(cg.networks.ethereum)

for (const owned of collection.ownedTokens ?? []) {
const meta = await eth.getNftMetadata(collection.contractAddress, owned.id)
console.log(meta.metadata.name, meta.metadata.image)
}

The image field returned by getNftMetadata() is a ChainGate proxy URL that resolves IPFS / data: / HTTPS URIs transparently — drop it directly into an <img src> without writing custom resolution logic.

Different address index

For HD wallets, query a different derivation index:

const tokens = await ethereum.addressTokenBalances({ index: 1 })

Without a wallet — query any address

If you don't need to derive the address from a wallet (e.g., showing the holdings of a third-party address in an explorer UI), use the EVM explorer directly:

const eth = cg.explore(cg.networks.ethereum)
const tokens = await eth.getAddressTokenBalances('0xVitalik...')

The return shape is identical to the connector method.

Key points

  • Discovery built in: ChainGate's indexer finds every contract the address has interacted with — no contract list to maintain.
  • One call, three standards: ERC-20, ERC-721, and ERC-1155 are returned in the same array; filter by .standard.
  • Amount-compatible: Each entry behaves like an Amount.base(), .min(), and .symbol all work.
  • NFT IDs included: ERC-721 / ERC-1155 entries expose ownedTokens; pair with getNftMetadata() to fetch decoded JSON.
  • Native EVM only: Available on Ethereum and Avalanche connectors. Not available on UTXO chains or evmRpc(...) networks.