tl;dr
When the alert logic requires to query something from the previous block, it's better to use the block.parentHash
instead of blockNumber - 1
to avoid issues with the Ethereum chain reorganizations.
How to fix
See the following reference code:
https://github.com/lidofinance/alerting-forta/blob/main/ethereum-steth/src/clients/eth_provider.ts#L181-L196
public async getBalanceByBlockHash(address: string, blockHash: string): Promise<E.Either<Error, BigNumber>> {
try {
const out = await retryAsync<EtherBigNumber>(
async (): Promise<EtherBigNumber> => {
return await this.jsonRpcProvider.getBalance(address, {
blockHash: blockHash,
} as never)
},
{ delay: DELAY_IN_500MS, maxTry: ATTEMPTS_5 },
)
return E.right(new BigNumber(String(out)))
} catch (e) {
return E.left(new NetworkError(e, `Could not fetch balance by address ${address} and blockHash ${blockHash}`))
}
}
https://github.com/lidofinance/alerting-forta/blob/main/ethereum-steth/src/services/vault/Vault.srv.ts#L60-L63
const prevBlockWithdrawalVaultBalance = await this.ethProvider.getBalanceByBlockHash(
this.withdrawalsVaultAddress,
blockEvent.block.parentHash,
)
Affected scope
List of the files that affected by the issue:
grep --include=\*.ts -rnw '.' -e ".*umber - 1" --exclude-dir=node_modules
./ethereum-validators-set/src/subagents/lido-report/agent-lido-report.ts:841: blockTag: txEvent.blockNumber - 1,
./ethereum-validators-set/src/subagents/exitbus-oracle/agent-exitbus-oracle.ts:283: blockEvent.blockNumber - 1,
./ethereum-validators-set/src/subagents/accounting-oracle/agent-accounting-oracle.ts:140: blockEvent.blockNumber - 1,
./ethereum-validators-set/src/subagents/accounting-oracle/agent-accounting-oracle.ts:148: blockEvent.blockNumber - 1,
./l2-bridge-linea/src/clients/linea_block_client.ts:72: const blocks = await this.provider.fetchBlocks(this.cachedBlockDto.number, latestBlock.right.number - 1)
./l2-bridge-mantle/src/clients/mantle_block_client.ts:72: const blocks = await this.mantleProvider.fetchBlocks(this.cachedBlockDto.number, latestBlock.right.number - 1)
./voting-watcher/src/agent-voting-watcher.ts:156: const prevBlock = await ethersProvider.getBlock(blockEvent.blockNumber - 1);
./ethereum-steth/src/services/steth_operation/StethOperation.srv.ts:230: this.ethProvider.getBufferedEther(shiftedBlockNumber - 1),
./l2-bridge-base/src/services/monitor_withdrawals.ts:54: const withdrawalEvents = await this.withdrawalsClient.getWithdrawalEvents(pastl2Block, l2BlockNumber - 1)
./l2-bridge-base/src/clients/base_block_client.ts:72: const blocks = await this.provider.fetchL2Blocks(this.cachedBlockDto.number, latestBlock.right.number - 1)
./ethereum-financial/src/services/aave/Aave.srv.ts:63: this.ethProvider.getStethBalance(this.aaveAstethAddress, blockEvent.number - 1),
./ethereum-financial/src/services/aave/Aave.srv.ts:64: await this.ethProvider.getTotalSupply(blockEvent.number - 1),
./ethereum-financial/src/services/aave/Aave.srv.ts:101: `${blockEvent.number - 1}`,
./l2-bridge-zksync/src/clients/zksync_block_client.ts:72: const blocks = await this.provider.fetchBlocks(this.cachedBlockDto.number, latestBlock.right.number - 1)
Explanations and references
References
Helpful picture