From 4b1d83dbceb691ae0fbce18a5b809d73fc873dc2 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 25 Aug 2025 11:02:29 +0530 Subject: [PATCH 01/11] chore(validator): update consts --- validator-cli/src/consts/bridgeRoutes.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/validator-cli/src/consts/bridgeRoutes.ts b/validator-cli/src/consts/bridgeRoutes.ts index 6b86a445..896be4ba 100644 --- a/validator-cli/src/consts/bridgeRoutes.ts +++ b/validator-cli/src/consts/bridgeRoutes.ts @@ -30,7 +30,7 @@ type RouteConfigs = { deposit: bigint; }; -export enum Network { +enum Network { DEVNET = "devnet", TESTNET = "testnet", } @@ -39,7 +39,7 @@ const arbToEthConfigs: { [key in Network]: RouteConfigs } = { [Network.DEVNET]: { veaInbox: veaInboxArbToEthDevnet, veaOutbox: veaOutboxArbToEthDevnet, - epochPeriod: 1800, + epochPeriod: 300, deposit: BigInt("1000000000000000000"), }, [Network.TESTNET]: { @@ -54,7 +54,7 @@ const arbToGnosisConfigs: { [key in Network]: RouteConfigs } = { [Network.DEVNET]: { veaInbox: veaInboxArbToGnosisDevnet, veaOutbox: veaOutboxArbToGnosisDevnet, - epochPeriod: 1800, + epochPeriod: 300, deposit: BigInt("100000000000000000"), }, [Network.TESTNET]: { @@ -66,7 +66,7 @@ const arbToGnosisConfigs: { [key in Network]: RouteConfigs } = { }, }; -export const bridges: { [chainId: number]: Bridge } = { +const bridges: { [chainId: number]: Bridge } = { 11155111: { chain: "sepolia", minChallengePeriod: 10800, @@ -87,10 +87,16 @@ export const bridges: { [chainId: number]: Bridge } = { }, }; +// For the remaining time in an epoch the bot should save snapshots +const snapshotSavingPeriod = { + [Network.DEVNET]: 90, // 1m 30s + [Network.TESTNET]: 600, // 10 mins +}; + const getBridgeConfig = (chainId: number): Bridge => { const bridge = bridges[chainId]; if (!bridge) throw new Error(`Bridge not found for chain`); return bridges[chainId]; }; -export { getBridgeConfig, Bridge }; +export { bridges, getBridgeConfig, Bridge, Network, snapshotSavingPeriod }; From 3440f8c407d3e6ca1b97979a84ee933e5cb79e4a Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 25 Aug 2025 11:03:39 +0530 Subject: [PATCH 02/11] feat(validator): devnet save period --- validator-cli/src/helpers/snapshot.test.ts | 6 ++++-- validator-cli/src/helpers/snapshot.ts | 18 ++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/validator-cli/src/helpers/snapshot.test.ts b/validator-cli/src/helpers/snapshot.test.ts index a78f52a8..fbaacd27 100644 --- a/validator-cli/src/helpers/snapshot.test.ts +++ b/validator-cli/src/helpers/snapshot.test.ts @@ -1,6 +1,7 @@ import { Network } from "../consts/bridgeRoutes"; import { isSnapshotNeeded, saveSnapshot } from "./snapshot"; import { MockEmitter } from "../utils/emitter"; +import { snapshotSavingPeriod } from "../consts/bridgeRoutes"; describe("snapshot", () => { let veaInbox: any; @@ -184,7 +185,8 @@ describe("snapshot", () => { expect(res).toEqual({ transactionHandler, latestCount: currentCount }); }); - it("should save snapshot if snapshot is needed at anytime for devnet", async () => { + it("should save snapshot in time limit for devnet", async () => { + const savingPeriod = snapshotSavingPeriod[Network.DEVNET]; const currentCount = 6; count = -1; veaInbox.count.mockResolvedValue(currentCount); @@ -192,7 +194,7 @@ describe("snapshot", () => { snapshotNeeded: true, latestCount: currentCount, }); - const now = 1801; // 600 seconds after the epoch started + const now = epochPeriod + epochPeriod - savingPeriod; // 60 seconds before the second epoch ends const transactionHandler = { saveSnapshot: jest.fn(), }; diff --git a/validator-cli/src/helpers/snapshot.ts b/validator-cli/src/helpers/snapshot.ts index a37172b9..8477678e 100644 --- a/validator-cli/src/helpers/snapshot.ts +++ b/validator-cli/src/helpers/snapshot.ts @@ -2,7 +2,7 @@ import { Network } from "../consts/bridgeRoutes"; import { getLastMessageSaved } from "../utils/graphQueries"; import { BotEvents } from "../utils/botEvents"; import { defaultEmitter } from "../utils/emitter"; - +import { snapshotSavingPeriod } from "../consts/bridgeRoutes"; interface SnapshotCheckParams { chainId: number; veaInbox: any; @@ -33,14 +33,12 @@ export const saveSnapshot = async ({ toSaveSnapshot = isSnapshotNeeded, now = Math.floor(Date.now() / 1000), }: SaveSnapshotParams): Promise => { - if (network != Network.DEVNET) { - const timeElapsed = now % epochPeriod; - const timeLeftForEpoch = epochPeriod - timeElapsed; - // Saving snapshots in last 10 minutes of the epoch on testnet - if (timeLeftForEpoch > 600) { - emitter.emit(BotEvents.SNAPSHOT_WAITING, timeLeftForEpoch); - return { transactionHandler, latestCount: count }; - } + const timeElapsed = now % epochPeriod; + const timeLeftForEpoch = epochPeriod - timeElapsed; + + if (timeLeftForEpoch > snapshotSavingPeriod[network]) { + emitter.emit(BotEvents.SNAPSHOT_WAITING, timeLeftForEpoch); + return { transactionHandler, latestCount: count }; } const { snapshotNeeded, latestCount } = await toSaveSnapshot({ chainId, @@ -71,7 +69,7 @@ export const isSnapshotNeeded = async ({ const lastSavedMessageId = await fetchLastSavedMessage(veaInboxAddress, chainId); const messageIndex = extractMessageIndex(lastSavedMessageId); // adding 1 to the message index to get the last saved count - lastSavedCount = messageIndex + 1; + lastSavedCount = messageIndex; } if (currentCount > lastSavedCount) { return { snapshotNeeded: true, latestCount: currentCount }; From 4adf134062ec617d47b07668fab4f6f44557754b Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 25 Aug 2025 11:09:22 +0530 Subject: [PATCH 03/11] fix(validator): txn state condtn --- .../src/utils/transactionHandlers/arbToEthHandler.ts | 10 +++++----- .../utils/transactionHandlers/arbToGnosisHandler.ts | 10 +++++----- .../transactionHandlers/baseTransactionHandler.ts | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts b/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts index 7a74bdcd..677f5bd6 100644 --- a/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts +++ b/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts @@ -26,7 +26,7 @@ export class ArbToEthTransactionHandler extends BaseTransactionHandler implements ITransact const now = Date.now(); const status = await this.checkTransactionStatus(this.transactions.startVerificationTxn, ContractType.OUTBOX, now); - if (status !== TransactionStatus.NOT_MADE && status !== TransactionStatus.EXPIRED) return; + if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; const cfg = getBridgeConfig(this.chainId); const timeOver = @@ -223,7 +223,7 @@ export abstract class BaseTransactionHandler implements ITransact const now = Date.now(); const status = await this.checkTransactionStatus(this.transactions.verifySnapshotTxn, ContractType.OUTBOX, now); - if (status !== TransactionStatus.NOT_MADE && status !== TransactionStatus.EXPIRED) return; + if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; const cfg = getBridgeConfig(this.chainId); const timeLeft = currentTimestamp - Number(this.claim.timestampVerification) - cfg.minChallengePeriod; @@ -251,7 +251,7 @@ export abstract class BaseTransactionHandler implements ITransact ContractType.OUTBOX, now ); - if (status !== TransactionStatus.NOT_MADE && status !== TransactionStatus.EXPIRED) return; + if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; const tx = await (this.veaOutbox as any).withdrawClaimDeposit(this.epoch, this.claim); this.emitter.emit(BotEvents.TXN_MADE, tx.hash, this.epoch, "Withdraw Claim Deposit"); @@ -271,7 +271,7 @@ export abstract class BaseTransactionHandler implements ITransact ContractType.OUTBOX, now ); - if (status !== TransactionStatus.NOT_MADE && status !== TransactionStatus.EXPIRED) return; + if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; const tx = await (this.veaOutbox as any).withdrawChallengeDeposit(this.epoch, this.claim); this.emitter.emit(BotEvents.TXN_MADE, tx.hash, this.epoch, "Withdraw Challenge Deposit"); @@ -286,7 +286,7 @@ export abstract class BaseTransactionHandler implements ITransact const now = Date.now(); const status = await this.checkTransactionStatus(this.transactions.saveSnapshotTxn, ContractType.INBOX, now); - if (status !== TransactionStatus.NOT_MADE && status !== TransactionStatus.EXPIRED) return; + if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; const tx = await (this.veaInbox as any).saveSnapshot(); this.emitter.emit(BotEvents.TXN_MADE, tx.hash, this.epoch, "Save Snapshot"); From cb0b6e608a97a99c8ee195ae1bf9cd18a5915e34 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 27 Aug 2025 12:16:12 +0530 Subject: [PATCH 04/11] chore: refactor txn check --- .../transactionHandlers/arbToEthHandler.ts | 31 +++++++------------ .../transactionHandlers/arbToGnosisHandler.ts | 20 ++++++------ .../baseTransactionHandler.ts | 30 ++++++++++++------ 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts b/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts index 677f5bd6..09b25405 100644 --- a/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts +++ b/validator-cli/src/utils/transactionHandlers/arbToEthHandler.ts @@ -25,10 +25,9 @@ export class ArbToEthTransactionHandler extends BaseTransactionHandler { this.emitter.emit(BotEvents.CLAIMING, this.epoch); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.claimTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) { - return; - } + + const toSubmit = await this.toSubmitTransaction(this.transactions.claimTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; const { routeConfig } = getBridgeConfig(this.chainId); const { deposit } = routeConfig[this.network]; @@ -51,10 +50,8 @@ export class ArbToEthTransactionHandler extends BaseTransactionHandler { this.emitter.emit(BotEvents.EXECUTING_SNAPSHOT, this.epoch); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.executeSnapshotTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) { - return; - } + const toSubmit = await this.toSubmitTransaction(this.transactions.executeSnapshotTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; const result = await execFn(sendSnapshotHash, this.veaInboxProvider, this.veaOutboxProvider); this.emitter.emit(BotEvents.TXN_MADE, result.hash, this.epoch, "Execute Snapshot"); @@ -142,10 +135,8 @@ export class ArbToEthDevnetTransactionHandler extends ArbToEthTransactionHandler public async devnetAdvanceState(stateRoot: string): Promise { this.emitter.emit(BotEvents.ADV_DEVNET, this.epoch); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.devnetAdvanceStateTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) { - return; - } + const toSubmit = await this.toSubmitTransaction(this.transactions.devnetAdvanceStateTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; const { routeConfig } = getBridgeConfig(this.chainId); const { deposit } = routeConfig[Network.DEVNET]; const tx = await this.veaOutboxDevnet.devnetAdvanceState(this.epoch, stateRoot, { diff --git a/validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts b/validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts index e96ce961..556c2339 100644 --- a/validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts +++ b/validator-cli/src/utils/transactionHandlers/arbToGnosisHandler.ts @@ -41,8 +41,8 @@ export class ArbToGnosisTransactionHandler extends BaseTransactionHandler { this.emitter.emit(BotEvents.CLAIMING, this.epoch); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.claimTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + const toSubmit = await this.toSubmitTransaction(this.transactions.claimTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; // Approves WETH for the claim if not already approved await this.approveWeth(); @@ -57,8 +57,8 @@ export class ArbToGnosisTransactionHandler extends BaseTransactionHandler { this.emitter.emit(BotEvents.ADV_DEVNET, this.epoch); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.devnetAdvanceStateTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + const toSubmit = await this.toSubmitTransaction(this.transactions.devnetAdvanceStateTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; await this.approveWeth(); const { routeConfig } = getBridgeConfig(this.chainId); const { deposit } = routeConfig[Network.DEVNET]; diff --git a/validator-cli/src/utils/transactionHandlers/baseTransactionHandler.ts b/validator-cli/src/utils/transactionHandlers/baseTransactionHandler.ts index 1ff0b990..643d0092 100644 --- a/validator-cli/src/utils/transactionHandlers/baseTransactionHandler.ts +++ b/validator-cli/src/utils/transactionHandlers/baseTransactionHandler.ts @@ -189,13 +189,23 @@ export abstract class BaseTransactionHandler implements ITransact return TransactionStatus.NOT_FINAL; } + public async toSubmitTransaction( + trnx: Transaction | null, + contract: ContractType, + currentTime: number + ): Promise { + const status = await this.checkTransactionStatus(trnx, contract, currentTime); + if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return false; + return true; + } + public async startVerification(currentTimestamp: number) { this.emitter.emit(BotEvents.STARTING_VERIFICATION, this.epoch); if (!this.claim) throw new ClaimNotSetError(); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.startVerificationTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + const toSubmit = await this.toSubmitTransaction(this.transactions.startVerificationTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; const cfg = getBridgeConfig(this.chainId); const timeOver = @@ -222,8 +232,8 @@ export abstract class BaseTransactionHandler implements ITransact if (!this.claim) throw new ClaimNotSetError(); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.verifySnapshotTxn, ContractType.OUTBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + const toSubmit = await this.toSubmitTransaction(this.transactions.verifySnapshotTxn, ContractType.OUTBOX, now); + if (!toSubmit) return; const cfg = getBridgeConfig(this.chainId); const timeLeft = currentTimestamp - Number(this.claim.timestampVerification) - cfg.minChallengePeriod; @@ -246,12 +256,12 @@ export abstract class BaseTransactionHandler implements ITransact if (!this.claim) throw new ClaimNotSetError(); const now = Date.now(); - const status = await this.checkTransactionStatus( + const toSubmit = await this.toSubmitTransaction( this.transactions.withdrawClaimDepositTxn, ContractType.OUTBOX, now ); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + if (!toSubmit) return; const tx = await (this.veaOutbox as any).withdrawClaimDeposit(this.epoch, this.claim); this.emitter.emit(BotEvents.TXN_MADE, tx.hash, this.epoch, "Withdraw Claim Deposit"); @@ -266,12 +276,12 @@ export abstract class BaseTransactionHandler implements ITransact if (!this.claim) throw new ClaimNotSetError(); const now = Date.now(); - const status = await this.checkTransactionStatus( + const toSubmit = await this.toSubmitTransaction( this.transactions.withdrawChallengeDepositTxn, ContractType.OUTBOX, now ); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + if (!toSubmit) return; const tx = await (this.veaOutbox as any).withdrawChallengeDeposit(this.epoch, this.claim); this.emitter.emit(BotEvents.TXN_MADE, tx.hash, this.epoch, "Withdraw Challenge Deposit"); @@ -285,8 +295,8 @@ export abstract class BaseTransactionHandler implements ITransact this.emitter.emit(BotEvents.SAVING_SNAPSHOT, this.epoch); const now = Date.now(); - const status = await this.checkTransactionStatus(this.transactions.saveSnapshotTxn, ContractType.INBOX, now); - if (status === TransactionStatus.PENDING || status === TransactionStatus.NOT_FINAL) return; + const toSubmit = await this.toSubmitTransaction(this.transactions.saveSnapshotTxn, ContractType.INBOX, now); + if (!toSubmit) return; const tx = await (this.veaInbox as any).saveSnapshot(); this.emitter.emit(BotEvents.TXN_MADE, tx.hash, this.epoch, "Save Snapshot"); From deeb431233c8f5492b7e055971b32dd0586ff920 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 27 Aug 2025 12:16:48 +0530 Subject: [PATCH 05/11] fix: merge imports, export type --- validator-cli/src/consts/bridgeRoutes.ts | 4 ++-- validator-cli/src/helpers/snapshot.test.ts | 3 +-- validator-cli/src/helpers/snapshot.ts | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/validator-cli/src/consts/bridgeRoutes.ts b/validator-cli/src/consts/bridgeRoutes.ts index 896be4ba..c1bb7ede 100644 --- a/validator-cli/src/consts/bridgeRoutes.ts +++ b/validator-cli/src/consts/bridgeRoutes.ts @@ -11,7 +11,7 @@ import veaOutboxArbToGnosisDevnet from "@kleros/vea-contracts/deployments/chiado import veaInboxArbToGnosisTestnet from "@kleros/vea-contracts/deployments/arbitrumSepolia/VeaInboxArbToGnosisTestnet.json"; import veaOutboxArbToGnosisTestnet from "@kleros/vea-contracts/deployments/chiado/VeaOutboxArbToGnosisTestnet.json"; import veaRouterArbToGnosisTestnet from "@kleros/vea-contracts/deployments/sepolia/RouterArbToGnosisTestnet.json"; -interface Bridge { +export interface Bridge { chain: string; minChallengePeriod: number; sequencerDelayLimit: number; @@ -99,4 +99,4 @@ const getBridgeConfig = (chainId: number): Bridge => { return bridges[chainId]; }; -export { bridges, getBridgeConfig, Bridge, Network, snapshotSavingPeriod }; +export { bridges, getBridgeConfig, Network, snapshotSavingPeriod }; diff --git a/validator-cli/src/helpers/snapshot.test.ts b/validator-cli/src/helpers/snapshot.test.ts index fbaacd27..5013f742 100644 --- a/validator-cli/src/helpers/snapshot.test.ts +++ b/validator-cli/src/helpers/snapshot.test.ts @@ -1,7 +1,6 @@ -import { Network } from "../consts/bridgeRoutes"; +import { Network, snapshotSavingPeriod } from "../consts/bridgeRoutes"; import { isSnapshotNeeded, saveSnapshot } from "./snapshot"; import { MockEmitter } from "../utils/emitter"; -import { snapshotSavingPeriod } from "../consts/bridgeRoutes"; describe("snapshot", () => { let veaInbox: any; diff --git a/validator-cli/src/helpers/snapshot.ts b/validator-cli/src/helpers/snapshot.ts index 8477678e..91427d65 100644 --- a/validator-cli/src/helpers/snapshot.ts +++ b/validator-cli/src/helpers/snapshot.ts @@ -1,8 +1,7 @@ -import { Network } from "../consts/bridgeRoutes"; +import { Network, snapshotSavingPeriod } from "../consts/bridgeRoutes"; import { getLastMessageSaved } from "../utils/graphQueries"; import { BotEvents } from "../utils/botEvents"; import { defaultEmitter } from "../utils/emitter"; -import { snapshotSavingPeriod } from "../consts/bridgeRoutes"; interface SnapshotCheckParams { chainId: number; veaInbox: any; From 6ddc49f6968f164de9aed7789542e1dce8c12cf0 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Wed, 10 Sep 2025 11:47:13 +0530 Subject: [PATCH 06/11] chore: udpate validator readme --- validator-cli/README.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/validator-cli/README.md b/validator-cli/README.md index 3c9fe8cf..b9aaf670 100644 --- a/validator-cli/README.md +++ b/validator-cli/README.md @@ -1,4 +1,4 @@ -# bots +# Validator bot A collection of bots for the Vea challenger and bridger ecosystem. @@ -8,4 +8,29 @@ A collection of bots for the Vea challenger and bridger ecosystem. `pm2 start` -Runs watcher every minute, and challenges any false claims on the fast bridge receiver. +By default, the watcher performs two core functions: + +- Bridger: Submits stored snapshots to the fast bridge receiver. +- Challenger: Challenges any detected invalid claims. + +# flags + +`--saveSnapshot` + +Enables snapshot saving on the inbox when the bot observes a valid state. + +`--path=challenger | bridger | both` + +- challenger: Only challenge invalid claims +- bridger: Only submit snapshots +- both: Default mode, acts as both challenger and bridger + +# Example usage + +Run as both challenger and bridger with snapshots enabled: + +`pm2 start -- --saveSnapshot` + +Run only as challenger: + +`pm2 start dist/watcher.js -- --path=challenger` From 9e9db78038832278692723a1965efefb5b5f5acc Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 6 Oct 2025 13:58:16 +0530 Subject: [PATCH 07/11] fix: handle claim resolution --- validator-cli/src/helpers/validator.ts | 10 ++++- validator-cli/src/utils/botEvents.ts | 1 + validator-cli/src/utils/claim.ts | 60 +++++++++++++++++++++---- validator-cli/src/utils/graphQueries.ts | 2 +- validator-cli/src/utils/logger.ts | 3 ++ 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/validator-cli/src/helpers/validator.ts b/validator-cli/src/helpers/validator.ts index 1c96b3da..74d96a81 100644 --- a/validator-cli/src/helpers/validator.ts +++ b/validator-cli/src/helpers/validator.ts @@ -74,6 +74,11 @@ export async function challengeAndResolveClaim({ } else { transactionHandler.claim = claim; } + // If claim is already resolved, nothing to do + if (claim.honest !== 0) { + emitter.emit(BotEvents.CLAIM_ALREADY_RESOLVED, epoch); + return null; + } const { challenged, toRelay } = await challengeAndCheckRelay({ veaInbox, @@ -94,6 +99,7 @@ export async function challengeAndResolveClaim({ claim, veaInbox, veaInboxProvider, + veaOutbox, queryRpc, ethBlockTag, transactionHandler, @@ -141,6 +147,7 @@ interface ResolveFlowParams { claim: ClaimStruct; veaInbox: any; veaInboxProvider: JsonRpcProvider; + veaOutbox: any; queryRpc: JsonRpcProvider; ethBlockTag: "latest" | "finalized"; transactionHandler: ITransactionHandler; @@ -154,6 +161,7 @@ async function handleResolveFlow({ claim, veaInbox, veaInboxProvider, + veaOutbox, queryRpc, ethBlockTag, transactionHandler, @@ -165,6 +173,7 @@ async function handleResolveFlow({ chainId, veaInbox, veaInboxProvider, + veaOutbox, veaOutboxProvider: queryRpc, epoch, fromBlock: blockNumberOutboxLowerBound, @@ -175,7 +184,6 @@ async function handleResolveFlow({ await transactionHandler.sendSnapshot(); return; } - const execStatus = claimResolveState.execution.status; if (execStatus === 1) { await transactionHandler.resolveChallengedClaim(claimResolveState.sendSnapshot.txHash); diff --git a/validator-cli/src/utils/botEvents.ts b/validator-cli/src/utils/botEvents.ts index 850c4cf9..5f82cdcb 100644 --- a/validator-cli/src/utils/botEvents.ts +++ b/validator-cli/src/utils/botEvents.ts @@ -32,6 +32,7 @@ export enum BotEvents { WITHDRAWING_CHALLENGE_DEPOSIT = "withdrawing_challenge_deposit", WITHDRAWING_CLAIM_DEPOSIT = "withdrawing_claim_deposit", WAITING_ARB_TIMEOUT = "waiting_arb_timeout", + CLAIM_ALREADY_RESOLVED = "claim_already_resolved", // Devnet state ADV_DEVNET = "advance_devnet", diff --git a/validator-cli/src/utils/claim.ts b/validator-cli/src/utils/claim.ts index 9263e121..32d62337 100644 --- a/validator-cli/src/utils/claim.ts +++ b/validator-cli/src/utils/claim.ts @@ -1,6 +1,7 @@ import { ClaimStruct } from "@kleros/vea-contracts/typechain-types/arbitrumToEth/VeaInboxArbToEth"; +import { VeaInboxArbToEth__factory } from "@kleros/vea-contracts/typechain-types"; import { JsonRpcProvider } from "@ethersproject/providers"; -import { ethers } from "ethers"; +import { ethers, Interface } from "ethers"; import { ClaimNotFoundError } from "./errors"; import { getMessageStatus } from "./arbMsgExecutor"; import { @@ -56,13 +57,14 @@ const getClaim = async ({ }; const claimHash = await veaOutbox.claimHashes(epoch); if (claimHash === ethers.ZeroHash) return null; - + console.log("CLAIM HASH FROM CONTRACT", claimHash); try { const [claimLogs, challengeLogs, verificationLogs] = await Promise.all([ veaOutbox.queryFilter(veaOutbox.filters.Claimed(null, epoch, null), fromBlock, toBlock), veaOutbox.queryFilter(veaOutbox.filters.Challenged(epoch, null), fromBlock, toBlock), veaOutbox.queryFilter(veaOutbox.filters.VerificationStarted(epoch), fromBlock, toBlock), ]); + console.log("CLAIM LOGS", claimLogs); claim.stateRoot = claimLogs[0].data; claim.claimer = `0x${claimLogs[0].topics[1].slice(26)}`; claim.timestampClaimed = (await veaOutboxProvider.getBlock(claimLogs[0].blockNumber)).timestamp; @@ -118,6 +120,7 @@ export interface ClaimResolveStateParams { chainId: number; veaInbox: any; veaInboxProvider: JsonRpcProvider; + veaOutbox: any; veaOutboxProvider: JsonRpcProvider; epoch: number; fromBlock: number; @@ -126,9 +129,10 @@ export interface ClaimResolveStateParams { } /** - * Fetches the claim resolve state. + * Fetches the claim resolve state. Verifies claimHash from sent snapshot logs with Outbox claimHash. To call if claim is not yet resolved else an extra snapshot will be sent. * @param veaInbox VeaInbox contract instance * @param veaInboxProvider VeaInbox provider + * @param veaOutbox VeaOutbox contract instance * @param veaOutboxProvider VeaOutbox provider * @param epoch epoch number of the claim to be fetched * @param fromBlock from block number @@ -140,11 +144,12 @@ const getClaimResolveState = async ({ chainId, veaInbox, veaInboxProvider, + veaOutbox, veaOutboxProvider, epoch, fromBlock, toBlock, - fetchMessageStatus, + fetchMessageStatus = getMessageStatus, }: ClaimResolveStateParams): Promise => { let claimResolveState: ClaimResolveState = { sendSnapshot: { @@ -156,20 +161,45 @@ const getClaimResolveState = async ({ txHash: "", }, }; - try { const sentSnapshotLogs = await veaInbox.queryFilter(veaInbox.filters.SnapshotSent(epoch, null), fromBlock, toBlock); if (sentSnapshotLogs.length > 0) { - claimResolveState.sendSnapshot.status = true; - claimResolveState.sendSnapshot.txHash = sentSnapshotLogs[0].transactionHash; + sentSnapshotLogs.sort((a, b) => + a.blockNumber !== b.blockNumber ? b.blockNumber - a.blockNumber : b.logIndex - a.logIndex + ); + // Add logic to check if the sent message has the actual claimHash or not + const expectedClaimHash = await getSentSnapshotData( + sentSnapshotLogs[0].transactionHash, + veaInboxProvider, + veaInbox.interface + ); + const claimHash = await veaOutbox.claimHashes(epoch); + console.log("CLAIM HASH FROM CONTRACT", claimHash); + console.log("EXPECTED CLAIM HASH FROM SENT SNAPSHOT", expectedClaimHash); + if (claimHash === expectedClaimHash) { + claimResolveState.sendSnapshot.status = true; + claimResolveState.sendSnapshot.txHash = sentSnapshotLogs[0].transactionHash; + } else { + return claimResolveState; + } } else { return claimResolveState; } } catch { const sentSnapshotFromGraph = await getSnapshotSentForEpoch(epoch, await veaInbox.getAddress(), chainId); if (sentSnapshotFromGraph) { - claimResolveState.sendSnapshot.status = true; - claimResolveState.sendSnapshot.txHash = sentSnapshotFromGraph.txHash; + const expectedClaimHash = await getSentSnapshotData( + sentSnapshotFromGraph.txHash, + veaInboxProvider, + veaInbox.interface + ); + const claimHash = await veaOutbox.claimHashes(epoch); + if (claimHash === expectedClaimHash) { + claimResolveState.sendSnapshot.status = true; + claimResolveState.sendSnapshot.txHash = sentSnapshotFromGraph.txHash; + } else { + return claimResolveState; + } } else { return claimResolveState; } @@ -204,4 +234,16 @@ const hashClaim = (claim: ClaimStruct) => { ); }; +const getSentSnapshotData = async (txHash: string, provider: JsonRpcProvider, inboxInterface: any): Promise => { + const tx = await provider.getTransaction(txHash); + if (!tx) return null; + + // Parse the transaction calldata to identify function + args + const parsed = inboxInterface.parseTransaction({ data: tx.data }); + const args = parsed.args; + const claimTuple = args[1] as ClaimStruct; + const expectedClaimHash = hashClaim(claimTuple); + return expectedClaimHash; +}; + export { getClaim, hashClaim, getClaimResolveState, ClaimHonestState }; diff --git a/validator-cli/src/utils/graphQueries.ts b/validator-cli/src/utils/graphQueries.ts index eebe4473..62efaa4e 100644 --- a/validator-cli/src/utils/graphQueries.ts +++ b/validator-cli/src/utils/graphQueries.ts @@ -162,7 +162,7 @@ const getSnapshotSentForEpoch = async ( `${subgraph}`, `{ snapshots(where: {epoch: ${epoch}, inbox_: { id: "${veaInbox}" }}) { - fallback{ + fallback(orderBy: timestamp, orderDirection: desc){ txHash } } diff --git a/validator-cli/src/utils/logger.ts b/validator-cli/src/utils/logger.ts index 34f2c5f8..9cbdbb57 100644 --- a/validator-cli/src/utils/logger.ts +++ b/validator-cli/src/utils/logger.ts @@ -141,4 +141,7 @@ export const configurableInitialize = (emitter: EventEmitter) => { emitter.on(BotEvents.CHALLENGER_WON_CLAIM, () => { console.log("Challenger won claim"); }); + emitter.on(BotEvents.CLAIM_ALREADY_RESOLVED, (epoch: number) => { + console.log(`Claim for epoch ${epoch} is already resolved.`); + }); }; From 1ecb72ea09c2c8f1eb2a08528c120d2d9fe2090b Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 27 Oct 2025 18:26:37 +0530 Subject: [PATCH 08/11] fix: save snapshot for missed claims --- validator-cli/src/helpers/snapshot.test.ts | 44 +++++++++++++++++++--- validator-cli/src/helpers/snapshot.ts | 23 ++++++++++- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/validator-cli/src/helpers/snapshot.test.ts b/validator-cli/src/helpers/snapshot.test.ts index 5013f742..4164b015 100644 --- a/validator-cli/src/helpers/snapshot.test.ts +++ b/validator-cli/src/helpers/snapshot.test.ts @@ -4,6 +4,7 @@ import { MockEmitter } from "../utils/emitter"; describe("snapshot", () => { let veaInbox: any; + let veaOutbox: any; let count: number = 1; const chainId = 11155111; let fetchLastSavedMessage: jest.Mock; @@ -14,8 +15,12 @@ describe("snapshot", () => { filters: { SnapshotSaved: jest.fn(), }, + snapshots: jest.fn(), getAddress: jest.fn().mockResolvedValue("0x1"), }; + veaOutbox = { + stateRoot: jest.fn(), + }; }); describe("isSnapshotNeeded", () => { it("should return false and updated count when there are no new messages and count is -1 ", () => { @@ -27,9 +32,10 @@ describe("snapshot", () => { const params = { chainId, veaInbox, + veaOutbox, count, fetchLastSavedMessage, - }; + } as any; expect(isSnapshotNeeded(params)).resolves.toEqual({ snapshotNeeded: false, latestCount: currentCount, @@ -45,9 +51,10 @@ describe("snapshot", () => { const params = { chainId, veaInbox, + veaOutbox, count, fetchLastSavedMessage, - }; + } as any; expect(isSnapshotNeeded(params)).resolves.toEqual({ snapshotNeeded: false, latestCount: count, @@ -62,9 +69,10 @@ describe("snapshot", () => { const params = { chainId, veaInbox, + veaOutbox, count, fetchLastSavedMessage, - }; + } as any; expect(isSnapshotNeeded(params)).resolves.toEqual({ snapshotNeeded: false, latestCount: currentCount, @@ -79,9 +87,10 @@ describe("snapshot", () => { const params = { chainId, veaInbox, + veaOutbox, count, fetchLastSavedMessage, - }; + } as any; expect(isSnapshotNeeded(params)).resolves.toEqual({ snapshotNeeded: true, latestCount: currentCount, @@ -96,9 +105,30 @@ describe("snapshot", () => { const params = { chainId, veaInbox, + veaOutbox, count, fetchLastSavedMessage, - }; + } as any; + expect(isSnapshotNeeded(params)).resolves.toEqual({ + snapshotNeeded: true, + latestCount: currentCount, + }); + }); + it.only("should return true if claim was missed in previous epoch", async () => { + count = 1; + let currentCount = 3; + veaInbox.count.mockResolvedValue(currentCount); + fetchLastSavedMessage = jest.fn().mockResolvedValue("message-3"); + veaInbox.queryFilter.mockRejectedValue(new Error("queryFilter failed")); + veaOutbox.stateRoot.mockResolvedValue("0xabcde"); + veaInbox.snapshots.mockResolvedValue("0x0"); + const params = { + chainId, + veaInbox, + veaOutbox, + count, + fetchLastSavedMessage, + } as any; expect(isSnapshotNeeded(params)).resolves.toEqual({ snapshotNeeded: true, latestCount: currentCount, @@ -119,6 +149,7 @@ describe("snapshot", () => { const res = await saveSnapshot({ chainId, veaInbox, + veaOutbox, network, epochPeriod, count, @@ -146,6 +177,7 @@ describe("snapshot", () => { const res = await saveSnapshot({ chainId, veaInbox, + veaOutbox, network, epochPeriod, count, @@ -172,6 +204,7 @@ describe("snapshot", () => { const res = await saveSnapshot({ chainId, veaInbox, + veaOutbox, network, epochPeriod, count: -1, @@ -200,6 +233,7 @@ describe("snapshot", () => { const res = await saveSnapshot({ chainId, veaInbox, + veaOutbox, network: Network.DEVNET, epochPeriod, count, diff --git a/validator-cli/src/helpers/snapshot.ts b/validator-cli/src/helpers/snapshot.ts index 91427d65..16708c52 100644 --- a/validator-cli/src/helpers/snapshot.ts +++ b/validator-cli/src/helpers/snapshot.ts @@ -1,10 +1,13 @@ +import { ZeroHash } from "ethers"; import { Network, snapshotSavingPeriod } from "../consts/bridgeRoutes"; import { getLastMessageSaved } from "../utils/graphQueries"; import { BotEvents } from "../utils/botEvents"; import { defaultEmitter } from "../utils/emitter"; interface SnapshotCheckParams { + epochPeriod: number; chainId: number; veaInbox: any; + veaOutbox: any; count: number; fetchLastSavedMessage?: typeof getLastMessageSaved; } @@ -12,6 +15,7 @@ interface SnapshotCheckParams { export interface SaveSnapshotParams { chainId: number; veaInbox: any; + veaOutbox: any; network: Network; epochPeriod: number; count: number; @@ -24,6 +28,7 @@ export interface SaveSnapshotParams { export const saveSnapshot = async ({ chainId, veaInbox, + veaOutbox, network, epochPeriod, count, @@ -40,8 +45,10 @@ export const saveSnapshot = async ({ return { transactionHandler, latestCount: count }; } const { snapshotNeeded, latestCount } = await toSaveSnapshot({ + epochPeriod, chainId, veaInbox, + veaOutbox, count, }); if (!snapshotNeeded) return { transactionHandler, latestCount }; @@ -50,28 +57,42 @@ export const saveSnapshot = async ({ }; export const isSnapshotNeeded = async ({ + epochPeriod, chainId, veaInbox, + veaOutbox, count, fetchLastSavedMessage = getLastMessageSaved, }: SnapshotCheckParams): Promise<{ snapshotNeeded: boolean; latestCount: number }> => { const currentCount = Number(await veaInbox.count()); + if (count == currentCount) { return { snapshotNeeded: false, latestCount: currentCount }; } let lastSavedCount: number; + let lastSavedSnapshot: string; try { const saveSnapshotLogs = await veaInbox.queryFilter(veaInbox.filters.SnapshotSaved()); lastSavedCount = Number(saveSnapshotLogs[saveSnapshotLogs.length - 1].args[2]); + lastSavedSnapshot = saveSnapshotLogs[saveSnapshotLogs.length - 1].args[1]; } catch { const veaInboxAddress = await veaInbox.getAddress(); - const lastSavedMessageId = await fetchLastSavedMessage(veaInboxAddress, chainId); + const { id: lastSavedMessageId, stateRoot: lastSavedStateRoot } = await fetchLastSavedMessage( + veaInboxAddress, + chainId + ); const messageIndex = extractMessageIndex(lastSavedMessageId); + lastSavedSnapshot = lastSavedStateRoot; // adding 1 to the message index to get the last saved count lastSavedCount = messageIndex; } + const epochNow = Math.floor(Date.now() / (1000 * epochPeriod)); + const currentSnapshot = await veaInbox.snapshots(epochNow); + const currentStateRoot = await veaOutbox.stateRoot(); if (currentCount > lastSavedCount) { return { snapshotNeeded: true, latestCount: currentCount }; + } else if (currentSnapshot == ZeroHash && lastSavedSnapshot != currentStateRoot) { + return { snapshotNeeded: true, latestCount: currentCount }; } return { snapshotNeeded: false, latestCount: currentCount }; }; From f794917ca604d263fb5b34f4a63bac95e7cc1239 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 27 Oct 2025 18:27:19 +0530 Subject: [PATCH 09/11] chore: check withdrawal before all --- validator-cli/src/helpers/validator.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/validator-cli/src/helpers/validator.ts b/validator-cli/src/helpers/validator.ts index 74d96a81..8b0b9c14 100644 --- a/validator-cli/src/helpers/validator.ts +++ b/validator-cli/src/helpers/validator.ts @@ -77,6 +77,10 @@ export async function challengeAndResolveClaim({ // If claim is already resolved, nothing to do if (claim.honest !== 0) { emitter.emit(BotEvents.CLAIM_ALREADY_RESOLVED, epoch); + if (claim.honest === 2) { + await transactionHandler.withdrawChallengeDeposit(); + return transactionHandler; + } return null; } From f61dccf0488f6975f7034553efc7c5643f80248f Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 27 Oct 2025 18:29:15 +0530 Subject: [PATCH 10/11] chore: cleanup and tests --- validator-cli/src/utils/claim.test.ts | 24 +++++++++++++++++++++--- validator-cli/src/utils/claim.ts | 11 +++++------ validator-cli/src/utils/graphQueries.ts | 6 ++++-- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/validator-cli/src/utils/claim.test.ts b/validator-cli/src/utils/claim.test.ts index 0f490590..34bb075d 100644 --- a/validator-cli/src/utils/claim.test.ts +++ b/validator-cli/src/utils/claim.test.ts @@ -1,4 +1,4 @@ -import { ethers } from "ethers"; +import { ethers, getAddress } from "ethers"; import { ClaimStruct } from "@kleros/vea-contracts/typechain-types/arbitrumToEth/VeaInboxArbToEth"; import { getClaim, hashClaim, getClaimResolveState, ClaimResolveStateParams } from "./claim"; import { ClaimNotFoundError } from "./errors"; @@ -232,8 +232,8 @@ describe("snapshotClaim", () => { describe("getClaimResolveState", () => { let veaInbox: any; - let veaInboxProvider: any; - let veaOutboxProvider: any; + let veaOutbox: any; + let fetchSentSnapshotData: any; const epoch = 1; const blockNumberOutboxLowerBound = 1234; const toBlock = "latest"; @@ -253,10 +253,17 @@ describe("snapshotClaim", () => { filters: { SnapshotSent: jest.fn(), }, + getAddress: jest.fn(), }; + veaOutbox = { + claimHashes: jest.fn().mockResolvedValueOnce(hashedMockClaim), + getAddress: jest.fn(), + }; + fetchSentSnapshotData = jest.fn().mockResolvedValueOnce(hashedMockClaim); mockClaimResolveStateParams = { chainId: 11155111, veaInbox, + veaOutbox, veaInboxProvider: { getBlock: jest.fn().mockResolvedValueOnce({ timestamp: mockClaim.timestampClaimed, number: 1234 }), } as any, @@ -267,6 +274,7 @@ describe("snapshotClaim", () => { fromBlock: blockNumberOutboxLowerBound, toBlock, fetchMessageStatus: jest.fn(), + fetchSentSnapshotData, }; }); @@ -287,5 +295,15 @@ describe("snapshotClaim", () => { expect(claimResolveState.sendSnapshot.status).toBeTruthy(); expect(claimResolveState.execution.status).toBe(0); }); + + it("should return false state if incorrect snapshot sent", async () => { + veaInbox.queryFilter.mockResolvedValueOnce([{ transactionHash: "0x1234" }]); + fetchSentSnapshotData = jest.fn().mockResolvedValueOnce("0xincorrecthash"); + mockClaimResolveStateParams.fetchSentSnapshotData = fetchSentSnapshotData; + const claimResolveState = await getClaimResolveState(mockClaimResolveStateParams); + expect(claimResolveState).toBeDefined(); + expect(claimResolveState.sendSnapshot.status).toBeFalsy(); + expect(claimResolveState.execution.status).toBe(0); + }); }); }); diff --git a/validator-cli/src/utils/claim.ts b/validator-cli/src/utils/claim.ts index 32d62337..b75dc132 100644 --- a/validator-cli/src/utils/claim.ts +++ b/validator-cli/src/utils/claim.ts @@ -57,14 +57,12 @@ const getClaim = async ({ }; const claimHash = await veaOutbox.claimHashes(epoch); if (claimHash === ethers.ZeroHash) return null; - console.log("CLAIM HASH FROM CONTRACT", claimHash); try { const [claimLogs, challengeLogs, verificationLogs] = await Promise.all([ veaOutbox.queryFilter(veaOutbox.filters.Claimed(null, epoch, null), fromBlock, toBlock), veaOutbox.queryFilter(veaOutbox.filters.Challenged(epoch, null), fromBlock, toBlock), veaOutbox.queryFilter(veaOutbox.filters.VerificationStarted(epoch), fromBlock, toBlock), ]); - console.log("CLAIM LOGS", claimLogs); claim.stateRoot = claimLogs[0].data; claim.claimer = `0x${claimLogs[0].topics[1].slice(26)}`; claim.timestampClaimed = (await veaOutboxProvider.getBlock(claimLogs[0].blockNumber)).timestamp; @@ -126,6 +124,7 @@ export interface ClaimResolveStateParams { fromBlock: number; toBlock: number | string; fetchMessageStatus?: typeof getMessageStatus; + fetchSentSnapshotData?: typeof getSentSnapshotData; } /** @@ -150,6 +149,7 @@ const getClaimResolveState = async ({ fromBlock, toBlock, fetchMessageStatus = getMessageStatus, + fetchSentSnapshotData = getSentSnapshotData, }: ClaimResolveStateParams): Promise => { let claimResolveState: ClaimResolveState = { sendSnapshot: { @@ -168,14 +168,13 @@ const getClaimResolveState = async ({ a.blockNumber !== b.blockNumber ? b.blockNumber - a.blockNumber : b.logIndex - a.logIndex ); // Add logic to check if the sent message has the actual claimHash or not - const expectedClaimHash = await getSentSnapshotData( + const expectedClaimHash = await fetchSentSnapshotData( sentSnapshotLogs[0].transactionHash, veaInboxProvider, veaInbox.interface ); const claimHash = await veaOutbox.claimHashes(epoch); - console.log("CLAIM HASH FROM CONTRACT", claimHash); - console.log("EXPECTED CLAIM HASH FROM SENT SNAPSHOT", expectedClaimHash); + if (claimHash === expectedClaimHash) { claimResolveState.sendSnapshot.status = true; claimResolveState.sendSnapshot.txHash = sentSnapshotLogs[0].transactionHash; @@ -188,7 +187,7 @@ const getClaimResolveState = async ({ } catch { const sentSnapshotFromGraph = await getSnapshotSentForEpoch(epoch, await veaInbox.getAddress(), chainId); if (sentSnapshotFromGraph) { - const expectedClaimHash = await getSentSnapshotData( + const expectedClaimHash = await fetchSentSnapshotData( sentSnapshotFromGraph.txHash, veaInboxProvider, veaInbox.interface diff --git a/validator-cli/src/utils/graphQueries.ts b/validator-cli/src/utils/graphQueries.ts index 62efaa4e..553fa0b4 100644 --- a/validator-cli/src/utils/graphQueries.ts +++ b/validator-cli/src/utils/graphQueries.ts @@ -177,6 +177,7 @@ const getSnapshotSentForEpoch = async ( type SnapshotSavedResponse = { snapshots: { + stateRoot: string; messages: { id: string; }[]; @@ -188,12 +189,13 @@ type SnapshotSavedResponse = { * @param veaInbox * @returns message id */ -const getLastMessageSaved = async (veaInbox: string, chainId: number): Promise => { +const getLastMessageSaved = async (veaInbox: string, chainId: number): Promise<{ id: string; stateRoot: string }> => { const subgraph = getInboxSubgraphUrl(chainId); const result: SnapshotSavedResponse = await request( `${subgraph}`, `{ snapshots(first:2, orderBy:timestamp,orderDirection:desc, where:{inbox:"${veaInbox}"}) { + stateRoot messages(first: 1,orderBy:timestamp,orderDirection:desc){ id } @@ -201,7 +203,7 @@ const getLastMessageSaved = async (veaInbox: string, chainId: number): Promise Date: Mon, 27 Oct 2025 18:29:44 +0530 Subject: [PATCH 11/11] chore: update rpc range --- validator-cli/src/watcher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator-cli/src/watcher.ts b/validator-cli/src/watcher.ts index da98878b..d7f7df88 100644 --- a/validator-cli/src/watcher.ts +++ b/validator-cli/src/watcher.ts @@ -14,7 +14,7 @@ import { ChallengeAndResolveClaimParams, challengeAndResolveClaim } from "./help import { saveSnapshot, SaveSnapshotParams } from "./helpers/snapshot"; import { getTransactionHandler } from "./utils/transactionHandlers"; -const RPC_BLOCK_LIMIT = 1000; // RPC_BLOCK_LIMIT is the limit of blocks that can be queried at once +const RPC_BLOCK_LIMIT = 100; // RPC_BLOCK_LIMIT is the limit of blocks that can be queried at once /** * @file This file contains the logic for watching bridge and validating/resolving for claims. @@ -157,6 +157,7 @@ async function processEpochsForNetwork({ const { updatedTransactionHandler, latestCount } = await saveSnapshot({ chainId, veaInbox, + veaOutbox, network, epochPeriod: routeConfig[network].epochPeriod, count: toWatch[networkKey].count, @@ -178,7 +179,6 @@ async function processEpochsForNetwork({ toBlock = epochBlock + RPC_BLOCK_LIMIT; } const claim = await getClaim({ chainId, veaOutbox, veaOutboxProvider, epoch, fromBlock: epochBlock, toBlock }); - let updatedTransactions; if (path > BotPaths.CLAIMER && claim != null) { const checkAndChallengeResolveDeps: ChallengeAndResolveClaimParams = {