From 31e14d950d53c10353c394761441c8ea989ac6f9 Mon Sep 17 00:00:00 2001 From: dylmanning Date: Thu, 15 May 2025 21:45:57 +1000 Subject: [PATCH 1/2] Chunk getContractEvents into 500 block range --- libs/BlockchainConnectionProvider.ts | 225 ++++++++++++++------------- 1 file changed, 115 insertions(+), 110 deletions(-) diff --git a/libs/BlockchainConnectionProvider.ts b/libs/BlockchainConnectionProvider.ts index a20aa68..61c743b 100644 --- a/libs/BlockchainConnectionProvider.ts +++ b/libs/BlockchainConnectionProvider.ts @@ -1,92 +1,81 @@ -import { - Address, - createPublicClient, - Log, - decodeEventLog, - http, - GetContractEventsReturnType, - getContract, -} from "viem"; -import IORedis from "ioredis"; -import { arbitrum, arbitrumSepolia } from "viem/chains"; -import { env } from "../env"; -import abi from "../contracts/FractalityTokenMigration.sol.json"; -import { MigrationRegisteredEvent } from "../interfaces"; -import { MySqlBigInt64BuilderInitial } from "drizzle-orm/mysql-core"; -import { BlockchainConnectionError } from "../errors"; +import { Address, createPublicClient, decodeEventLog, http, getContract } from 'viem' +import { arbitrum, arbitrumSepolia } from 'viem/chains' +import { env } from '../env' +import abi from '../contracts/FractalityTokenMigration.sol.json' +import { MigrationRegisteredEvent } from '../interfaces' +import { BlockchainConnectionError } from '../errors' interface BlockchainConnectionProviderOptions { - providerUrl: string; - y2kTokenMigrationAddress: Address; - frctRTokenMigrationAddress: Address; + providerUrl: string + y2kTokenMigrationAddress: Address + frctRTokenMigrationAddress: Address } const ERC20Abi = [ { - name: "decimals", - type: "function", + name: 'decimals', + type: 'function', inputs: [], - outputs: [{ name: "", type: "uint8" }], - stateMutability: "view", + outputs: [{ name: '', type: 'uint8' }], + stateMutability: 'view' }, { - name: "balanceOf", - type: "function", - inputs: [{ name: "account", type: "address" }], - outputs: [{ name: "", type: "uint256" }], - stateMutability: "view", + name: 'balanceOf', + type: 'function', + inputs: [{ name: 'account', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' }, { - name: "totalSupply", - type: "function", + name: 'totalSupply', + type: 'function', inputs: [], - outputs: [{ name: "", type: "uint256" }], - stateMutability: "view", + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' }, { - name: "name", - type: "function", + name: 'name', + type: 'function', inputs: [], - outputs: [{ name: "", type: "string" }], - stateMutability: "view", + outputs: [{ name: '', type: 'string' }], + stateMutability: 'view' }, { - name: "symbol", - type: "function", + name: 'symbol', + type: 'function', inputs: [], - outputs: [{ name: "", type: "string" }], - stateMutability: "view", - }, -]; + outputs: [{ name: '', type: 'string' }], + stateMutability: 'view' + } +] export class BlockchainConnectionProvider { - public lastAssetProcessedBlock: bigint = BigInt(0); - public lastShareProcessedBlock: bigint = BigInt(0); - public y2kTokenMigrationAddress: Address; - public frctRTokenMigrationAddress: Address; + public lastAssetProcessedBlock: bigint = BigInt(0) + public lastShareProcessedBlock: bigint = BigInt(0) + public y2kTokenMigrationAddress: Address + public frctRTokenMigrationAddress: Address - public _viemClient: ReturnType; + public _viemClient: ReturnType constructor(opts: BlockchainConnectionProviderOptions) { - this.y2kTokenMigrationAddress = opts.y2kTokenMigrationAddress; - this.frctRTokenMigrationAddress = opts.frctRTokenMigrationAddress; - this._viemClient = this._init(opts); - console.log("Connected to blockchain", this._viemClient.chain!.name); + this.y2kTokenMigrationAddress = opts.y2kTokenMigrationAddress + this.frctRTokenMigrationAddress = opts.frctRTokenMigrationAddress + this._viemClient = this._init(opts) + console.log('Connected to blockchain', this._viemClient.chain!.name) } private _init = (opts: BlockchainConnectionProviderOptions) => { return createPublicClient({ - chain: env.BLOCKCHAIN_ENVIRONMENT === "test" ? arbitrumSepolia : arbitrum, - transport: http(opts.providerUrl), - }); - }; + chain: env.BLOCKCHAIN_ENVIRONMENT === 'test' ? arbitrumSepolia : arbitrum, + transport: http(opts.providerUrl) + }) + } public async getCurrentBlockNumber(): Promise { try { - return this._viemClient.getBlockNumber(); + return this._viemClient.getBlockNumber() } catch (error) { - throw new BlockchainConnectionError("Error getting current block number: " + error); + throw new BlockchainConnectionError('Error getting current block number: ' + error) } - } public async getArbitrumTokenDecimals(): Promise { @@ -94,82 +83,98 @@ export class BlockchainConnectionProvider { const y2kMigrationContract = getContract({ address: this.y2kTokenMigrationAddress, abi: abi.abi, - client: { public: this._viemClient }, - }); + client: { public: this._viemClient } + }) const frctRMigrationContract = getContract({ address: this.frctRTokenMigrationAddress, abi: abi.abi, - client: { public: this._viemClient }, - }); - const y2kToken = await y2kMigrationContract.read.token(); - const frctRToken = await frctRMigrationContract.read.token(); + client: { public: this._viemClient } + }) + const y2kToken = await y2kMigrationContract.read.token() + const frctRToken = await frctRMigrationContract.read.token() const y2kErc20 = getContract({ address: y2kToken as Address, abi: ERC20Abi, - client: { public: this._viemClient }, - }); + client: { public: this._viemClient } + }) const frctRErc20 = getContract({ address: frctRToken as Address, abi: ERC20Abi, - client: { public: this._viemClient }, - }); + client: { public: this._viemClient } + }) - const y2kDecimals = (await y2kErc20.read.decimals()) as bigint; - const frctRDecimals = (await frctRErc20.read.decimals()) as bigint; + const y2kDecimals = (await y2kErc20.read.decimals()) as bigint + const frctRDecimals = (await frctRErc20.read.decimals()) as bigint if (y2kDecimals !== frctRDecimals) { - throw new Error("Fatal error:Y2K and FRCT-R decimals do not match"); + throw new Error('Fatal error:Y2K and FRCT-R decimals do not match') } - return y2kDecimals; + return y2kDecimals } - //TODO: make sure that we run this in a way that it doesn't exceed alchemy's 2000 block limit public async scanMigrations( migrationAddress: Address, fromBlock: bigint, toBlock: bigint ): Promise { -try{ - //This is the current block number - if (fromBlock >= toBlock) { - throw new Error("from block must be before toBlock"); + try { + if (fromBlock >= toBlock) { + throw new Error('from block must be before toBlock') + } + + console.log(`Getting blocks from ${fromBlock} to ${toBlock}`) + + // NOTE: Alchemy block limit + const CHUNK_SIZE = BigInt(500) + + const decodedLogs: MigrationRegisteredEvent[] = [] + + let currentFromBlock = fromBlock + + while (currentFromBlock <= toBlock) { + const chunkToBlock = + currentFromBlock + CHUNK_SIZE > toBlock + ? toBlock + : currentFromBlock + CHUNK_SIZE - BigInt(1) + + console.log(`Fetching chunk from ${currentFromBlock} to ${chunkToBlock}`) + + const logs = await this._viemClient.getContractEvents({ + address: migrationAddress, + abi: abi.abi, + eventName: 'MigrationRegistered', + fromBlock: currentFromBlock, + toBlock: chunkToBlock + }) + + logs.forEach((log) => { + const decodedLog = decodeEventLog({ + abi: abi.abi, + data: log.data, + topics: log.topics, + eventName: 'MigrationRegistered' + }) + + decodedLogs.push({ + migrationContractAddress: migrationAddress, + eventName: decodedLog.eventName, + transactionHash: log.transactionHash, + caller: (decodedLog.args as any).caller as Address, + migrationAddress: (decodedLog.args as any).migrationAddress as Address, + amount: BigInt((decodedLog.args as any).amount as string) + }) + }) + + currentFromBlock = chunkToBlock + BigInt(1) + } + + return decodedLogs + } catch (error) { + throw new BlockchainConnectionError('Error scanning migrations: ' + error) } - - console.log(`Getting blocks from ${fromBlock} to ${toBlock}`); - const logs = await this._viemClient.getContractEvents({ - address: migrationAddress, - abi: abi.abi, - eventName: "MigrationRegistered", - fromBlock, - toBlock, - }); - - let decodedLogs: MigrationRegisteredEvent[] = []; - - logs.forEach((log) => { - const decodedLog = decodeEventLog({ - abi: abi.abi, - data: log.data, - topics: log.topics, - eventName: "MigrationRegistered", - }); - - decodedLogs.push({ - migrationContractAddress: migrationAddress, - eventName: decodedLog.eventName, - transactionHash: log.transactionHash, - caller: (decodedLog.args as any).caller as Address, - migrationAddress: (decodedLog.args as any).migrationAddress as Address, - amount: BigInt((decodedLog.args as any).amount as string), - }); - }); - return decodedLogs; - } catch (error) { - throw new BlockchainConnectionError("Error scanning migrations: " + error); - } } } From 8b8201aae6e0cd5ebfbe5372dd1113fdb7bc73c3 Mon Sep 17 00:00:00 2001 From: Jose Herrera Date: Thu, 15 May 2025 19:21:54 -0400 Subject: [PATCH 2/2] added more accurate error type --- errors.ts | 7 +++++++ libs/BlockchainConnectionProvider.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/errors.ts b/errors.ts index 7207fa4..33984b3 100644 --- a/errors.ts +++ b/errors.ts @@ -16,6 +16,13 @@ export class RedisError extends Error { } } + export class AlchemyScanError extends Error { + constructor(message: string) { + super(message); + this.name = "AlchemyScanError"; + } + } + export class DecimalConversionError extends Error { constructor(message: string) { super(message); diff --git a/libs/BlockchainConnectionProvider.ts b/libs/BlockchainConnectionProvider.ts index 61c743b..27217c3 100644 --- a/libs/BlockchainConnectionProvider.ts +++ b/libs/BlockchainConnectionProvider.ts @@ -3,7 +3,7 @@ import { arbitrum, arbitrumSepolia } from 'viem/chains' import { env } from '../env' import abi from '../contracts/FractalityTokenMigration.sol.json' import { MigrationRegisteredEvent } from '../interfaces' -import { BlockchainConnectionError } from '../errors' +import { AlchemyScanError, BlockchainConnectionError } from '../errors' interface BlockchainConnectionProviderOptions { providerUrl: string y2kTokenMigrationAddress: Address @@ -174,7 +174,7 @@ export class BlockchainConnectionProvider { return decodedLogs } catch (error) { - throw new BlockchainConnectionError('Error scanning migrations: ' + error) + throw new AlchemyScanError(`Error scanning migrations in block range ${fromBlock} to ${toBlock}: ${error}`) } } }