Skip to Content
Supra Layer 1SupraMVMTypeScript SDKGuidesTrack an Account's Transactions

Track an Account’s Transactions

How to list the transactions of an account with the TypeScript SDK: which feed lists which transactions, how to read a feed to its end, and where the transfer of a coin or a fungible asset shows.

Which feed to read

An account has several feeds. Each lists a different set of transactions, and a transaction can be in more than one.

MethodListsNodes
supra.account.getAccountTransactionsThe user transactions the account sentEvery node
supra.account.getAccountCoinTransactionsThe transactions that touched a CoinStore of the accountEvery node
supra.account.getAccountFaTransactionsThe transactions that withdrew from or deposited to one fungible storeEvery node
supra.account.getAccountAutoTransactionsThe automated transactions of the accountEvery node
supra.account.getAccountHistoryEvery finalized transaction that concerns the account, once, with the categories it matchedNodes from supra_node_v12.0.0 (Devnet)

getAccountFaTransactions takes the address of a fungible store, not of an account: an account address returns an empty page. supra.account.getPrimaryStoreAddress({ accountAddress, metadataAddress }) derives the address of an account’s primary store for a fungible asset, SUPRA when metadataAddress is left out. getAccountHistory omits automated transactions, which getAccountAutoTransactions lists. On a Testnet or Mainnet node (v11.7) it throws SupraRouteUnsupportedError, a SupraNotFoundError. It is a preview that can change until supra_node_v12.0.0 reaches Testnet; see Read methods of v12 nodes.

Which feed shows SUPRA

An account holds SUPRA as a coin, in its CoinStore, as a fungible asset, in its primary fungible store, or in both. The form decides which feed lists a SUPRA transfer:

  • On Devnet, SUPRA is held in the primary store, including the SUPRA from the faucet. A transfer is in getAccountTransactions for the sender, in getAccountFaTransactions on the primary store of the sender and of the receiver, and in getAccountHistory.
  • On Testnet and Mainnet (v11.7), SUPRA is held in the CoinStore, in the primary store, or in both. A transfer of SUPRA held in the CoinStore is in getAccountTransactions for the sender and in getAccountCoinTransactions. A transfer of SUPRA held in the primary store is in getAccountFaTransactions on the primary store.

getAccountCoinTransactions returns an empty page for SUPRA held in a primary store. An account that holds SUPRA in both forms needs both feeds, or getAccountHistory where the node serves it, which lists SUPRA held in a CoinStore as well. getAccountTransactions lists only what the account sent: the receiver of a transfer finds it in the coin or the fungible asset feed. The faucet’s transaction, which funds an account, is also in those feeds and not in getAccountTransactions.

Items can be of any txn_type. Only user and automated transactions have a header, payload and authenticator, so narrow on txn_type before reading them, as the sample below does.

Read a feed to its end

Each method returns { response, cursor }, where response is one page. Pass cursor back unchanged, as options.start, until it is undefined. For getAccountAutoTransactions the option is options.cursor. options.count is the page size, 1 to 100; the node’s default is 20. A feed is newest first, and options.ascending: true reads it oldest first.

  • A page can be empty and still carry a cursor, so do not stop at an empty page. The feed has ended when cursor is undefined.
  • getAccountHistory applies options.categories while it scans, so a page can hold fewer entries than count, or none, before the history is exhausted. The cursor "0" does not end it.
  • getAccountTransactions pages by sequence number. options.start is a sequence number, inclusive in both orders, or the cursor of the previous page. The SDK computes the cursor as the sequence number after the last transaction of the page, in the requested order, and returns undefined for an empty page or when a descending read reaches sequence number 0.

Create read_feed.ts

The example uses Devnet. It sends three transfers and reads two feeds two items at a time. Where the faucet’s SUPRA is a coin (a v11.7 node), the primary store of the account has no movements, so the second feed is empty.

mkdir supra-feeds && cd supra-feeds npm init -y npm install supra-ts-sdk@devnet npm install --save-dev tsx
import { Network, type PaginatedResponse, SupraAccount, SupraClient, TransactionType } from 'supra-ts-sdk' // Reads a feed to its end: the page function takes the cursor of the previous page, `undefined` for the first one. async function readAll<T>(readPage: (start: string | undefined) => Promise<PaginatedResponse<T[]>>): Promise<T[]> { const items: T[] = [] let cursor: string | undefined do { const page = await readPage(cursor) items.push(...page.response) // a page can be empty and still carry a cursor cursor = page.cursor } while (cursor !== undefined) return items } async function main() { const supra = new SupraClient({ network: Network.DEVNET }) const sender = new SupraAccount() const receiver = new SupraAccount().address() const faucet = await supra.faucet.fundAccountWithFaucet({ accountAddress: sender.address() }) await supra.transaction.waitForTransaction({ transactionHash: faucet.hash }) // Three transfers, so that the account has something to read. for (let i = 0; i < 3; i++) { await supra.coin.transferSupraCoin({ senderAccount: sender, receiverAccountAddress: receiver, amount: 1_000n, optionalTransactionArgs: { enableTransactionWaitAndSimulationArgs: { enableWaitForTransaction: true } }, }) } // The transactions the account sent, oldest first, two to a page. `start` is a sequence number. const sent = await readAll((start) => supra.account.getAccountTransactions({ accountAddress: sender.address(), options: { count: 2, ascending: true, start }, }), ) for (const txn of sent) { // Only user and automated transactions have a header, payload and authenticator. if (txn.txn_type === TransactionType.User || txn.txn_type === TransactionType.Auto) { console.log('sent', txn.header.sequence_number, txn.hash) } } // The movements of the account's primary fungible store, newest first. const storeAddress = await supra.account.getPrimaryStoreAddress({ accountAddress: sender.address() }) const movements = await readAll((start) => supra.account.getAccountFaTransactions({ storeAddress, options: { count: 2, start } }), ) console.log('store movements:', movements.length) } main()
npx tsx read_feed.ts

The script prints the three transfers with sequence numbers 0, 1 and 2. On Devnet store movements: is 4: the faucet transaction and the three transfers. On a v11.7 node, where the faucet’s SUPRA is held as a coin, it is 0.

Transfers of other tokens

supra.coin.transferCoin moves a coin by its type (coinType), and supra.fungibleAsset.transferFungibleAsset moves a fungible asset by the address of its metadata object (assetAddress). Both take the usual optionalTransactionArgs and use the client’s sequence numbers, like the SUPRA transfers. The feed that lists a transfer follows from where the token is held:

  • A coin transfer withdraws from the sender’s CoinStore of that coin type, and takes any remainder from the sender’s primary store when the coin has a paired fungible asset. It deposits to the receiver’s CoinStore, or to the receiver’s primary store when the receiver has no CoinStore and the coin has a paired fungible asset. It shows in getAccountCoinTransactions for each account whose CoinStore it moved, and in getAccountFaTransactions on each primary store it moved. SUPRA held in primary stores moves between primary stores only.
  • A fungible asset transfer moves only the balance of the sender’s primary store of that asset, and shows in getAccountFaTransactions on the primary stores of the sender and the receiver. There is one feed for each asset, at getPrimaryStoreAddress({ accountAddress, metadataAddress }).
  • Either shows in getAccountTransactions for the sender, and in getAccountHistory where the node serves it.

Create transfer_tokens.ts

SUPRA is the one token every network has, so the script transfers it as a coin and as a fungible asset. For another token, replace the coin type and the asset address. On a v11.7 node, the SUPRA of a new account is a coin, so the script moves it to the primary store with migrateCoinToFungibleStore before the fungible asset transfer. The script prints the feeds that list each transfer.

import { Network, SupraAccount, SupraClient, SupraRouteUnsupportedError } from 'supra-ts-sdk' async function main() { const supra = new SupraClient({ network: Network.DEVNET }) const sender = new SupraAccount() const receiver = new SupraAccount().address() const waitForCommit = { enableTransactionWaitAndSimulationArgs: { enableWaitForTransaction: true } } const faucet = await supra.faucet.fundAccountWithFaucet({ accountAddress: sender.address() }) await supra.transaction.waitForTransaction({ transactionHash: faucet.hash }) // SUPRA stands in for another token: pass the coin type, or the address of the fungible asset's metadata object. const coinTransfer = await supra.coin.transferCoin({ senderAccount: sender, receiverAccountAddress: receiver, amount: 1_000n, coinType: '0x1::supra_coin::SupraCoin', optionalTransactionArgs: waitForCommit, }) // A fungible asset transfer moves only what the primary fungible store holds. SUPRA that an account holds as a coin // (Testnet and Mainnet) is moved to the store first. const { coin } = await supra.account.getAccountSupraBalance({ accountAddress: sender.address() }) if (coin > 0n) { await supra.fungibleAsset.migrateCoinToFungibleStore({ senderAccount: sender, optionalTransactionArgs: waitForCommit, }) } const faTransfer = await supra.fungibleAsset.transferFungibleAsset({ senderAccount: sender, receiverAccountAddress: receiver, amount: 1_000n, assetAddress: '0xa', optionalTransactionArgs: waitForCommit, }) // The first page of each feed of the sender, and of the primary store that holds the SUPRA the transfers moved. const accountAddress = sender.address() const storeAddress = await supra.account.getPrimaryStoreAddress({ accountAddress, metadataAddress: '0xa' }) const feeds = { sent: (await supra.account.getAccountTransactions({ accountAddress })).response, coin: (await supra.account.getAccountCoinTransactions({ accountAddress })).response, fa: (await supra.account.getAccountFaTransactions({ storeAddress })).response, history: await supra.account .getAccountHistory({ accountAddress }) .then((page) => page.response.map((entry) => entry.transaction)) .catch((error) => { if (error instanceof SupraRouteUnsupportedError) { return [] // a v11.7 node has no account history } throw error }), } for (const [name, transaction] of [ ['transferCoin', coinTransfer], ['transferFungibleAsset', faTransfer], ] as const) { const listedIn = Object.entries(feeds) .filter(([, transactions]) => transactions.some((listed) => listed.hash === transaction.hash)) .map(([feed]) => feed) console.log(name, 'is listed in:', listedIn.join(', ')) } } main()
npx tsx transfer_tokens.ts

On Devnet, where SUPRA is held in the primary store, the script prints sent, fa, history for both transfers. On a v11.7 node it prints sent, coin for transferCoin, which moved the account’s CoinStore, and sent, fa for transferFungibleAsset, which moved the primary store after the migration.

Settlement items

A cross-VM settlement of native SUPRA, on nodes from supra_node_v12.0.0, appears in the coin and fungible asset feeds as an item with txn_type settlement and no header, payload or authenticator. getAccountCoinTransactions and getAccountFaTransactions take options.type: 'settlement' to list only those. See Read methods of v12 nodes for the settlement methods.

Last updated on