-
Couldn't load subscription status.
- Fork 7
Feat/hashi executor #434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Feat/hashi executor #434
Conversation
✅ Deploy Preview for veascan ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
WalkthroughIntegrates an optional Hashi executor and message-execution pipeline: adds Hashi configs/ABIs/types/utilities, GraphQL helpers, Hashi orchestration and tests, threads a separate hashiNonce through init/state, extends relay/proof to include msgSender/from, and adds related events/logging and state file. Changes
Sequence Diagram(s)sequenceDiagram
actor Relayer
participant RH as relayerHelpers
participant HE as HashiExecutor
participant GQ as GraphQueries
participant HM as toExecuteMessage
participant SF as StateFile
Relayer->>RH: initialize(chainId, ...) → {nonce, hashiNonce}
RH-->>Relayer: {nonce, hashiNonce}
alt HASHI_EXECUTOR_ENABLED
Relayer->>HE: runHashiExecutor({chainId, network, nonce: hashiNonce, emitter})
HE->>GQ: getNonceFrom / getVeaMsgTrnx / getCount
GQ-->>HE: nonces, txHashes, counts
loop per nonce range
HE->>HM: toExecuteMessage(nonce, veaInbox, rpcInbox, ...)
HM-->>HE: VeaNonceToHashiMessage | null
end
HE->>HE: executeBatchOnHashi(messages[])
HE-->>Relayer: updated hashiNonce
end
Relayer->>SF: updateStateFile(..., nonce, hashiNonce)
SF-->>Relayer: persisted
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Areas to focus review on:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
relayer-cli/src/utils/proof.ts (2)
23-49: Harden getMessageDataToRelay: handle empty results, narrow query, and type the return
- Query asks for first: 5 but only index 0 is used; set first: 1.
- Guard when messageSents is empty to avoid result[0] access.
- Explicit return type e.g., Promise<[string, string, string] | undefined>.
- chainId param is currently unused; either use it to select subgraph or remove.
Apply:
- messageSents(first: 5, where: {nonce: ${nonce}, inbox: "${inbox}"}) { + messageSents(first: 1, where: {nonce: ${nonce}, inbox: "${inbox}"}) { @@ - return [result[`messageSents`][0].to.id, result[`messageSents`][0].msgSender.id, result[`messageSents`][0].data]; + const [first] = result.messageSents ?? []; + if (!first) return undefined; + return [first.to.id, first.msgSender.id, first.data];
69-108: Call sites properly updated; null-safety and casing fixes still neededtoLocaleLowerCase() produces different results than toLowerCase() for certain locales like Turkish, which can cause issues when normalizing addresses. Both call sites in
relay.ts(lines 30 and 165) correctly pass theinboxAddressparameter, but the following changes inproof.tsremain unaddressed:
- Replace
inboxAddress.toLocaleLowerCase()withtoLowerCase()(line 84)- Add null-safety checks before accessing
result[layer${i}][0].hash(lines 97-101); return empty array if layer missing or emptychainIdparameter is unused; consider removing or using for subgraph selection- const layerId = inboxAddress.toLocaleLowerCase() + "-" + proofIndices[i]; + const layerId = inboxAddress.toLowerCase() + "-" + proofIndices[i]; @@ - for (let i = 0; i < proofIndices.length; i++) { - proof.push(result[`layer${i}`][0].hash); - } + for (let i = 0; i < proofIndices.length; i++) { + const layer = result[`layer${i}`]; + if (!layer || layer.length === 0 || !layer[0]?.hash) { + return []; + } + proof.push(layer[0].hash); + }relayer-cli/src/utils/relay.test.ts (1)
115-117: Comment vs stub mismatch on gas estimateThe comment references an estimated gas of 500000 → 600000 after 20% buffer, but the stub returns 600000. Either change the stub to 500000 or fix the comment.
- // With an estimated gas of 500000, the computed gasLimit is (500000 * 120)/100 = 600000. + // With an estimated gas of 600000, the computed gasLimit is 600000 (per current stub/logic).Or:
- mockBatchSend.estimateGas = jest.fn().mockResolvedValue(600000); + mockBatchSend.estimateGas = jest.fn().mockResolvedValue(500000);relayer-cli/src/utils/relay.ts (3)
176-186: Fix return when no messages were batched.
lastNoncestaysnullwhen nothing was added; returninglastNonce + 1yields1and violatesPromise<number | null>.Apply:
- return lastNonce + 1; // return current nonce + // If no messages were prepared, signal no progress. + return lastNonce === null ? null : lastNonce + 1;
76-86: Guard against missing PRIVATE_KEY (batch path).
fetchBatcher/fetchVeaOutboxwill construct a signer; withoutPRIVATE_KEYthis fails at runtime.Apply:
const bridgeConfig = fetchBridgeConfig(chainId); const privateKey = process.env.PRIVATE_KEY; + if (!privateKey) throw new MissingEnvironmentVariable("PRIVATE_KEY");
140-148: Guard against missing PRIVATE_KEY (relayAllFrom).Same risk here when creating
batcherandveaOutbox.Apply:
const privateKey = process.env.PRIVATE_KEY; + if (!privateKey) throw new MissingEnvironmentVariable("PRIVATE_KEY");relayer-cli/src/utils/relayerHelpers.ts (1)
66-86: Fix state persistence and type safety for nullable hashiNonce.The call site at relayer.ts:79 passes potentially null/undefined values:
initializereturns{ nonce: number | null; hashiNonce: number | null }andrunHashiExecutorreturnsnumber | undefined. The current function signature incorrectly types params as non-nullablenumber, and the conditionif (nonceFrom != null)skips state writes when onlyhashiNonceadvances, losing progress.Apply:
-async function updateStateFile( +async function updateStateFile( chainId: number, createdTimestamp: number, - nonceFrom: number, - hashiNonceFrom: number, + nonceFrom: number | null, + hashiNonceFrom: number | null, network: string, emitter: EventEmitter, fileSystem: typeof fs = fs, removeLock: typeof releaseLock = releaseLock ) { - if (nonceFrom != null) { + if (nonceFrom != null || hashiNonceFrom != null) { const stateDir = process.env.STATE_DIR || ""; if (!fileSystem.existsSync(stateDir)) { fileSystem.mkdirSync(stateDir, { recursive: true }); } - const chain_state_file = process.env.STATE_DIR + network + "_" + chainId + ".json"; - const json = { - ts: createdTimestamp, - nonce: nonceFrom, - hashiNonce: hashiNonceFrom, - }; + const chain_state_file = path.join(stateDir, `${network}_${chainId}.json`); + const existing = + fileSystem.existsSync(chain_state_file) + ? JSON.parse(fileSystem.readFileSync(chain_state_file, { encoding: "utf8" })) + : { ts: createdTimestamp, nonce: 0, hashiNonce: 0 }; + const json = { + ts: createdTimestamp, + nonce: nonceFrom ?? existing.nonce ?? 0, + hashiNonce: hashiNonceFrom ?? existing.hashiNonce ?? 0, + }; fileSystem.writeFileSync(chain_state_file, JSON.stringify(json), { encoding: "utf8" }); } removeLock(network, chainId); emitter.emit(BotEvents.LOCK_RELEASED); }
🧹 Nitpick comments (13)
relayer-cli/src/utils/graphQueries.ts (3)
4-20: Prefer GraphQL variables over string interpolation and reduce log noise
- Build queries with variables to avoid injection/escaping issues and to improve caching.
- Consider gating console.log behind a DEBUG/LOG_LEVEL env.
Example:
-const query = `{messageSents(first: 1, where: {nonce: ${nonce}, inbox: "${inboxAddress}"}) { id transactionHash }}` -const result = await request(url, query) +const query = `query($nonce: Int!, $inbox: String!) { + messageSents(first: 1, where: { nonce: $nonce, inbox: $inbox }) { id transactionHash } +}` +const result = await request(url, query, { nonce, inbox: inboxAddress })
2-2: Type safety noteDefining a minimal common interface for veaOutbox (e.g., with stateRoot()) allows easier mocking and testing across chains. Based on learnings.
12-19: Error handling is fine; consider structured loggingCatching and returning [] is acceptable here; add context (nonce/inbox) to the log object for observability.
relayer-cli/.env.dist (1)
24-27: Lint warnings vs. intended quoting; add trailing newline
- Quotes around STATE_DIR are intentional; keep as-is. Based on learnings.
- Add a newline at EOF to appease linters.
HASHI_EXECUTOR_ENABLED=true +Optional: default to false in dist to prevent accidental enablement in new setups.
relayer-cli/src/utils/relay.test.ts (1)
64-65: Test fixture updated to [to, from, data]Good alignment with the new tuple signature. Consider asserting that the encoded calldata reflects inclusion of the “from” parameter to prevent regressions.
relayer-cli/src/utils/logger.ts (1)
63-72: Add a handler for HASHI_EXECUTION_FAILED to surface errorsYou emit a failure event in BotEvents but don’t log it here. Add an error listener for parity with MESSAGE_EXECUTION_FAILED.
Suggested diff:
// Hashi executor logs emitter.on(BotEvents.EXECUTING_HASHI, (startNonce, endNonce) => { console.log(`Executing Hashi for nonces from ${startNonce} to ${endNonce}`); }); emitter.on(BotEvents.HASHI_EXECUTED, (endNonce) => { console.log(`Successfully executed Hashi till ${endNonce}`); }); emitter.on(BotEvents.HASHI_BATCH_TXN, (txHash, batchSize) => { console.log(`Hashi batch transaction ${txHash} for ${batchSize} messages`); }); + emitter.on(BotEvents.HASHI_EXECUTION_FAILED, (failedNonce, reason) => { + console.error(`Hashi execution failed at nonce ${failedNonce}`, reason); + });relayer-cli/src/utils/botEvents.ts (1)
21-25: Enum additions LGTM; ensure consumers handle failure eventNew Hashi events look good. Confirm logger binds HASHI_EXECUTION_FAILED (and it emits error-level logs).
relayer-cli/src/consts/bridgeRoutes.ts (1)
20-24: Parameterize Hashi/Yaru/Yaho addresses and verify correctness per chainHard-coding addresses risks drift across envs. Prefer env vars (e.g., HASHI_YAHO_ARBITRUM_SEPOLIA, HASHI_YARU_SEPOLIA, HASHI_HASHI_SEPOLIA, etc.) and document required values. Also confirm these addresses are correct for 11155111 and 10200 and that rpcInbox pointing to RPC_ARBITRUM_SEPOLIA is intentional for both.
Also applies to: 67-71, 79-83
relayer-cli/src/utils/hashi.test.ts (1)
6-14: Reuse shared MockEmitter to avoid duplicationThere’s already a MockEmitter utility (validator-cli/src/utils/emitter.ts). Reuse it here to reduce duplication and keep behavior consistent.
relayer-cli/src/utils/hashiHelpers/hashiMsgUtils.ts (1)
11-23: Prefer public ethers APIs and correct return typing
- Use bigint (primitive) as return type for getHashiMsgId.
- Replace internal toBeArray with getBytes and validate 32‑byte length.
- Since you now accept Uint8Array in HashiMessage.data (see types suggestion), the data branch remains correct.
Suggested diff:
export function getHashiMsgId( sourceChainId: bigint | number | string, dispatcherAddress: string, message: HashiMessage -): BigInt { +): bigint { // Normalize exactly as Solidity would see inputs const source = BigInt(sourceChainId); const dispatcher = getAddress(dispatcherAddress); // checksum normalize const msgHash = calculateMessageHash(message); const encodedId = coder.encode(["uint256", "address", "bytes32"], [source, dispatcher, msgHash]); return hexToBigInt32Bytes(keccak256(encodedId)); } @@ -function hexToBigInt32Bytes(hex32: string): bigint { - const bytes = toBeArray(hex32); +function hexToBigInt32Bytes(hex32: string): bigint { + const bytes = getBytes(hex32); + if (bytes.length !== 32) { + throw new Error(`Expected 32-byte hex string, got ${bytes.length} bytes`); + } // Convert bytes to BigInt (big-endian) let n = BigInt(0); for (const b of Array.from(bytes)) { n = (n << BigInt(8)) + BigInt(b); } return n; }Also applies to: 34-41, 56-64
relayer-cli/src/utils/relay.ts (1)
127-133: Doc nit: update JSDoc param name.Comment says
@param msgSenderbut code usesmsgSenders: string[].- * @param msgSender The address of the sender + * @param msgSenders The addresses of the sendersrelayer-cli/src/utils/hashi.ts (2)
24-45: Typo in function name:toExecuteMesssage.Three “s” makes grepping/imports error-prone; consider renaming to
toExecuteMessageand re-exporting an alias for backward compatibility.
229-235: Remove stray console.log; use BotEvents if needed.
console.log("Message already executed flag:", flag);is noisy in prod paths.- console.log("Message already executed flag:", flag);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
relayer-cli/.env.dist(1 hunks)relayer-cli/src/consts/bridgeRoutes.ts(2 hunks)relayer-cli/src/relayer.ts(3 hunks)relayer-cli/src/utils/botEvents.ts(1 hunks)relayer-cli/src/utils/graphQueries.ts(1 hunks)relayer-cli/src/utils/hashi.test.ts(1 hunks)relayer-cli/src/utils/hashi.ts(1 hunks)relayer-cli/src/utils/hashiHelpers/abi.ts(1 hunks)relayer-cli/src/utils/hashiHelpers/hashiMsgUtils.ts(1 hunks)relayer-cli/src/utils/hashiHelpers/hashiTypes.ts(1 hunks)relayer-cli/src/utils/logger.ts(1 hunks)relayer-cli/src/utils/proof.ts(2 hunks)relayer-cli/src/utils/relay.test.ts(1 hunks)relayer-cli/src/utils/relay.ts(4 hunks)relayer-cli/src/utils/relayerHelpers.test.ts(3 hunks)relayer-cli/src/utils/relayerHelpers.ts(4 hunks)relayer-cli/state/devnet_11155111.json(1 hunks)relayer-subgraph-inbox/src/vea-inbox.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-08-27T07:02:45.825Z
Learnt from: mani99brar
PR: kleros/vea#430
File: contracts/src/interfaces/inboxes/IVeaInbox.sol:0-0
Timestamp: 2025-08-27T07:02:45.825Z
Learning: The sendMessage signature simplification in IVeaInbox.sol and related call site updates across relay-cli and test files were fixed in a separate PR by mani99brar.
Applied to files:
relayer-subgraph-inbox/src/vea-inbox.ts
📚 Learning: 2024-12-10T08:00:35.645Z
Learnt from: mani99brar
PR: kleros/vea#370
File: bridger-cli/src/consts/bridgeRoutes.ts:28-30
Timestamp: 2024-12-10T08:00:35.645Z
Learning: In `bridger-cli/src/consts/bridgeRoutes.ts`, additional validation in the `getBridgeConfig` function is unnecessary because error handling and validation are managed by upper layers in the application.
Applied to files:
relayer-cli/src/consts/bridgeRoutes.ts
📚 Learning: 2025-07-07T12:29:34.770Z
Learnt from: mani99brar
PR: kleros/vea#427
File: relayer-cli/.env.dist:24-24
Timestamp: 2025-07-07T12:29:34.770Z
Learning: In relayer-cli/.env.dist, the user (mani99brar) intentionally keeps quotes around environment variable values like `STATE_DIR="/home/user/vea/relayer-cli/state/"` as this works as intended for their use case.
Applied to files:
relayer-cli/.env.dist
📚 Learning: 2024-12-09T09:04:04.819Z
Learnt from: mani99brar
PR: kleros/vea#370
File: bridger-cli/src/utils/graphQueries.ts:36-58
Timestamp: 2024-12-09T09:04:04.819Z
Learning: In `bridger-cli/src/utils/graphQueries.ts` (TypeScript), the `getVerificationStatus` function is no longer needed and has been removed.
Applied to files:
relayer-cli/src/utils/graphQueries.ts
📚 Learning: 2024-12-09T09:40:28.400Z
Learnt from: mani99brar
PR: kleros/vea#370
File: bridger-cli/src/utils/transactionHandler.ts:13-13
Timestamp: 2024-12-09T09:40:28.400Z
Learning: In `bridger-cli/src/utils/transactionHandler.ts`, the `veaOutbox` implementations differ for each chain, but a common interface should be defined for type checks to enhance type safety.
Applied to files:
relayer-cli/src/utils/graphQueries.ts
🧬 Code graph analysis (5)
relayer-cli/src/utils/hashi.test.ts (2)
validator-cli/src/utils/emitter.ts (1)
MockEmitter(6-14)relayer-cli/src/utils/hashiHelpers/hashiTypes.ts (2)
HashiMessage(1-10)VeaNonceToHashiMessage(12-16)
relayer-cli/src/relayer.ts (3)
relayer-cli/src/utils/hashi.ts (1)
runHashiExecutor(237-237)relayer-cli/src/utils/relay.ts (2)
relayBatch(188-188)relayAllFrom(188-188)relayer-cli/src/utils/relayerHelpers.ts (1)
updateStateFile(206-206)
relayer-cli/src/utils/relayerHelpers.test.ts (2)
relayer-cli/src/utils/relayerHelpers.ts (2)
initialize(205-205)updateStateFile(206-206)relayer-cli/src/utils/lock.ts (1)
releaseLock(72-85)
relayer-cli/src/utils/hashiHelpers/hashiMsgUtils.ts (1)
relayer-cli/src/utils/hashiHelpers/hashiTypes.ts (1)
HashiMessage(1-10)
relayer-cli/src/utils/hashi.ts (3)
relayer-cli/src/utils/hashiHelpers/hashiTypes.ts (2)
VeaNonceToHashiMessage(12-16)HashiMessage(1-10)relayer-cli/src/utils/hashiHelpers/abi.ts (3)
YaruAbi(13-342)messageDispatchedAbi(5-7)thresholdViewAbi(9-11)relayer-cli/src/utils/hashiHelpers/hashiMsgUtils.ts (1)
getHashiMsgId(11-23)
🪛 dotenv-linter (4.0.0)
relayer-cli/.env.dist
[warning] 24-24: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 27-27: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Analyze (javascript)
- GitHub Check: test
- GitHub Check: dependency-review
🔇 Additional comments (16)
relayer-subgraph-inbox/src/vea-inbox.ts (1)
34-38: Offsets verified as correct against contract implementation.The msgSender extraction at byte 28 and data extraction at byte 48 are correct. The contract encodes messages via
abi.encodePacked(oldCount, _to, msg.sender, _data)where oldCount (uint64) occupies bytes 0-7, _to (address) bytes 8-27, msg.sender (address) bytes 28-47, and _data (bytes) starts at byte 48. The TypeScript implementation accurately mirrors this format.relayer-cli/src/utils/proof.ts (2)
8-10: Schema change acknowledged: msgSender.id addedLooks good and matches the new [to, from, data] flow. Ensure downstream tuple types reflect this.
116-135: Proof indices logic: LGTMThe index computation looks consistent and guards nonce >= count.
relayer-cli/src/utils/relay.test.ts (1)
136-139: Batch expectations: OKTargets/values/datas arrays match the mock encoder behavior by nonce index. Looks consistent.
Also applies to: 167-172
relayer-cli/state/devnet_11155111.json (1)
1-5: No action required—ts is correctly stored as seconds and never consumed.The verification confirms the state file is safe. The
tsfield is explicitly written asMath.floor(Date.now() / 1000)(line 37 in relayerHelpers.ts), storing it in seconds. Theinitializefunction only extractsnonceandhashiNoncefrom the state file—tsis never read back, eliminating any risk of misinterpretation as milliseconds. The format is unambiguous in the code.relayer-cli/src/utils/graphQueries.ts (1)
31-47: Systematically unusedchainIdacross relayer functions—clarify design intentThe
chainIdparameter is unused ingetCount, but this mirrors the same pattern ingetMessageDataToRelay,getProofAtCount, and other relayer utilities—all receivechainIdyet use a singleprocess.env.RELAYER_SUBGRAPHendpoint. Clarify whether:
- Single subgraph is intentional and
chainIdcan be removed from all function signatures, or- Each function should use
chainIdto select chain-specific subgraph endpoints (aligning with the claimer'sgetOutboxSubgraphUrl/getInboxSubgraphUrlpattern).The query already filters appropriately by
stateRoot, but consistency across the codebase matters for maintainability.relayer-cli/src/utils/relayerHelpers.test.ts (1)
25-38: LGTM: tests correctly validate hashiNonce initialization and persistenceThe initialize and updateStateFile expectations reflect the extended state shape. Good coverage on both create and read paths.
Consider adding one test where initialize reads a state missing hashiNonce to ensure backward compatibility defaults to 0.
Also applies to: 41-54, 61-65
relayer-cli/src/relayer.ts (2)
66-71: Guard Hashi executor with try/catch and emit failure eventAvoid taking down the loop on transient Hashi errors. Emit BotEvents.HASHI_EXECUTION_FAILED and continue.
Suggested diff:
- if (hashiExecutorEnabled) { - // Execute messages on Hashi - hashiNonce = await runHashiExecutor({ chainId, network, nonce: hashiNonce, emitter }); - } + if (hashiExecutorEnabled) { + try { + // Execute messages on Hashi + hashiNonce = await runHashiExecutor({ chainId, network, nonce: hashiNonce, emitter }); + } catch (err) { + emitter.emit(BotEvents.HASHI_EXECUTION_FAILED, hashiNonce ?? 0, err); + } + }⛔ Skipped due to learnings
Learnt from: fcanela PR: kleros/vea#344 File: relayer-cli/src/testnetRelayer.ts:14-22 Timestamp: 2024-11-26T11:30:41.074Z Learning: In 'relayer-cli/src/testnetRelayer.ts', the error handling strategy is to allow errors to propagate and be handled by an upper layer, so adding try/catch statements in the main loop is not required.Learnt from: fcanela PR: kleros/vea#344 File: relayer-cli/src/devnetRelayExample.ts:9-14 Timestamp: 2024-11-26T11:30:35.723Z Learning: In `relayer-cli/src/devnetRelayExample.ts`, error handling is managed by the upper layer via `ShutdownManager`'s error signal handler, so adding a `try...catch` block in the main loop is unnecessary.Learnt from: fcanela PR: kleros/vea#344 File: relayer-cli/src/utils/relay.ts:28-40 Timestamp: 2024-11-26T11:30:36.180Z Learning: In `relayer-cli/src/utils/relay.ts`, avoid suggesting adding try/catch blocks that only log errors and rethrow in the `relay` function.
72-75: Review comment is incorrect—function signature already uses object parametersThe
relayBatchfunction atrelayer-cli/src/utils/relay.ts:63is defined with object destructuring:const relayBatch = async ({ chainId, network, nonce, maxBatchSize, emitter, ... })The call at
relayer-cli/src/relayer.ts:74correctly passes an object:await relayBatch({ chainId, network, nonce, maxBatchSize, emitter })All test invocations (relay.test.ts lines 82, 101, 122, 145, 180) also use object parameters, confirming this is the intended API. The proposed fix to change to positional parameters would break the code.
Likely an incorrect or invalid review comment.
relayer-cli/src/utils/relay.ts (3)
34-36: LGTM: expanded message tuple is wired correctly.
sendMessage(proof, nonce, to, from, data)aligns with the new[to, from, data]shape.
102-106: LGTM: batch path updated forfromparam.Static call and ABI encoding include
fromconsistently.
168-171: LGTM: relayAllFrom uses extended tuple and encodes correctly.relayer-cli/src/utils/relayerHelpers.ts (3)
28-29: LGTM: initialize now returns both nonce and hashiNonce.
38-39: LGTM: bootstrap includes hashiNonce=0 in new state.
45-55: LGTM: loads hashiNonce from state when present.relayer-cli/src/utils/hashiHelpers/abi.ts (1)
1-342: LGTM: ABI surfaces match the executor’s encode/decode usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
relayer-cli/src/utils/graphQueries.ts (1)
64-86: Paginate >1000 results and use variables; normalize casing.Current query caps at 1000 and may miss older nonces. Use a paginated loop with
first/skipand GraphQL variables; keep addresses lowercased. This was flagged earlier; re-iterating with a concrete patch.-const getNonceFrom = async (chainId: number, inbox: string, nonce: number, msgSender: string) => { - const subgraph = process.env.RELAYER_SUBGRAPH; - - const result = (await request( - `https://api.studio.thegraph.com/query/${subgraph}`, - `{ - messageSents( - first: 1000, - where: { - inbox: "${inbox}", - nonce_gte: ${nonce}, - msgSender_: {id: "${msgSender.toLowerCase()}"} - }, - orderBy: nonce, - orderDirection: asc - ) { - nonce - } - }` - )) as MessageSentsResponse; - - return result[`messageSents`].map((a: { nonce: string | number }) => Number(a.nonce)); -}; +const getNonceFrom = async (_chainId: number, inbox: string, nonce: number, msgSender: string) => { + const subgraph = process.env.RELAYER_SUBGRAPH; + if (!subgraph) throw new Error("Missing RELAYER_SUBGRAPH"); + const endpoint = `https://api.studio.thegraph.com/query/${subgraph}`; + const query = gql` + query MsgsFrom($inbox: String!, $nonce: BigInt!, $sender: String!, $first: Int!, $skip: Int!) { + messageSents( + first: $first + skip: $skip + where: { inbox: $inbox, nonce_gte: $nonce, msgSender_: { id: $sender } } + orderBy: nonce + orderDirection: asc + ) { + nonce + } + } + `; + const first = 1000; + let skip = 0; + const acc: number[] = []; + for (;;) { + const vars = { inbox: inbox.toLowerCase(), nonce, sender: msgSender.toLowerCase(), first, skip }; + const page = (await request(endpoint, query, vars)) as MessageSentsResponse; + const items = page.messageSents; + acc.push(...items.map((a) => Number(a.nonce))); + if (items.length < first) break; + skip += first; + } + return acc; +};
🧹 Nitpick comments (7)
relayer-cli/src/utils/graphQueries.ts (1)
31-47: Remove or underscore unused parameter; add ENV guard and variables in getCount.
chainIdis unused; mark as_chainIdto satisfy TS and future use. Also guardRELAYER_SUBGRAPHand use variables.-const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, chainId: number): Promise<number> => { - const subgraph = process.env.RELAYER_SUBGRAPH; +const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, _chainId: number): Promise<number> => { + const subgraph = process.env.RELAYER_SUBGRAPH; + if (!subgraph) throw new Error("Missing RELAYER_SUBGRAPH"); const stateRoot = await veaOutbox.stateRoot(); - const result = (await request( - `https://api.studio.thegraph.com/query/${subgraph}`, - `{ - snapshotSaveds(first: 1, where: { stateRoot: "${stateRoot}" }) { - count - } - }` - )) as SnapshotResponse; + const endpoint = `https://api.studio.thegraph.com/query/${subgraph}`; + const query = gql` + query SnapByRoot($stateRoot: Bytes!) { + snapshotSaveds(first: 1, where: { stateRoot: $stateRoot }) { + count + } + } + `; + const result = (await request(endpoint, query, { stateRoot })) as SnapshotResponse; - if (result["snapshotSaveds"].length == 0) return 0; + if (result.snapshotSaveds.length === 0) return 0; - return Number(result["snapshotSaveds"][0].count); + return Number(result.snapshotSaveds[0].count); };relayer-cli/src/utils/hashi.ts (6)
11-11: Hardcoded SOURCE_CHAIN_ID OK for current scope; add TODO.Accepted per project context (Arbitrum Sepolia only). Add a reminder for future multi-route support.
-const SOURCE_CHAIN_ID = 421614; // Arbitrum Sepolia +const SOURCE_CHAIN_ID = 421614; // Arbitrum Sepolia +// TODO: Make source chain id configurable when enabling non-Arbitrum routes.Based on learnings.
36-61: Reuse provider and log interface across the loop to cut overhead.Currently a new provider/interface can be constructed per nonce (via defaults). Build once and pass in.
async function runHashiExecutor({ @@ const bridgeConfig = fetchBridgeConfig(chainId); const { veaContracts, rpcInbox } = bridgeConfig; const veaInboxAddress = veaContracts[network].veaInbox.address; const privateKey = process.env.PRIVATE_KEY; const veaInbox = fetchVeaInbox(veaInboxAddress, privateKey, rpcInbox, chainId); + const providerInbox = new JsonRpcProvider(rpcInbox); + const logIFace = new Interface(messageDispatchedAbi); const inboxCount = await veaInbox.count(); @@ - const toExecute = await isMessageExecutable({ chainId, nonce, veaInboxAddress, rpcInbox }); + const toExecute = await isMessageExecutable({ + chainId, + nonce, + veaInboxAddress, + rpcInbox, + provider: providerInbox, + logIFace, + });
80-86: Optional: inject signer/private key for testability and clearer failures.Keep env handling upstream if desired, but allowing an injected
signer(orprivateKeyparam) decouples this util from process.env.- const provider = new JsonRpcProvider(rpcOutbox); - const signer = new Wallet(process.env.PRIVATE_KEY!, provider); + const provider = new JsonRpcProvider(rpcOutbox); + // Consider: accept a signer/privateKey param; fallback to env for convenience. + const signer = new Wallet(process.env.PRIVATE_KEY!, provider);Based on learnings.
162-176: Consider early-config checks for Yaho address; clearer error if misconfigured.Fail-fast on missing
yahoAddressbefore decoding to improve diagnostics; optional per your fail-fast stance on hashes.const hashes = await fetchVeaMsgTrnx(nonce, veaInboxAddress); const bridgeConfig = fetchBridgeConfig(chainId); - const yahoAddress = bridgeConfig.yahoAddress?.toLowerCase(); + const yahoAddress = bridgeConfig.yahoAddress?.toLowerCase(); + if (!yahoAddress) throw new Error("Missing yahoAddress in bridge config");
211-240: Message ID domain/dispatcher selection — defer change, track via TODO.Using
SOURCE_CHAIN_IDandbridgeConfig(message.targetChainId)is acceptable for current Arbitrum-only routes. Add a TODO to derive domain + dispatcher from the source chain config when expanding routes.- const domain = BigInt(SOURCE_CHAIN_ID); // uint256 - const id = getHashiMsgId(SOURCE_CHAIN_ID, bridgeConfig.yahoAddress!, message); // bytes32 + const domain = BigInt(SOURCE_CHAIN_ID); // uint256 + const id = getHashiMsgId(SOURCE_CHAIN_ID, bridgeConfig.yahoAddress!, message); // bytes32 + // TODO: When adding more routes, compute ID with the source chain's dispatcher and chain id.Based on learnings.
242-242: Typo in exported function name; add alias to ease future rename.Function is spelled
toExecuteMesssage. Consider exporting an alias now to avoid breakage later.-export { executeBatchOnHashi, runHashiExecutor, toExecuteMesssage }; +export { executeBatchOnHashi, runHashiExecutor, toExecuteMesssage }; +// Back-compat alias; safe to remove after repo-wide rename. +export { toExecuteMesssage as toExecuteMessage };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
relayer-cli/src/utils/graphQueries.ts(1 hunks)relayer-cli/src/utils/hashi.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-10-27T13:45:23.401Z
Learnt from: mani99brar
PR: kleros/vea#434
File: relayer-cli/src/utils/hashi.ts:206-235
Timestamp: 2025-10-27T13:45:23.401Z
Learning: The Hashi executor in relayer-cli currently only supports routes from Arbitrum Sepolia (chain ID 421614), so the hardcoded SOURCE_CHAIN_ID constant in relayer-cli/src/utils/hashi.ts is acceptable for the current scope. This will be updated when support for other source chains is added.
Applied to files:
relayer-cli/src/utils/hashi.ts
📚 Learning: 2024-12-09T10:18:00.133Z
Learnt from: mani99brar
PR: kleros/vea#370
File: bridger-cli/src/utils/ethers.ts:20-82
Timestamp: 2024-12-09T10:18:00.133Z
Learning: In `bridger-cli/src/utils/ethers.ts`, prefer to keep the existing `chainId` checks without refactoring to avoid overcomplicating the code with additional conditional logic.
Applied to files:
relayer-cli/src/utils/hashi.ts
📚 Learning: 2024-11-19T10:25:55.588Z
Learnt from: fcanela
PR: kleros/vea#344
File: validator-cli/src/utils/arbMsgExecutor.ts:14-14
Timestamp: 2024-11-19T10:25:55.588Z
Learning: In the `validator-cli/src/utils/arbMsgExecutor.ts` file, checks for commonly required environment variables like `PRIVATE_KEY` should be performed earlier in the application lifecycle, before individual functions are executed.
Applied to files:
relayer-cli/src/utils/hashi.ts
📚 Learning: 2024-11-20T11:50:12.303Z
Learnt from: madhurMongia
PR: kleros/vea#359
File: validator-cli/src/utils/arbMsgExecutor.ts:13-13
Timestamp: 2024-11-20T11:50:12.303Z
Learning: In the `validator-cli` codebase, when initializing `Wallet` instances in TypeScript, it's acceptable to rely on the `Wallet` class's built-in validation of the private key retrieved from environment variables, without adding explicit checks for the existence of `PRIVATE_KEY`.
Applied to files:
relayer-cli/src/utils/hashi.ts
📚 Learning: 2025-10-27T13:46:03.446Z
Learnt from: mani99brar
PR: kleros/vea#434
File: relayer-cli/src/utils/hashi.ts:80-86
Timestamp: 2025-10-27T13:46:03.446Z
Learning: In the relayer-cli codebase, when initializing Wallet instances, it's acceptable to rely on the Wallet class's built-in validation of the private key retrieved from environment variables, without adding explicit checks for the existence of PRIVATE_KEY.
Applied to files:
relayer-cli/src/utils/hashi.ts
📚 Learning: 2024-12-09T09:40:28.400Z
Learnt from: mani99brar
PR: kleros/vea#370
File: bridger-cli/src/utils/transactionHandler.ts:13-13
Timestamp: 2024-12-09T09:40:28.400Z
Learning: In `bridger-cli/src/utils/transactionHandler.ts`, the `veaOutbox` implementations differ for each chain, but a common interface should be defined for type checks to enhance type safety.
Applied to files:
relayer-cli/src/utils/hashi.tsrelayer-cli/src/utils/graphQueries.ts
📚 Learning: 2024-12-09T09:04:04.819Z
Learnt from: mani99brar
PR: kleros/vea#370
File: bridger-cli/src/utils/graphQueries.ts:36-58
Timestamp: 2024-12-09T09:04:04.819Z
Learning: In `bridger-cli/src/utils/graphQueries.ts` (TypeScript), the `getVerificationStatus` function is no longer needed and has been removed.
Applied to files:
relayer-cli/src/utils/graphQueries.ts
🧬 Code graph analysis (1)
relayer-cli/src/utils/hashi.ts (3)
relayer-cli/src/utils/hashiHelpers/hashiTypes.ts (2)
VeaNonceToHashiMessage(12-16)HashiMessage(1-10)relayer-cli/src/utils/hashiHelpers/abi.ts (3)
YaruAbi(13-342)messageDispatchedAbi(5-7)thresholdViewAbi(9-11)relayer-cli/src/utils/hashiHelpers/hashiMsgUtils.ts (1)
getHashiMsgId(11-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: test
- GitHub Check: Analyze (javascript)
- GitHub Check: dependency-review
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
relayer-cli/src/utils/hashi.test.ts (2)
1-231: Verify function name typo in hashi.ts.The past review flagged a typo
toExecuteMesssage(triple 's') inhashi.tsat lines 20, 43, 157, and 237. While this test file's imports use the correct spelling, ensure the actual function definition and all its references inhashi.tshave been corrected totoExecuteMessage.Run the following script to check for any remaining instances of the typo:
#!/bin/bash # Description: Search for the typo 'toExecuteMesssage' (triple 's') in hashi.ts and related files # Search for the typo with triple 's' echo "Checking for typo 'toExecuteMesssage' (triple 's'):" rg -n 'toExecuteMesssage' relayer-cli/src/utils/ # Verify correct spelling exists echo -e "\nVerifying correct spelling 'toExecuteMessage':" rg -n 'toExecuteMessage' relayer-cli/src/utils/hashi.ts -C 1 | head -20
101-117: Type mismatch: HashiMessage numeric fields should be bigint.Line 116 expects
hashiMessage.nonceto beBigInt(1), but theHashiMessageinterface definesnonce(along withtargetChainIdandthreshold) asnumber. This creates a type mismatch since:
- Decoded event logs return
BigIntvalues (lines 85-87)- Test assertions expect
BigInt(line 116)- But the type definition specifies
numberUpdate
hashiTypes.tsto define these three fields asbigintto align with runtime behavior and eliminate type inconsistencies.
🧹 Nitpick comments (1)
relayer-cli/src/utils/hashi.test.ts (1)
6-14: Extract MockEmitter to shared test utilities.This class duplicates the identical
MockEmitterfromvalidator-cli/src/utils/emitter.ts. To avoid code duplication and maintain consistency across test files, extract it to a shared test utilities module (e.g.,test-utils/mockEmitter.ts) that bothvalidator-cliandrelayer-clican import.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
relayer-cli/src/utils/hashi.test.ts(1 hunks)relayer-cli/src/utils/hashi.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- relayer-cli/src/utils/hashi.ts
🧰 Additional context used
🧬 Code graph analysis (1)
relayer-cli/src/utils/hashi.test.ts (2)
validator-cli/src/utils/emitter.ts (1)
MockEmitter(6-14)relayer-cli/src/utils/hashiHelpers/hashiTypes.ts (2)
HashiMessage(1-10)VeaNonceToHashiMessage(12-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript)
- GitHub Check: dependency-review
🔇 Additional comments (1)
relayer-cli/src/utils/hashi.test.ts (1)
82-95: LGTM: Mock event log correctly uses BigInt.The mock properly simulates decoded event logs by returning
BigIntvalues for numeric fields (nonce,targetChainId,threshold), which matches actual blockchain event decoding behavior.
Added Hashi Executor in
relayer-cliWhen enabled the hashi executor in
relayer-cliwill run from the currenthashiNonce, check for hashi message execution and execute messages in batches.New files added:
hashi.tshas main hashi logic for execution and message verification functionshashiHelpersinlcudes hashi abis, types and helper functions for message construction.graphQuiers.tsrefactored all the qraph queries in this file except forproof.tsFiles updated:
relayerHelpers.tsaddedhashiNonce(represents vea nonce for a msg) which is stored in the state file..envadded newHASHI_EXECUTOR_ENABLEDflag which will run the hashi executorrelayer.tsadded the hashi executorPR-Codex overview
This PR focuses on implementing and enabling a new
Hashi Executorfeature, which enhances message processing in therelayer-cliby managing message execution through theHashiprotocol, including updates to state management and logging.Detailed summary
hashiNonceto the state management indevnet_11155111.json.HASHI_EXECUTOR_ENABLEDin.env.dist.botEvents.tsforHashi.frominrelay.test.ts.msgSenderinproof.ts.HashiMessageinterface and related types inhashiTypes.ts.Hashiexecution inlogger.ts.hashiNonceinrelayerHelpers.ts.updateStateFileto savehashiNonce.hashiHelpersfor message hashing and execution.runHashiExecutorto process messages viaHashi.relayfunctions to accommodate new message parameters.Hashimessage execution logic inhashi.test.ts.Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores