Asset Transfer Subscription
Description
Section titled “Description”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
Section titled “Prerequisites”- LocalNet running (via
algokit localnet start)
Run This Example
Section titled “Run This Example”From the repository’s examples/subscriber directory:
cd examples/subscribernpx tsx 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)})Other examples
Section titled “Other examples”- Basic Poll Once
- Continuous Subscriber
- Payment Filters
- Asset Transfer Subscription
- App Call Subscription
- Multiple Named Filters
- Balance Change Tracking
- ARC-28 Event Subscription
- Inner Transaction Subscription
- Batch Handling & Data Mappers
- Watermark Persistence
- Sync Behaviours
- Custom Filters
- Stateless Subscriptions
- Lifecycle Hooks & Error Handling