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.
| Method | Lists | Nodes |
|---|---|---|
supra.account.getAccountTransactions | The user transactions the account sent | Every node |
supra.account.getAccountCoinTransactions | The transactions that touched a CoinStore of the account | Every node |
supra.account.getAccountFaTransactions | The transactions that withdrew from or deposited to one fungible store | Every node |
supra.account.getAccountAutoTransactions | The automated transactions of the account | Every node |
supra.account.getAccountHistory | Every finalized transaction that concerns the account, once, with the categories it matched | Nodes 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
getAccountTransactionsfor the sender, ingetAccountFaTransactionson the primary store of the sender and of the receiver, and ingetAccountHistory. - 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 theCoinStoreis ingetAccountTransactionsfor the sender and ingetAccountCoinTransactions. A transfer of SUPRA held in the primary store is ingetAccountFaTransactionson 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
cursorisundefined. getAccountHistoryappliesoptions.categorieswhile it scans, so a page can hold fewer entries thancount, or none, before the history is exhausted. The cursor"0"does not end it.getAccountTransactionspages by sequence number.options.startis 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 returnsundefinedfor 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 tsximport { 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.tsThe 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
CoinStoreof 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’sCoinStore, or to the receiver’s primary store when the receiver has noCoinStoreand the coin has a paired fungible asset. It shows ingetAccountCoinTransactionsfor each account whoseCoinStoreit moved, and ingetAccountFaTransactionson 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
getAccountFaTransactionson the primary stores of the sender and the receiver. There is one feed for each asset, atgetPrimaryStoreAddress({ accountAddress, metadataAddress }). - Either shows in
getAccountTransactionsfor the sender, and ingetAccountHistorywhere 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.tsOn 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.