Skip to content
Algorand Developer Portal

Payment Filters

← Back to Examples

This example demonstrates payment transaction filters.

  • Filter by sender, receiver, amount range, and note prefix
  • Verify each filter matches the expected transactions
  • LocalNet running (via algokit localnet start)

From the repository’s examples/subscriber directory:

Terminal window
cd examples/subscriber
npx tsx 03-payment-filters.ts

View source on GitHub

03-payment-filters.ts
/**
* Example: Payment Filters
*
* This example demonstrates payment transaction filters.
* - Filter by sender, receiver, amount range, and note prefix
* - Verify each filter matches the expected transactions
*
* Prerequisites:
* - LocalNet running (via `algokit localnet start`)
*/
import { algo, AlgorandClient, microAlgo } from '@algorandfoundation/algokit-utils';
import {
printHeader,
printStep,
printInfo,
printSuccess,
printError,
shortenAddress,
formatMicroAlgo,
createFilterTester,
type SubscribedTransaction,
} from './shared/utils.js';
async function main() {
printHeader('03 — Payment Filters');
// 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 3 accounts (sender, receiver, thirdParty)
printStep(2, 'Create and fund 3 accounts (sender, receiver, thirdParty)');
const sender = await algorand.account.fromEnvironment('FILTER_SENDER', algo(100));
const receiver = await algorand.account.fromEnvironment('FILTER_RECEIVER', algo(100));
const thirdParty = await algorand.account.fromEnvironment('FILTER_THIRD_PARTY', algo(100));
const senderAddr = sender.addr.toString();
const receiverAddr = receiver.addr.toString();
const thirdPartyAddr = thirdParty.addr.toString();
printInfo(`Sender: ${shortenAddress(senderAddr)}`);
printInfo(`Receiver: ${shortenAddress(receiverAddr)}`);
printInfo(`Third party: ${shortenAddress(thirdPartyAddr)}`);
printSuccess('3 accounts created and funded');
// Step 3: Send 5 payments with varying senders, receivers, amounts, and notes
printStep(3, 'Send 5 payments with varying parameters');
const payments = [
{ sender: sender.addr, receiver: receiver.addr, amount: algo(1), note: 'invoice-001' },
{ sender: sender.addr, receiver: thirdParty.addr, amount: algo(5), note: 'invoice-002' },
{ sender: receiver.addr, receiver: sender.addr, amount: algo(2), note: 'receipt-001' },
{ sender: thirdParty.addr, receiver: receiver.addr, amount: algo(3), note: 'invoice-003' },
{
sender: sender.addr,
receiver: receiver.addr,
amount: microAlgo(500_000),
note: 'receipt-002',
},
];
const txnResults = [];
for (const [i, p] of payments.entries()) {
const result = await algorand.send.payment({
sender: p.sender,
receiver: p.receiver,
amount: p.amount,
note: p.note,
});
txnResults.push(result);
printInfo(
`Txn ${i + 1}: ${shortenAddress(p.sender.toString())} -> ${shortenAddress(p.receiver.toString())} | ${formatMicroAlgo(p.amount.microAlgo)} | note: "${p.note}"`,
);
}
printSuccess(`Sent ${payments.length} payments`);
// Record watermark before first txn
const watermarkBefore = txnResults[0].confirmation!.confirmedRound! - 1n;
const testFilter = createFilterTester(algorand.client.algod, watermarkBefore);
const formatPayment = (txn: SubscribedTransaction) => {
const amount = txn.paymentTransaction?.amount ?? 0n;
const note = txn.note ? Buffer.from(txn.note).toString('utf-8') : '';
printInfo(` Matched: ${txn.id} | amount: ${formatMicroAlgo(amount)} | note: "${note}"`);
};
// Step 4: Filter by sender (sender account only)
printStep(4, 'Filter: sender = sender account');
const senderTxns = await testFilter(
'sender-filter',
{ sender: senderAddr },
3,
'Sender filter matched 3 payments from sender',
formatPayment,
);
// Step 5: Filter by receiver (receiver account only)
printStep(5, 'Filter: receiver = receiver account');
const receiverTxns = await testFilter(
'receiver-filter',
{ receiver: receiverAddr },
3,
'Receiver filter matched 3 payments to receiver',
formatPayment,
);
// Step 6: Filter by minAmount/maxAmount range (1_000_000 to 3_000_000 microAlgo)
printStep(6, 'Filter: minAmount=1000000, maxAmount=3000000');
// Payments in range [1M, 3M]: txn1 (1M), txn3 (2M), txn4 (3M) = 3
const rangeTxns = await testFilter(
'amount-range',
{ minAmount: 1_000_000, maxAmount: 3_000_000 },
3,
'Amount range filter matched 3 payments in [1M, 3M] microAlgo',
formatPayment,
);
// Step 7: Filter by notePrefix ("invoice")
printStep(7, 'Filter: notePrefix = "invoice"');
// Txns with "invoice" prefix: txn1, txn2, txn4 = 3
const invoiceTxns = await testFilter(
'note-prefix',
{ notePrefix: 'invoice' },
3,
'Note prefix filter matched 3 payments with "invoice" prefix',
formatPayment,
);
// Step 8: Summary
printStep(8, 'Summary');
printInfo(`Sender filter: ${senderTxns.length} matched`);
printInfo(`Receiver filter: ${receiverTxns.length} matched`);
printInfo(`Amount [1M,3M] filter: ${rangeTxns.length} matched`);
printInfo(`notePrefix="invoice" filter: ${invoiceTxns.length} matched`);
printHeader('Example complete');
}
main().catch(err => {
printError(err.message);
process.exit(1);
});