Tracking Deposits
Apart from sending transactions, an exchange also needs a way to listen for transactions sent to their addresses (deposits). We won't cover how this can be done for ERC20-like smart contracts on Zilliqa in this tutorial, but the same strategy can be applied.
Info
The code in this tutorial is derived from the example application.
Setting Up
To implement a simple and familiar polling mechanism in Node.js, we will use a few additional dependencies:
npm i node-cron p-map lodash
Implementing the Handler Function
We will use a simple class
called DepositCron
to set up our cron job.
We'll start by implementing a handler method, aptly named handler
.
export class DepositCron {
addresses: string[] = [];
frequency: string = "* * * * *";
svc: ZilliqaService;
task: cron.ScheduledTask;
// you should persist the last fetched block to a database, and initialise
// this cron job with that block number, to avoid fetch all blocks from 0 to
// present.
lastFetchedTxBlock: number = 0;
constructor(frequency: string, svc: ZilliqaService, addresses: string[]) {
this.frequency = frequency;
this.svc = svc;
this.addresses = addresses;
}
async handler() {
const currentTxBlock = await this.svc.getTxBlock();
console.log("Current tx block: ", currentTxBlock);
if (currentTxBlock > this.lastFetchedTxBlock) {
// get transactions from lastFetchedTxBlock + 1 to current, and set
// lastFetchedTxBlock to current
const transactions = await pMap(
range(this.lastFetchedTxBlock + 1, currentTxBlock),
(blk) => this.svc.getDeposits(this.addresses, blk),
).then(flatten);
this.lastFetchedTxBlock = currentTxBlock;
// we are only logging to stdout, but in a real application, you would
// be writing the result to the database.
console.log(
`Found ${transactions.length} deposits for ${this.addresses}`,
);
}
}
}
Let's unpack handler
. We are taking several steps:
- Fetch the current
TxBlock
. - We compare the value of the current
TxBlock
against the one we have recorded usinglastFetchedTxBlock
. - If there is a difference, we fetch all transactions that have been
processed between
lastFetchedTxBlock + 1
and the currentTxBlock
- i.e., everyone transaction we have missed.
- We then call
svc.getDeposits
for every transaction processed in that span of blocks. It compares thetoAddr
property of each transaction against theaddresses
array we passed to theconstructor
, checking if it contains ourtoAddr
. If so, then a transaction to an address we are interested in has occurred.
Starting the Cron Job
So far we have no way of starting up or controlling our CronJob
. We'll do that by
implementing start
, stop
, and nuke
methods.
export class DepositCron {
addresses: string[] = [];
frequency: string = "* * * * *";
svc: ZilliqaService;
task: cron.ScheduledTask;
// you should persist the last fetched block to a database, and initialise
// this cron job with that block number, to avoid fetch all blocks from 0 to
// present.
lastFetchedTxBlock: number = 0;
constructor(frequency: string, svc: ZilliqaService, addresses: string[]) {
this.frequency = frequency;
this.svc = svc;
this.addresses = addresses;
this.task = cron.schedule(this.frequency, this.handler.bind(this));
}
async handler() {
const currentTxBlock = await this.svc.getTxBlock();
console.log("Current tx block: ", currentTxBlock);
if (currentTxBlock > this.lastFetchedTxBlock) {
// get transactions from lastFetchedTxBlock + 1 to current, and set
// lastFetchedTxBlock to current
const transactions = await pMap(
range(this.lastFetchedTxBlock + 1, currentTxBlock),
(blk) => this.svc.getDeposits(this.addresses, blk),
).then(flatten);
this.lastFetchedTxBlock = currentTxBlock;
// we are only logging to stdout, but in a real application, you would
// be writing the result to the database.
console.log(
`Found ${transactions.length} deposits for ${this.addresses}`,
);
}
}
async start() {
this.task.start();
}
async stop() {
this.task.stop();
}
async nuke() {
this.task.destroy();
}
}
Now that we have our methods, we can use the cron job like so:
// app.ts
// initialise services
const zilliqaSvc = new services.ZilliqaService(
"https://stress-test-api.aws.z7a.xyz",
{
[config.get("mnemonic")]: 8,
},
);
// boot up cron jobs
// these can also be destroyed
const depositCron = new crons.DepositCron("* * * * *", zilliqaSvc);
depositCron.start();