This library a simple, but flexible / configurable Algorand transaction subscription / indexing mechanism. It allows you to quickly create Node.js or JavaScript services that follow or subscribe to the Algorand Blockchain.
Before installing, you’ll need to decide on the version you want to target. Version 2 and 3 have largerly the same feature set, however v2 leverages algosdk@>=2.9.0<3.0, whereas v3 leverages algosdk@>=3.0.0. It is recommended that you aim to target the latest version, however in some circumstances that might not be possible.
Once you’ve decided on the target version, this library can be installed from NPM using your favourite npm client, e.g.:
To target algosdk@2 and use version 2 of AlgoKit Subscriber, run the below:
npm install algosdk@^2.10.0 @algorandfoundation/algokit-utils@^7.1.0 @algorandfoundation/algokit-subscriber@^2.2.0
To target algosdk@3 and use the latest version of AlgoKit Subscriber, run the below:
npm install algosdk@^3.1.0 @algorandfoundation/algokit-utils @algorandfoundation/algokit-subscriber
Whilst we aim to minimise breaking changes, there are situations where they are required.
If you’re migrating from an older version to v3, please refer to the v3 migration guide.
// Create subscriber
const subscriber = new AlgorandSubscriber(
{
filters: [
{
name: 'filter1',
filter: {
type: TransactionType.pay,
sender: 'ABC...',
},
},
],
/* ... other options (use intellisense to explore) */
},
algod,
optionalIndexer,
)
// Set up subscription(s)
subscriber.on('filter1', async (transaction) => {
// ...
})
//...
// Set up error handling
subscriber.onError((e) => {
// ...
})
// Either: Start the subscriber (if in long-running process)
subscriber.start()
// OR: Poll the subscriber (if in cron job / periodic lambda)
subscriber.pollOnce()
The balance change semantics work mostly as expected, however the sematics around asset creation and destruction warrants further clarification.
When an asset is created, the full asset supply is attributed to the asset creators account.
The balance change for an asset create transaction will be as below:
{
"address": "VIDHG4SYANCP2GUQXXSFSNBPJWS4TAQSI3GH4GYO54FSYPDIBYPMSF7HBY", // The asset creator
"assetId": 2391n, // The created asset id
"amount": 100000n, // Full asset supply of the created asset
"roles": ["AssetCreator"]
}
When an asset is destroyed, the full asset supply must be in the asset creators account and the asset manager must send the destroy transaction. Unfortunately we cannot determine the asset creator or full asset supply from the transaction data. As a result the balance change will always be attributed to the asset manager and will have a 0 amount. If you need to account for the asset supply being destroyed from the creators account, you’ll need to handle this separately.
The balance change for an asset destroy transaction will be as below:
{
"address": "PIDHG4SYANCP2GUQXXSFSNBPJWS4TAQSI3GH4GYO54FSYPDIBYPMSF7HBY", // The asset destroyer, which will always be the asset manager
"assetId": 2391n, // The destroyed asset id
"amount": 0n, // This value will always be 0
"roles": ["AssetDestroyer"]
}
The following code, when algod is pointed to TestNet, will find all transactions emitted by the Data History Museum since the beginning of time in seconds and then find them in real-time as they emerge on the chain.
The watermark is stored in-memory so this particular example is not resilient to restarts. To change that you can implement proper persistence of the watermark. There is an example that uses the file system to demonstrate this.
const algorand = AlgorandClient.fromEnvironment()
let watermark = 0n
const subscriber = new AlgorandSubscriber(
{
events: [
{
eventName: 'dhm-asset',
filter: {
type: TransactionType.acfg,
// Data History Museum creator account on TestNet
sender: 'ER7AMZRPD5KDVFWTUUVOADSOWM4RQKEEV2EDYRVSA757UHXOIEKGMBQIVU',
},
},
],
frequencyInSeconds: 5,
maxRoundsToSync: 100,
syncBehaviour: 'catchup-with-indexer',
watermarkPersistence: {
get: async () => watermark,
set: async (newWatermark) => {
watermark = newWatermark
},
},
},
algorand.client.algod,
algorand.client.indexer,
)
subscriber.onBatch('dhm-asset', async (events) => {
console.log(`Received ${events.length} asset changes`)
// ... do stuff with the events
})
subscriber.onError((e) => {
// eslint-disable-next-line no-console
console.error(e)
})
subscriber.start()
The following code, when algod is pointed to MainNet, will find all transfers of USDC that are greater than $1 and it will poll every 1s for new transfers.
const algorand = AlgorandClient.fromEnvironment()
let watermark = 0n
const subscriber = new AlgorandSubscriber(
{
events: [
{
eventName: 'usdc',
filter: {
type: TransactionType.axfer,
assetId: 31566704n, // MainNet: USDC
minAmount: 1_000_000n, // $1
},
},
],
waitForBlockWhenAtTip: true,
syncBehaviour: 'skip-sync-newest',
watermarkPersistence: {
get: async () => watermark,
set: async (newWatermark) => {
watermark = newWatermark
},
},
},
algorand.client.algod,
)
subscriber.on('usdc', (transfer) => {
// eslint-disable-next-line no-console
console.log(
`${transfer.sender} sent ${transfer.assetTransferTransaction?.receiver} USDC$${Number(
(transfer.assetTransferTransaction?.amount ?? 0n) / 1_000_000n,
).toFixed(2)} in transaction ${transfer.id}`,
)
})
subscriber.onError((e) => {
// eslint-disable-next-line no-console
console.error(e)
})
subscriber.start()
To try examples in this repository:
npm install.env.sample to .env and edit to point to the Algorand node you wantnpm run dhm or F5 in Visual Studio Code to get breakpoint debugging against one of the examples (or choose another)