From 4b1d83dbceb691ae0fbce18a5b809d73fc873dc2 Mon Sep 17 00:00:00 2001 From: Mani Brar Date: Mon, 25 Aug 2025 11:02:29 +0530 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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;