Skip to content

Asset Transfer Subscription

← Back to Examples

This example demonstrates ASA lifecycle subscription.

  • Subscribe to asset creation with assetCreate filter
  • Subscribe to asset transfers with type and assetId filters
  • Track opt-in and transfer transactions
  • LocalNet running (via algokit localnet start)

From the repository’s examples/subscriber directory:

Terminal window
cd examples/subscriber
npx tsx 04-asset-transfer.ts

View source on GitHub

04-asset-transfer.ts
/**
* Example: Asset Transfer Subscription
*
* This example demonstrates ASA lifecycle subscription.
* - Subscribe to asset creation with assetCreate filter
* - Subscribe to asset transfers with type and assetId filters
* - Track opt-in and transfer transactions
*
* Prerequisites:
* - LocalNet running (via `algokit localnet start`)
*/
import { algo, AlgorandClient } from '@algorandfoundation/algokit-utils'
import { printHeader, printStep, printInfo, printSuccess, printError, shortenAddress, createFilterTester } from './shared/utils.js'
async function main() {
printHeader('04 — Asset Transfer Subscription')
// Step 1: Connect to LocalNet
printStep(1, 'Connect to LocalNet')
const algorand = AlgorandClient.defaultLocalNet()
const status = await algorand.client.algod.status()
printInfo(`Current round: ${status.lastRound.toString()}`)
printSuccess('Connected to LocalNet')
// Step 2: Create 2 accounts (creator, receiver)
printStep(2, 'Create and fund 2 accounts (creator, receiver)')
const creator = await algorand.account.fromEnvironment('ASSET_CREATOR', algo(100))
const receiver = await algorand.account.fromEnvironment('ASSET_RECEIVER', algo(100))
const creatorAddr = creator.addr.toString()
const receiverAddr = receiver.addr.toString()
printInfo(`Creator: ${shortenAddress(creatorAddr)}`)
printInfo(`Receiver: ${shortenAddress(receiverAddr)}`)
printSuccess('2 accounts created and funded')
// Step 3: Create an ASA (fungible token) from creator account
printStep(3, 'Create ASA from creator account')
const createResult = await algorand.send.assetCreate({
sender: creator.addr,
total: 1_000_000n,
decimals: 0,
assetName: 'TestToken',
unitName: 'TT',
})
const assetId = createResult.assetId
const createRound = createResult.confirmation.confirmedRound!
printInfo(`Created asset ID: ${assetId.toString()}`)
printInfo(`Confirmed round: ${createRound.toString()}`)
printSuccess('ASA created')
// Step 4: Receiver opts in to the asset
printStep(4, 'Receiver opts in to asset')
const optInResult = await algorand.send.assetOptIn({
sender: receiver.addr,
assetId,
})
printInfo(`Opt-in txn ID: ${optInResult.txIds.at(-1)}`)
printSuccess('Receiver opted in')
// Step 5: Creator transfers tokens to receiver
printStep(5, 'Transfer 500 tokens from creator to receiver')
const transferResult = await algorand.send.assetTransfer({
sender: creator.addr,
receiver: receiver.addr,
assetId,
amount: 500n,
})
printInfo(`Transfer txn ID: ${transferResult.txIds.at(-1)}`)
printSuccess('Transferred 500 tokens')
// Watermark: just before the asset creation round
const watermarkBefore = createRound - 1n
const testFilter = createFilterTester(algorand.client.algod, watermarkBefore)
// Step 6: Subscribe with assetCreate filter — matches the creation transaction
printStep(6, 'Filter: assetCreate = true')
const createTxns = await testFilter(
'asset-create', { assetCreate: true }, 1,
'assetCreate filter matched 1 creation transaction',
(txn) => printInfo(` Created asset: ${txn.createdAssetId} | txn: ${txn.id}`),
)
// Step 7: Subscribe with type=axfer + assetId filter — matches opt-in and transfer
printStep(7, 'Filter: type = axfer, assetId = created asset')
const axferTxns = await testFilter(
'asset-transfers', { type: 'axfer', assetId: assetId }, 2,
'axfer filter matched 2 transactions (opt-in + transfer)',
(txn) => {
const axfer = txn.assetTransferTransaction!
printInfo(
` Transfer: ${shortenAddress(txn.sender)} -> ${shortenAddress(axfer.receiver)} | amount: ${axfer.amount} | txn: ${txn.id}`,
)
},
)
// Step 8: Summary
printStep(8, 'Summary')
printInfo(`Asset ID: ${assetId.toString()}`)
printInfo(`assetCreate filter: ${createTxns.length} matched (creation)`)
printInfo(`axfer + assetId filter: ${axferTxns.length} matched (opt-in + transfer)`)
printHeader('Example complete')
}
main().catch((err) => {
printError(err.message)
process.exit(1)
})