144 lines
4.7 KiB
TypeScript
144 lines
4.7 KiB
TypeScript
import { EvmBlock, EvmTransaction, ReceiptTransaction } from "../../entities";
|
|
import { EvmBlockRepository } from "../../repositories";
|
|
import { GetEvmOpts } from "./GetEvmLogs";
|
|
import winston from "winston";
|
|
|
|
export class GetEvmTransactions {
|
|
private readonly blockRepo: EvmBlockRepository;
|
|
protected readonly logger: winston.Logger;
|
|
|
|
constructor(blockRepo: EvmBlockRepository) {
|
|
this.logger = winston.child({ module: "GetEvmTransactions" });
|
|
this.blockRepo = blockRepo;
|
|
}
|
|
|
|
async execute(range: Range, opts: GetEvmOpts): Promise<EvmTransaction[]> {
|
|
const fromBlock = range.fromBlock;
|
|
const toBlock = range.toBlock;
|
|
const chain = opts.chain;
|
|
|
|
if (fromBlock > toBlock) {
|
|
this.logger.info(
|
|
`[${chain}][exec] Invalid range [fromBlock: ${fromBlock} - toBlock: ${toBlock}]`
|
|
);
|
|
return [];
|
|
}
|
|
|
|
let populatedTransactions: EvmTransaction[] = [];
|
|
const isTransactionsPresent = true;
|
|
|
|
this.logger.info(
|
|
`[${chain}][exec] Processing blocks [fromBlock: ${fromBlock} - toBlock: ${toBlock}]`
|
|
);
|
|
|
|
let currentBlock = fromBlock;
|
|
const batchSize = 9;
|
|
|
|
while (currentBlock <= toBlock) {
|
|
const batchPromises = [];
|
|
|
|
for (let i = 0; i < batchSize && currentBlock <= toBlock; i++, currentBlock++) {
|
|
// Push each getBlock call as a promise into the batchPromises array
|
|
batchPromises.push(this.blockRepo.getBlock(chain, currentBlock, isTransactionsPresent));
|
|
}
|
|
|
|
const results = await Promise.allSettled(batchPromises);
|
|
|
|
results.forEach(async (result, index) => {
|
|
if (result.status === "fulfilled") {
|
|
const evmBlock = result.value;
|
|
const transactions = evmBlock.transactions ?? [];
|
|
|
|
// Only process transactions to the contract address configured
|
|
const transactionsByAddressConfigured = transactions.filter(
|
|
(transaction) =>
|
|
opts.addresses?.includes(String(transaction.to).toLowerCase()) ||
|
|
opts.addresses?.includes(String(transaction.from).toLowerCase())
|
|
);
|
|
|
|
if (transactionsByAddressConfigured.length > 0) {
|
|
const hashNumbers = new Set(
|
|
transactionsByAddressConfigured.map((transaction) => transaction.hash)
|
|
);
|
|
const receiptTransactions = await this.blockRepo.getTransactionReceipt(
|
|
chain,
|
|
hashNumbers
|
|
);
|
|
|
|
const filterTransactions = this.filterTransactions(
|
|
opts,
|
|
transactionsByAddressConfigured,
|
|
receiptTransactions
|
|
);
|
|
|
|
await this.populateTransaction(
|
|
opts,
|
|
evmBlock,
|
|
receiptTransactions,
|
|
filterTransactions,
|
|
populatedTransactions
|
|
);
|
|
}
|
|
} else if (result.status === "rejected") {
|
|
this.logger.warn(
|
|
`[${chain}][exec] Invalid range [fromBlock: ${fromBlock} - toBlock: ${toBlock}]`
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
this.logger.info(
|
|
`[${chain}][exec] Got ${
|
|
populatedTransactions?.length
|
|
} transactions to process for ${this.populateLog(opts, fromBlock, toBlock)}`
|
|
);
|
|
return populatedTransactions;
|
|
}
|
|
|
|
/**
|
|
* This method filter the transactions in base your logs with the topic and address configured in the job
|
|
* For example: Redeemed or MintAndWithdraw transactions
|
|
*/
|
|
private filterTransactions(
|
|
opts: GetEvmOpts,
|
|
transactionsByAddressConfigured: EvmTransaction[],
|
|
receiptTransactions: Record<string, ReceiptTransaction>
|
|
): EvmTransaction[] {
|
|
return transactionsByAddressConfigured.filter((transaction) => {
|
|
const optsTopics = opts.topics;
|
|
const logs = receiptTransactions[transaction.hash]?.logs || [];
|
|
|
|
return logs.some((log) => {
|
|
return optsTopics?.find((topic) => log.topics?.includes(topic));
|
|
});
|
|
});
|
|
}
|
|
|
|
private async populateTransaction(
|
|
opts: GetEvmOpts,
|
|
evmBlock: EvmBlock,
|
|
receiptTransactions: Record<string, ReceiptTransaction>,
|
|
filterTransactions: EvmTransaction[],
|
|
populatedTransactions: EvmTransaction[]
|
|
) {
|
|
filterTransactions.forEach((transaction) => {
|
|
transaction.status = receiptTransactions[transaction.hash].status;
|
|
transaction.timestamp = evmBlock.timestamp;
|
|
transaction.environment = opts.environment;
|
|
transaction.chainId = opts.chainId;
|
|
transaction.chain = opts.chain;
|
|
transaction.logs = receiptTransactions[transaction.hash].logs;
|
|
populatedTransactions.push(transaction);
|
|
});
|
|
}
|
|
|
|
private populateLog(opts: GetEvmOpts, fromBlock: bigint, toBlock: bigint): string {
|
|
return `[addresses:${opts.addresses}][topics:${opts.topics}][blocks:${fromBlock} - ${toBlock}]`;
|
|
}
|
|
}
|
|
|
|
type Range = {
|
|
fromBlock: bigint;
|
|
toBlock: bigint;
|
|
};
|