Skip to content

Conversation

@unknownunknown1
Copy link
Contributor

@unknownunknown1 unknownunknown1 commented Nov 5, 2025

Summary by CodeRabbit

  • New Features

    • Added an OP Stack relay task and new relay scripts; added Base Sepolia as a new testnet target.
  • Documentation

    • Added a comprehensive L2→L1 bridging guide covering Gnosis, Polygon, zkSync, Arbitrum, and Optimism with step-by-step procedures.
  • Refactor

    • Updated cross-chain proof/relay flows and integrations for Arbitrum and zkSync.
  • Chores

    • Added ALCHEMY_API_KEY to env example, expanded network/verification config, updated package scripts/dependencies, and added Base Sepolia deployment/metaevidence entries.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Walkthrough

Adds BaseSepolia network support, Alchemy/Etherscan configuration, new relay tooling (Hardhat task relay-op), viem/ethers5 updates, zksync proof script changes, bridging docs, metaevidence & deployment artifacts for baseSepolia, and related scripts/package updates.

Changes

Cohort / File(s) Summary
Env template
contracts/.env.example
Added ALCHEMY_API_KEY=''.
Hardhat config
contracts/hardhat.config.ts, contracts/hardhat.config.zksync.ts
Imported ./tasks/relay-op; added Base/BaseSepolia networks and companionNetworks; switched several RPCs to Alchemy; added top-level etherscan with apiKey mappings and customChains; removed a commented import in zksync config.
Package & scripts
contracts/package.json
Added relay and deploy scripts (relay:staging, relay:production, relay-op:*, deploy:baseSepolia*, metaevidence:baseSepolia); bumped @arbitrum/sdk to ^4.0.4; added viem and ethers5 alias.
Documentation
contracts/docs/Bridging.md
New L2→L1 bridging guide covering multiple chains with commands, endpoints, and notes.
ZKSync proof script
contracts/scripts/execute_proof.js
Replaced zksync-web3 with zksync-ethers + ethers; swapped provider roles; replaced getL1MessageProofgetLogProof and proveL1MessageInclusionproveL2MessageInclusion; updated ABIs, providers, and logging.
Event helper
contracts/scripts/get_event_properties.js
Replaced ethers.utils.idethers.id and ethers.utils.defaultAbiCoderethers.AbiCoder.defaultAbiCoder.
Chains env
contracts/scripts/chains.env
Added baseSepolia to testnet arrays and explorer mappings.
Hardhat tasks
contracts/tasks/relay-op.js, contracts/tasks/relay-arbitrum.js
New relay-op Hardhat task (Viem clients, proof/prove/finalize flow for OP Stack); updated Arbitrum task to Arbitrum SDK v4 names and ethers5, renamed to relay-arbitrum.
Deploy configs & artifacts
contracts/deploy/home/optimism.js, contracts/deploy/foreign/optimism.js, contracts/deployments/baseSepolia/*, contracts/metaevidence-cids.json
Added baseSepolia entries in home/foreign parameters; new deployment artifact and .chainId for baseSepolia; added metaevidence CID mapping for baseSepolia.
Misc
contracts/hardhat.config.zksync.ts
Removed commented import line.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HTask as Hardhat Task (relay-op)
    participant L2 as L2 RPC / Public Client
    participant L1 as L1 RPC / Wallet Client

    User->>HTask: run relay-op --txhash <tx>
    HTask->>L2: fetchTransactionReceipt(tx)
    L2-->>HTask: l2Receipt

    alt waiting-to-prove
        HTask->>HTask: waitToProve(poll)
        Note right of HTask: polls until proof available
    else ready-to-prove
        HTask->>L2: buildProveWithdrawal(l2Receipt)
        L2-->>HTask: proof
        HTask->>L1: proveWithdrawal(proof)
        L1-->>HTask: proofSubmitted
    end

    alt ready-to-finalize
        HTask->>L1: finalizeWithdrawal()
        L1-->>HTask: finalized
    else waiting-to-finalize
        HTask->>HTask: waitForFinalizationWindow()
    end

    HTask-->>User: log final status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus review on:
    • contracts/tasks/relay-op.js — new cross-chain state machine, Viem client usage, proof construction/submission.
    • contracts/scripts/execute_proof.js — provider swaps, proof retrieval (getLogProof) and ABI/provider correctness.
    • contracts/hardhat.config.ts & contracts/package.json — network/etherscan config and dependency changes (@arbitrum/sdk, viem, ethers5 alias).

Possibly related PRs

Suggested reviewers

  • jaybuidl
  • salgozino

Poem

🐇 I tunneled through configs, proofs, and chain,
Swapped providers, stitched a Sepolia lane.
Viem hops, ethers5 twirls, Alchemy lights the way—
BaseSepolia now wakes to bridge the day. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'docs: L2 bridging doc' is vague and generic, using non-descriptive terms that don't clearly convey the specific changes beyond mentioning documentation and bridging. Revise the title to be more specific about the changes, such as 'docs: Add L2-to-L1 bridging manual and configuration updates' or similar to better reflect the comprehensive nature of the changes including new bridging documentation, environment configuration, network setup, and relay tasks.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch docs/bridging-doc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/scripts/get_event_properties.js (1)

29-44: Restore ethers v5-compatible helpers.
ethers.id and ethers.AbiCoder.defaultAbiCoder() only exist in ethers v6. The repo still runs on ethers v5 (e.g., contracts/tasks/relay-arbitrum.js requires "ethers5" and the docs stress the v5 requirement), so these calls will throw at runtime, breaking selector derivation and encoding. Please keep using the v5 ethers.utils helpers until the toolchain is upgraded.

Apply this diff to fix the regression:

   const topicHash = "0x3a36e47291f4201faf137fab081d92295bce2d53be2c6ca68ba82c7faa9ce241"; // L1MessageSent
   const eventLogs = receipt.logs.filter((log) => log.topics[0] === topicHash);
@@
 function getFunctionSelector(functionSignature) {
-  const hash = ethers.id(functionSignature);
+  const hash = ethers.utils.id(functionSignature);
   const selector = hash.slice(0, 10); // 0x + first 4 bytes
 
   return selector;
 }
@@
-    return ethers.AbiCoder.defaultAbiCoder().encode([param.type], [param.value]).slice(2);
+    return ethers.utils.defaultAbiCoder.encode([param.type], [param.value]).slice(2);
   });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ab8242 and d66b925.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • contracts/.env.example (1 hunks)
  • contracts/docs/Bridging.md (1 hunks)
  • contracts/hardhat.config.ts (6 hunks)
  • contracts/hardhat.config.zksync.ts (0 hunks)
  • contracts/package.json (3 hunks)
  • contracts/scripts/execute_proof.js (5 hunks)
  • contracts/scripts/get_event_properties.js (2 hunks)
  • contracts/tasks/relay-arbitrum.js (3 hunks)
  • contracts/tasks/relay-op.js (1 hunks)
💤 Files with no reviewable changes (1)
  • contracts/hardhat.config.zksync.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-03-24T14:46:25.118Z
Learnt from: jaybuidl
Repo: kleros/cross-chain-realitio-proxy PR: 68
File: dynamic-script/package.json:2-4
Timestamp: 2025-03-24T14:46:25.118Z
Learning: The kleros/cross-chain-realitio-dynamic-script package is distributed via IPFS CID, not through NPM, so package name changes don't create breaking changes for consumers.

Applied to files:

  • contracts/package.json
🧬 Code graph analysis (4)
contracts/tasks/relay-op.js (3)
contracts/scripts/execute_proof.js (4)
  • require (3-3)
  • require (4-4)
  • hre (1-1)
  • chainId (16-16)
contracts/tasks/relay-arbitrum.js (5)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • chainId (25-25)
  • receipt (43-43)
contracts/scripts/get_event_properties.js (2)
  • receipt (2-2)
  • receipt (57-57)
contracts/scripts/get_event_properties.js (1)
contracts/scripts/execute_proof.js (1)
  • ethers (2-2)
contracts/tasks/relay-arbitrum.js (3)
contracts/scripts/execute_proof.js (5)
  • require (3-3)
  • require (4-4)
  • l2Receipt (45-45)
  • l2Receipt (49-49)
  • l2Provider (19-19)
contracts/tasks/relay-op.js (7)
  • require (1-1)
  • require (3-7)
  • require (9-9)
  • require (11-16)
  • require (17-17)
  • receipt (69-69)
  • l1Wallet (52-55)
contracts/scripts/get_event_properties.js (2)
  • receipt (2-2)
  • receipt (57-57)
contracts/scripts/execute_proof.js (4)
contracts/tasks/relay-arbitrum.js (8)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • foreignNetworks (21-24)
  • chainId (25-25)
  • l1Provider (28-28)
  • l2Provider (29-29)
  • l2Receipt (44-44)
contracts/tasks/relay-op.js (6)
  • require (1-1)
  • require (3-7)
  • require (9-9)
  • require (11-16)
  • require (17-17)
  • chainId (39-39)
contracts/tasks/update-deployments.js (4)
  • require (1-1)
  • require (3-3)
  • homeProxyContract (84-84)
  • foreignProxyContract (143-143)
contracts/deploy/home/polygon.js (1)
  • homeProxy (31-31)
🪛 dotenv-linter (4.0.0)
contracts/.env.example

[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 2-2: [UnorderedKey] The ALCHEMY_API_KEY key should go before the PRIVATE_KEY key

(UnorderedKey)

🪛 markdownlint-cli2 (0.18.1)
contracts/docs/Bridging.md

9-9: Bare URL used

(MD034, no-bare-urls)


24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


33-33: Bare URL used

(MD034, no-bare-urls)


40-40: Bare URL used

(MD034, no-bare-urls)


53-53: Bare URL used

(MD034, no-bare-urls)


62-62: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


73-73: Bare URL used

(MD034, no-bare-urls)


74-74: Bare URL used

(MD034, no-bare-urls)


80-80: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


97-97: Bare URL used

(MD034, no-bare-urls)

Comment on lines 27 to 33
const blockNumber = l1MessageSentEvent.blockNumber;
const homeProxy = `0x${BigInt(l1MessageSentEvent.address).toString(16)}`;
const msgHash = l1MessageSentEvent.msgHash;
const eventData = await getCalldata(txHash, l1Provider);
const homeProxyContract = new ethers.Contract(homeProxy, RealitioHomeArbitrationProxy.abi, l1Provider);
const eventData = await getCalldata(txHash, l2Provider);
const homeProxyContract = new ethers.Contract(homeProxy, RealitioHomeArbitrationProxy.abi, l2Provider);
console.log(await homeProxyContract.foreignProxy());
const foreignProxyContract = new ethers.Contract(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preserve the proxy address formatting

Casting l1MessageSentEvent.address to BigInt and back removes any leading zeros, so ethers.Contract will reject addresses whose high nibble is 0x0. Use the address string directly (normalized with ethers.getAddress) to avoid intermittent failures.

Apply this diff:

-  const homeProxy = `0x${BigInt(l1MessageSentEvent.address).toString(16)}`;
+  const homeProxy = ethers.getAddress(l1MessageSentEvent.address);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const blockNumber = l1MessageSentEvent.blockNumber;
const homeProxy = `0x${BigInt(l1MessageSentEvent.address).toString(16)}`;
const msgHash = l1MessageSentEvent.msgHash;
const eventData = await getCalldata(txHash, l1Provider);
const homeProxyContract = new ethers.Contract(homeProxy, RealitioHomeArbitrationProxy.abi, l1Provider);
const eventData = await getCalldata(txHash, l2Provider);
const homeProxyContract = new ethers.Contract(homeProxy, RealitioHomeArbitrationProxy.abi, l2Provider);
console.log(await homeProxyContract.foreignProxy());
const foreignProxyContract = new ethers.Contract(
const blockNumber = l1MessageSentEvent.blockNumber;
const homeProxy = ethers.getAddress(l1MessageSentEvent.address);
const msgHash = l1MessageSentEvent.msgHash;
const eventData = await getCalldata(txHash, l2Provider);
const homeProxyContract = new ethers.Contract(homeProxy, RealitioHomeArbitrationProxy.abi, l2Provider);
console.log(await homeProxyContract.foreignProxy());
const foreignProxyContract = new ethers.Contract(
🤖 Prompt for AI Agents
In contracts/scripts/execute_proof.js around lines 27 to 33, the code converts
l1MessageSentEvent.address to BigInt and back which strips leading zeros and can
create invalid addresses for ethers.Contract; replace that conversion with the
original address string normalized via ethers.getAddress (or ensure it already
starts with "0x" and pass ethers.getAddress(l1MessageSentEvent.address)) so the
resulting homeProxy preserves leading zeros and is a checksummed valid address
for the Contract constructor.

Comment on lines 52 to 112
const l1Wallet = createWalletClient({
account,
transport: http(l1RpcUrl),
}).extend(walletActionsL1());

// L2 clients
const l2Public = createPublicClient({
chain: chain.l2,
transport: http(l2RpcUrl),
}).extend(publicActionsL2());

const l2Wallet = createWalletClient({
account,
transport: http(l2RpcUrl),
}).extend(walletActionsL2());

// Retrieve L2 tx
const receipt = await l2Public.getTransactionReceipt({ hash: txhash });
const status = await l1Public.getWithdrawalStatus({
receipt,
targetChain: l2Public.chain,
});

console.log(`Withdrawal status: ${status}`);

const { output, withdrawal } = await l1Public.waitToProve({
receipt,
targetChain: l2Public.chain,
});

// Only prove if necessary
// Note that proving the message resets the 1 week timeout each time, so this condition is mandatory.
if (status === "waiting-to-prove" || status === "ready-to-prove") {
console.log("Proving withdrawal...");

const proveArgs = await l2Public.buildProveWithdrawal({
account,
output,
withdrawal
});

// Note that proof cant be obtained with Infura RPC
await l1Wallet.proveWithdrawal(proveArgs);
console.log("Proven ✅");
}

if (status === "ready-to-finalize") {
// Not required by this script but keep in case bots need it
/*
console.log("Waiting until message becomes relayable...");
await l1Public.waitToFinalize({
targetChain: l2Public.chain,
withdrawalHash: receipt.transactionHash
});*/

console.log("Finalizing withdrawal...");
await l1Wallet.finalizeWithdrawal({
targetChain: l2Wallet.chain,
withdrawal,
});
console.log("Done ✅");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Set wallet clients' chains to avoid undefined targetChain.
Both wallet clients are created without a chain, so l2Wallet.chain is undefined. When you reach the finalizeWithdrawal branch, targetChain: l2Wallet.chain ends up undefined and viem will reject the call at runtime. Provide the chain upfront (and reuse chain.l2 directly when finalizing) so the task can actually submit the finalization.

Apply this diff to fix the issue:

     const l1Public = createPublicClient({
       chain: chain.l1,
       transport: http(l1RpcUrl),
     }).extend(publicActionsL1());
 
     const l1Wallet = createWalletClient({
       account,
+      chain: chain.l1,
       transport: http(l1RpcUrl),
     }).extend(walletActionsL1());
 
     // L2 clients
     const l2Public = createPublicClient({
       chain: chain.l2,
       transport: http(l2RpcUrl),
     }).extend(publicActionsL2());
 
     const l2Wallet = createWalletClient({
       account,
+      chain: chain.l2,
       transport: http(l2RpcUrl),
     }).extend(walletActionsL2());
@@
       console.log("Finalizing withdrawal...");
       await l1Wallet.finalizeWithdrawal({
-        targetChain: l2Wallet.chain,
+        targetChain: chain.l2,
         withdrawal,
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const l1Wallet = createWalletClient({
account,
transport: http(l1RpcUrl),
}).extend(walletActionsL1());
// L2 clients
const l2Public = createPublicClient({
chain: chain.l2,
transport: http(l2RpcUrl),
}).extend(publicActionsL2());
const l2Wallet = createWalletClient({
account,
transport: http(l2RpcUrl),
}).extend(walletActionsL2());
// Retrieve L2 tx
const receipt = await l2Public.getTransactionReceipt({ hash: txhash });
const status = await l1Public.getWithdrawalStatus({
receipt,
targetChain: l2Public.chain,
});
console.log(`Withdrawal status: ${status}`);
const { output, withdrawal } = await l1Public.waitToProve({
receipt,
targetChain: l2Public.chain,
});
// Only prove if necessary
// Note that proving the message resets the 1 week timeout each time, so this condition is mandatory.
if (status === "waiting-to-prove" || status === "ready-to-prove") {
console.log("Proving withdrawal...");
const proveArgs = await l2Public.buildProveWithdrawal({
account,
output,
withdrawal
});
// Note that proof cant be obtained with Infura RPC
await l1Wallet.proveWithdrawal(proveArgs);
console.log("Proven ✅");
}
if (status === "ready-to-finalize") {
// Not required by this script but keep in case bots need it
/*
console.log("Waiting until message becomes relayable...");
await l1Public.waitToFinalize({
targetChain: l2Public.chain,
withdrawalHash: receipt.transactionHash
});*/
console.log("Finalizing withdrawal...");
await l1Wallet.finalizeWithdrawal({
targetChain: l2Wallet.chain,
withdrawal,
});
console.log("Done ✅");
const l1Wallet = createWalletClient({
account,
chain: chain.l1,
transport: http(l1RpcUrl),
}).extend(walletActionsL1());
// L2 clients
const l2Public = createPublicClient({
chain: chain.l2,
transport: http(l2RpcUrl),
}).extend(publicActionsL2());
const l2Wallet = createWalletClient({
account,
chain: chain.l2,
transport: http(l2RpcUrl),
}).extend(walletActionsL2());
// Retrieve L2 tx
const receipt = await l2Public.getTransactionReceipt({ hash: txhash });
const status = await l1Public.getWithdrawalStatus({
receipt,
targetChain: l2Public.chain,
});
console.log(`Withdrawal status: ${status}`);
const { output, withdrawal } = await l1Public.waitToProve({
receipt,
targetChain: l2Public.chain,
});
// Only prove if necessary
// Note that proving the message resets the 1 week timeout each time, so this condition is mandatory.
if (status === "waiting-to-prove" || status === "ready-to-prove") {
console.log("Proving withdrawal...");
const proveArgs = await l2Public.buildProveWithdrawal({
account,
output,
withdrawal
});
// Note that proof cant be obtained with Infura RPC
await l1Wallet.proveWithdrawal(proveArgs);
console.log("Proven ✅");
}
if (status === "ready-to-finalize") {
// Not required by this script but keep in case bots need it
/*
console.log("Waiting until message becomes relayable...");
await l1Public.waitToFinalize({
targetChain: l2Public.chain,
withdrawalHash: receipt.transactionHash
});*/
console.log("Finalizing withdrawal...");
await l1Wallet.finalizeWithdrawal({
targetChain: chain.l2,
withdrawal,
});
console.log("Done ✅");
🤖 Prompt for AI Agents
In contracts/tasks/relay-op.js around lines 52 to 112, both wallet clients are
created without specifying the chain which leaves l2Wallet.chain undefined and
causes finalizeWithdrawal to be called with an undefined targetChain; set the
chain explicitly when creating the wallet clients (use chain.l1 for l1Wallet and
chain.l2 for l2Wallet) or, alternatively, pass chain.l2 directly to
finalizeWithdrawal as targetChain; update the createWalletClient calls to
include chain: chain.l1 and chain: chain.l2 respectively (or replace
targetChain: l2Wallet.chain with targetChain: chain.l2) so the finalization call
receives a valid chain object.

Copy link

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d66b925 and ec06dd4.

📒 Files selected for processing (1)
  • contracts/hardhat.config.ts (6 hunks)
🔇 Additional comments (4)
contracts/hardhat.config.ts (4)

7-7: LGTM!

The relay-op task import follows the established pattern and enables the new relay functionality for OP Stack chains.


272-289: LGTM! Past issue resolved.

The customChains configuration is correct. Both base and polygon entries now have proper browserURL values pointing to their respective block explorers. The previous issue with Polygon's browserURL incorrectly pointing to Base's Blockscout has been resolved.


88-88: ALCHEMY_API_KEY is already documented in setup instructions.

Verification confirms that ALCHEMY_API_KEY is documented in contracts/docs/Bridging.md under the Requirements section, satisfying the concern raised in the review comment.


153-153: Etherscan v2 API configuration is correct and compatible.

The v2 API URLs with chainid parameters are properly configured:

  • Mainnet: chainid=1 matches network chainId: 1
  • Polygon: chainid=137 matches network chainId: 137
  • Other networks follow the same pattern consistently

Etherscan V2 requires a chainid parameter for multichain calls and is supported by Hardhat's verification tooling. The implementation matches the official format and no changes are required.

Comment on lines 265 to 271
apiKey: {
// These are separate from Ethereum's etherscan API key
optimisticEthereum: process.env.OPTIMISM_API_KEY!,
mainnet: process.env.ETHERSCAN_API_KEY!,
polygon: process.env.ETHERSCAN_API_KEY!,
base: process.env.ETHERSCAN_API_KEY!
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use optional chaining or default values instead of non-null assertions.

The non-null assertion operator (!) on lines 267-270 will throw runtime errors if the environment variables are undefined. This can cause Hardhat to fail even for tasks that don't require Etherscan verification.

Consider using optional chaining with fallback values:

   etherscan: {
     apiKey: {
       // These are separate from Ethereum's etherscan API key
-      optimisticEthereum: process.env.OPTIMISM_API_KEY!,
-      mainnet: process.env.ETHERSCAN_API_KEY!,
-      polygon: process.env.ETHERSCAN_API_KEY!,
-      base: process.env.ETHERSCAN_API_KEY!
+      optimisticEthereum: process.env.OPTIMISM_API_KEY || "",
+      mainnet: process.env.ETHERSCAN_API_KEY || "",
+      polygon: process.env.ETHERSCAN_API_KEY || "",
+      base: process.env.ETHERSCAN_API_KEY || ""
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
apiKey: {
// These are separate from Ethereum's etherscan API key
optimisticEthereum: process.env.OPTIMISM_API_KEY!,
mainnet: process.env.ETHERSCAN_API_KEY!,
polygon: process.env.ETHERSCAN_API_KEY!,
base: process.env.ETHERSCAN_API_KEY!
},
apiKey: {
// These are separate from Ethereum's etherscan API key
optimisticEthereum: process.env.OPTIMISM_API_KEY || "",
mainnet: process.env.ETHERSCAN_API_KEY || "",
polygon: process.env.ETHERSCAN_API_KEY || "",
base: process.env.ETHERSCAN_API_KEY || ""
},
🤖 Prompt for AI Agents
In contracts/hardhat.config.ts around lines 265 to 271, the apiKey entries use
non-null assertions (process.env.VAR!) which will throw at runtime if those env
vars are missing; replace the non-null assertions with a safe fallback by using
optional chaining or default values (e.g., process.env.OPTIMISM_API_KEY ?? "" or
process.env.OPTIMISM_API_KEY || "") so the config loads even when keys are
undefined, ensuring tasks that don’t need Etherscan won’t fail.

Comment on lines +2 to +4
For Reality Cross-chain proxies L1 → L2 bridging is automatic for every bridge.
However, L2 → L1 bridging requires manual steps, which differ by chain.
Use this guide until bots are configured to handle everything automatically.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@unknownunknown1 this doc is very useful, thanks!

Would be nice if we can have the full cycle and specify all the methods that must be called until the bot is there.

For example, the L1->L2 usually is automatically bridged as you have said, but we need to call handleNotifiedRequest or handleRejectedRequest after receiveArbitrationRequest is automatically called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not much to add to be honest. handleNotifiedRequest is the default function, while handleRejectedRequest is rather edge case. But I will add the steps for relaying the question back to Reality as it's a bit tricky

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (1)
contracts/hardhat.config.ts (1)

276-284: Non-null assertions on environment variables may cause runtime errors.

As noted in a previous review, the non-null assertions (!) on lines 278-283 will throw runtime errors if the environment variables are undefined, causing Hardhat to fail even for tasks that don't require Etherscan verification.

Based on previous review feedback, apply this diff to use safe fallbacks:

   etherscan: {
     apiKey: {
       // These are separate from Ethereum's etherscan API key
-      optimisticEthereum: process.env.OPTIMISM_API_KEY!,
-      mainnet: process.env.ETHERSCAN_API_KEY!,
-      polygon: process.env.ETHERSCAN_API_KEY!,
-      base: process.env.ETHERSCAN_API_KEY!,
-      baseSepolia: process.env.ETHERSCAN_API_KEY!,
-      sepolia: process.env.ETHERSCAN_API_KEY!
+      optimisticEthereum: process.env.OPTIMISM_API_KEY || "",
+      mainnet: process.env.ETHERSCAN_API_KEY || "",
+      polygon: process.env.ETHERSCAN_API_KEY || "",
+      base: process.env.ETHERSCAN_API_KEY || "",
+      baseSepolia: process.env.ETHERSCAN_API_KEY || "",
+      sepolia: process.env.ETHERSCAN_API_KEY || ""
     },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec06dd4 and b7d430d.

📒 Files selected for processing (9)
  • contracts/deploy/foreign/optimism.js (2 hunks)
  • contracts/deploy/home/optimism.js (2 hunks)
  • contracts/deployments/baseSepolia/.chainId (1 hunks)
  • contracts/deployments/baseSepolia/RealitioHomeProxyBase.json (1 hunks)
  • contracts/hardhat.config.ts (8 hunks)
  • contracts/metaevidence-cids.json (1 hunks)
  • contracts/package.json (5 hunks)
  • contracts/scripts/chains.env (1 hunks)
  • contracts/tasks/relay-op.js (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • contracts/deployments/baseSepolia/RealitioHomeProxyBase.json
  • contracts/deployments/baseSepolia/.chainId
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/tasks/relay-op.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-03-24T14:46:25.118Z
Learnt from: jaybuidl
Repo: kleros/cross-chain-realitio-proxy PR: 68
File: dynamic-script/package.json:2-4
Timestamp: 2025-03-24T14:46:25.118Z
Learning: The kleros/cross-chain-realitio-dynamic-script package is distributed via IPFS CID, not through NPM, so package name changes don't create breaking changes for consumers.

Applied to files:

  • contracts/package.json
🧬 Code graph analysis (1)
contracts/deploy/home/optimism.js (5)
contracts/deploy/shared/chains.js (1)
  • homeChains (28-28)
contracts/deploy/home/polygon.js (1)
  • homeChains (4-4)
contracts/deploy/home/arbitrum.js (1)
  • homeChains (3-3)
contracts/deploy/home/gnosis.js (1)
  • homeChains (3-3)
contracts/deploy/home/zksync.js (1)
  • homeChains (4-4)
🔇 Additional comments (11)
contracts/scripts/chains.env (1)

14-14: LGTM! BaseSepolia testnet integration looks good.

The additions follow the established pattern for testnet configurations, with the explorer URL correctly pointing to Base's Sepolia block explorer.

Also applies to: 22-22

contracts/metaevidence-cids.json (1)

16-16: LGTM! Metaevidence CID for baseSepolia added.

The entry follows the same pattern as other network metaevidence mappings.

contracts/package.json (4)

17-25: New relay scripts added for multiple networks.

The relay scripts follow a consistent pattern across staging, production, and various OP-stack networks.


34-34: BaseSepolia deployment and metaevidence scripts added.

The scripts follow the established pattern for other testnet deployments.

Also applies to: 51-53


129-129: viem library is actively used in the codebase and necessary.

viem (v2.38.3) is imported and used across multiple files including sdk/src/lib.ts, sdk/src/contracts.ts, test files, and contracts/tasks/relay-op.js. It provides essential Ethereum client utilities (createPublicClient, createWalletClient, getContract, Transport types) that serve a different purpose than ethers—viem offers low-level client operations while ethers provides higher-level abstractions. The dependency addition is justified.


106-106: Dual ethers versions serve distinct purposes and are actively used.

The ethers5 alias is required by the Arbitrum relay task (contracts/tasks/relay-arbitrum.js), which uses ethers v5 APIs. The main codebase uses ethers v6 with TypeChain integration. This is a legitimate architectural choice for cross-chain operations, not unnecessary bloat.

contracts/hardhat.config.ts (2)

7-7: New relay-op task imported.

This task enables the relay operations for OP-stack networks including baseSepolia.


285-319: Etherscan custom chains configuration looks good.

The custom chains configuration properly defines Base, BaseSepolia, Sepolia, and Polygon networks with their respective Etherscan v2 API endpoints and block explorer URLs.

contracts/deploy/home/optimism.js (1)

3-3: BaseSepolia added to home chains.

The destructuring correctly includes baseSepolia alongside other OP-stack networks.

contracts/deploy/foreign/optimism.js (2)

11-16: BaseSepolia foreign proxy parameters added.

The configuration follows the established pattern for testnet deployments with 1 juror and includes the proper Base Sepolia L1StandardBridge address with documentation reference.


43-43: Documentation URL updated for Base network.

The comment now points to the correct Base documentation URL.

Comment on lines +16 to +19
[baseSepolia.chainId]: {
realitio: "0x86b653Dc9aa6d7311109276925927087573f91CB",
variant: "Base",
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if node_modules exists and examine the reality.eth config
if [ -f "node_modules/@reality.eth/contracts/config.json" ]; then
  echo "=== Reality.eth config found ==="
  cat node_modules/@reality.eth/contracts/config.json | jq '.' || cat node_modules/@reality.eth/contracts/config.json
else
  echo "Config file not found at expected location"
  # Try to find the package
  find . -name "@reality.eth" -type d 2>/dev/null | head -5
fi

Repository: kleros/cross-chain-realitio-proxy

Length of output: 119


🏁 Script executed:

# Examine the optimism.js file to understand the context and pattern
wc -l contracts/deploy/home/optimism.js
head -50 contracts/deploy/home/optimism.js

Repository: kleros/cross-chain-realitio-proxy

Length of output: 2021


🏁 Script executed:

# Search for realityETHConfig usage in the codebase
rg "realityETHConfig" -A 3 -B 1

Repository: kleros/cross-chain-realitio-proxy

Length of output: 7137


🏁 Script executed:

# Verify the baseSepolia chainId and search for any comments about why it's hardcoded
rg "baseSepolia" -A 2 -B 2

Repository: kleros/cross-chain-realitio-proxy

Length of output: 6398


🏁 Script executed:

# Check package.json for @reality.eth/contracts version and any related comments
cat contracts/package.json | jq '.dependencies | select(.["@reality.eth/contracts"])'

Repository: kleros/cross-chain-realitio-proxy

Length of output: 59


🏁 Script executed:

# Search for any comments or documentation about baseSepolia or the hardcoded address
rg "0x86b653Dc9aa6d7311109276925927087573f91CB" -B 5 -A 5

Repository: kleros/cross-chain-realitio-proxy

Length of output: 1856


🏁 Script executed:

# Look for any historical context or TODOs related to baseSepolia Realitio
rg "baseSepolia|Base.*Sepolia" contracts/deploy/home/ -A 3 -B 1

Repository: kleros/cross-chain-realitio-proxy

Length of output: 839


Add documentation for the hardcoded baseSepolia Realitio address.

baseSepolia is the only network in this file using a hardcoded Realitio address (0x86b653Dc9aa6d7311109276925927087573f91CB) instead of realityETHConfig(chainId, "ETH", "3.0").address like unichainSepolia, optimismSepolia, and other networks. The address is confirmed deployed and active, but lacks explanation for why hardcoding is necessary. Add a comment clarifying whether this is due to baseSepolia being unsupported by the @reality.eth/contracts library or if this is an intentional custom deployment.

🤖 Prompt for AI Agents
In contracts/deploy/home/optimism.js around lines 16–19, the baseSepolia entry
uses a hardcoded Realitio address without explanation; add a concise comment
above that object explaining why it's hardcoded (state whether baseSepolia is
unsupported by @reality.eth/contracts, or this is a deliberate custom
deployment), include provenance (transaction/verification link or reference to
how the address was confirmed) and note whether switching to
realityETHConfig(chainId, "ETH", "3.0").address was considered and why it was
not used; ensure the comment is one or two lines and clear for future
maintainers.

optimismSepolia: {
chainId: 11155420,
url: `https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY}`,
url: `https://opt-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if .env.example exists and examine its contents
fd -t f '.env.example' --exec cat {}

Repository: kleros/cross-chain-realitio-proxy

Length of output: 59


🏁 Script executed:

# Also check the hardhat.config.ts file to see the lines in question
wc -l contracts/hardhat.config.ts

Repository: kleros/cross-chain-realitio-proxy

Length of output: 108


🏁 Script executed:

# Read the relevant sections of hardhat.config.ts (lines 85-95, 195-210, 225-240)
sed -n '85,95p;195,210p;225,240p' contracts/hardhat.config.ts | cat -n

Repository: kleros/cross-chain-realitio-proxy

Length of output: 1754


🏁 Script executed:

# Search for any documentation that mentions ALCHEMY_API_KEY
rg 'ALCHEMY_API_KEY' --type-list | head -20

Repository: kleros/cross-chain-realitio-proxy

Length of output: 466


🏁 Script executed:

# Search for ALCHEMY_API_KEY usage in the codebase
rg 'ALCHEMY_API_KEY' --type ts

Repository: kleros/cross-chain-realitio-proxy

Length of output: 1042


🏁 Script executed:

# Get exact lines 201 and 231 with context
sed -n '195,210p' contracts/hardhat.config.ts | cat -n
echo "---"
sed -n '225,235p' contracts/hardhat.config.ts | cat -n

Repository: kleros/cross-chain-realitio-proxy

Length of output: 1066


🏁 Script executed:

# Check for documentation files (README, setup guides, etc.)
fd -t f '(README|readme|SETUP|setup|\.env\.example)' --extension md --extension txt --extension rst

Repository: kleros/cross-chain-realitio-proxy

Length of output: 106


🏁 Script executed:

# Check if any .env files or examples exist
fd -t f -e env

Repository: kleros/cross-chain-realitio-proxy

Length of output: 105


🏁 Script executed:

# Check README.md for ALCHEMY_API_KEY
rg 'ALCHEMY_API_KEY' README.md

Repository: kleros/cross-chain-realitio-proxy

Length of output: 59


🏁 Script executed:

# Check contracts/README.md for ALCHEMY_API_KEY
rg 'ALCHEMY_API_KEY' contracts/README.md

Repository: kleros/cross-chain-realitio-proxy

Length of output: 59


🏁 Script executed:

# Check chains.env content
cat contracts/scripts/chains.env

Repository: kleros/cross-chain-realitio-proxy

Length of output: 1431


🏁 Script executed:

# Look at the root README to understand setup instructions
head -100 README.md

Repository: kleros/cross-chain-realitio-proxy

Length of output: 5536


Add ALCHEMY_API_KEY to documentation and create .env.example.

ALCHEMY_API_KEY is used across multiple networks (optimismSepolia, optimism, base, baseSepolia, gnosis) in hardhat.config.ts and SDK tests, but is not documented in the project's setup instructions or .env guidance. Create a .env.example file with ALCHEMY_API_KEY listed, and update README.md (or contracts/README.md) to mention this requirement in the "Adding support for a new chain" and environment setup sections.

🤖 Prompt for AI Agents
In contracts/hardhat.config.ts around line 89, ALCHEMY_API_KEY is referenced for
network RPCs but is not documented; add ALCHEMY_API_KEY to project setup docs
and provide a .env.example. Update README.md (or contracts/README.md) to mention
ALCHEMY_API_KEY in the "Adding support for a new chain" and environment setup
sections, and create a top-level .env.example file that lists ALCHEMY_API_KEY
(with placeholder value and brief comment) alongside other env vars so
contributors know to set it before running tests or configuring networks.

Comment on lines +103 to +112
baseSepolia: {
chainId: 84532,
url: `https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
// url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
accounts: [process.env.PRIVATE_KEY as string],
tags: ["home"],
companionNetworks: {
foreign: "sepolia",
},
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

BaseSepolia network configuration missing verification settings.

Unlike other networks in the config (e.g., optimismSepolia at lines 96-101, arbitrumSepolia at lines 122-127), the baseSepolia network is missing the verify.etherscan configuration block. This will prevent contract verification on the Base Sepolia explorer.

Apply this diff to add verification settings:

     baseSepolia: {
       chainId: 84532,
       url: `https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
       // url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
       accounts: [process.env.PRIVATE_KEY as string],
       tags: ["home"],
       companionNetworks: {
         foreign: "sepolia",
       },
+      verify: {
+        etherscan: {
+          apiUrl: "https://api-sepolia.basescan.org/api",
+          apiKey: process.env.BASESCAN_API_KEY,
+        },
+      },
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
baseSepolia: {
chainId: 84532,
url: `https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
// url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
accounts: [process.env.PRIVATE_KEY as string],
tags: ["home"],
companionNetworks: {
foreign: "sepolia",
},
},
baseSepolia: {
chainId: 84532,
url: `https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
// url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
accounts: [process.env.PRIVATE_KEY as string],
tags: ["home"],
companionNetworks: {
foreign: "sepolia",
},
verify: {
etherscan: {
apiUrl: "https://api-sepolia.basescan.org/api",
apiKey: process.env.BASESCAN_API_KEY,
},
},
},
🤖 Prompt for AI Agents
In contracts/hardhat.config.ts around lines 103 to 112, the baseSepolia network
is missing the verify.etherscan configuration needed for contract verification;
add a verify block under baseSepolia that configures etherscan verification by
referencing the appropriate API key env var (e.g., process.env.ETHERSCAN_API_KEY
or a BASE-specific key), and include a customChains entry for Base Sepolia with
network name 'base', chainId 84532, and the correct API and browser URLs for the
Base Sepolia explorer so Hardhat can locate the verification endpoints.

baseSepolia: {
chainId: 84532,
url: `https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
// url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix misleading comment for baseSepolia.

The comment references "optimism-sepolia" but this is the baseSepolia network configuration.

Apply this diff:

-      // url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
+      // url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY} --port 8547`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://optimism-sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547`
// url: `http://127.0.0.1:8547`, // fork with `anvil --fork-url https://base-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY} --port 8547`
🤖 Prompt for AI Agents
In contracts/hardhat.config.ts around line 106, the inline comment mistakenly
references "optimism-sepolia" while this is the baseSepolia network config;
update the comment to reference base Sepolia (e.g., use sepolia.infura.io URL)
and adjust the example fork command to `anvil --fork-url
https://sepolia.infura.io/v3/${process.env.INFURA_API_KEY} --port 8547` (or
otherwise state "base Sepolia") so the comment accurately reflects the network
being forked.

"dependencies": {
"@arbitrum/nitro-contracts": "^1.3.0",
"@arbitrum/sdk": "^v3.1.9",
"@arbitrum/sdk": "^4.0.4",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@arbitrum/sdk v4.0.0 breaking changes migration guide

💡 Result:

Here’s a concise migration checklist for @arbitrum/sdk v3 → v4 with the main breaking changes and quick code notes.

  1. Terminology and API renames
  • L1/L2 → parent/child terminology; many types/functions renamed accordingly. Use Parent/Child where appropriate. [1]
  1. Network types & registration
  • L2Network → ArbitrumNetwork; getL2Network → getArbitrumNetwork; l2Networks → getArbitrumNetworks.
  • L1Network, Network, getL1Network, getParentForNetwork removed.
  • Only Arbitrum networks (Arbitrum One, Arbitrum testnets, Orbit chains) are registered; use registerCustomArbitrumNetwork and getArbitrumNetworks / getChildrenForNetwork. [1][2]
  1. Bridger classes updated
  • AssetBridger/Erc20Bridger APIs changed; new bridgers named EthBridger, Erc20Bridger, EthL1L3Bridger, Erc20L1L3Bridger (see updated constructors/usage). Instantiate with an ArbitrumNetwork (not generic Network). [1][2]
  1. Message classes & lifecycle
  • Message classes renamed/reshaped to ParentToChildMessage and ChildToParentMessage; creation/receipt parsing flows updated (constructors and helper methods changed). Review message creation from tx receipts. [1]
  1. Network helper functions
  • New helpers: getArbitrumNetworks, getChildrenForNetwork. Use these instead of the removed getParentForNetwork/getL1Network. [1]
  1. Registration requirement
  • registerCustomArbitrumNetwork must be used for custom Arbitrum chains before using bridgers or message utilities. [1][2]
  1. Code example (v4 pattern)
  • Fetch network: const child = await getArbitrumNetwork(childChainId)
  • Create bridger: const ethBridger = new EthBridger(child)
  • Deposit: await ethBridger.deposit({ amount, parentSigner })
    (See repo docs for full method signatures.) [2]

Where to read full details and examples

  • Official migration doc (v3 → v4) — primary migration guide and detailed breaking-change list. [1]
  • @arbitrum/sdk v4 repository and README (examples, API reference). [2]

References
[1] Arbitrum SDK migration guide (v3 → v4) — docs.arbitrum.io/sdk/migrate.
[2] OffchainLabs/arbitrum-sdk (v4) — GitHub README & repo.


Review @arbitrum/sdk v4 breaking changes before upgrade.

The update from v3.1.9 to v4.0.4 introduces substantial breaking changes:

  • Terminology shift: L1/L2 → parent/child (affects types, functions, and method names)
  • API changes: L2NetworkArbitrumNetwork, getL2NetworkgetArbitrumNetwork, and several removed functions
  • Bridger classes restructured with updated constructors and signatures
  • Message classes renamed: MessageParentToChildMessage/ChildToParentMessage
  • New requirement: registerCustomArbitrumNetwork must be called for custom chains

Ensure all SDK usage in this codebase is updated to match the v4 API. Refer to the migration guide at docs.arbitrum.io/sdk/migrate.

🤖 Prompt for AI Agents
In contracts/package.json around line 123, the dependency bump to
"@arbitrum/sdk": "^4.0.4" may break existing code because v4 renamed types and
methods (L1/L2 → parent/child, L2Network → ArbitrumNetwork, getL2Network →
getArbitrumNetwork), restructured Bridger constructors, renamed Message classes,
and requires registerCustomArbitrumNetwork for custom chains; update all
imports/usages across the codebase to the v4 API (rename
types/functions/classes, adjust constructor/signature calls, replace Message
usages with ParentToChildMessage/ChildToParentMessage, and call
registerCustomArbitrumNetwork where custom chains are used), run the migration
guide at docs.arbitrum.io/sdk/migrate to map each old API to its v4 equivalent,
and run tests to ensure no remaining references to v3 APIs.

Copy link

@coderabbitai coderabbitai bot left a 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 (1)
contracts/tasks/relay-op.js (1)

101-118: Use consistent chain reference for clarity.

Line 112 uses l2Wallet.chain while the rest of the code uses l2Public.chain (lines 75, 82, 106). While both work now that the wallet has a chain, using l2Public.chain or chain.l2 would be more consistent.

Apply this diff for consistency:

       console.log("Finalizing withdrawal...");
       await l1Wallet.finalizeWithdrawal({
-        targetChain: l2Wallet.chain,
+        targetChain: chain.l2,
         withdrawal,
       });
🧹 Nitpick comments (1)
contracts/tasks/relay-op.js (1)

80-99: LGTM!

The proving logic correctly:

  • Waits for the withdrawal to be provable
  • Only proves when status requires it (preventing unnecessary timeout resets)
  • Includes helpful note about Infura RPC limitations

Consider wrapping the RPC calls and proof submission in try-catch blocks for better error reporting, especially since the Infura limitation (line 96) could cause failures.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7d430d and 3cb5b61.

📒 Files selected for processing (1)
  • contracts/tasks/relay-op.js (1 hunks)
🔇 Additional comments (5)
contracts/tasks/relay-op.js (5)

1-17: LGTM!

The imports are appropriate for an OP Stack withdrawal relay task, including all necessary viem modules and chain definitions.


21-29: LGTM!

The task definition and PRIVATE_KEY validation are correct. Proper error handling for missing environment variable and normalization of the key format.


45-69: LGTM!

The client creation is correct. Both wallet clients now include the chain parameter (lines 55 and 67), which addresses the critical issue from the previous review about l2Wallet.chain being undefined.


71-78: LGTM!

Transaction receipt retrieval and withdrawal status check are correct.


120-123: LGTM!

Proper handling of the already-finalized case with early return.

Comment on lines +40 to +43
const chainId = hre.network.config.chainId;
const chain = CHAIN_MAP[chainId];
const l1RpcUrl = hre.config.networks[chain.l1NetworkName].url;
const l2RpcUrl = hre.network.config.url;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add validation for chain configuration lookups.

Lines 41-42 perform lookups without validation:

  • If chainId is not in CHAIN_MAP, chain will be undefined and cause errors when accessing chain.l1 or chain.l2
  • If chain.l1NetworkName doesn't exist in hre.config.networks, the code will throw an unclear error

Add validation after the lookups:

 const chainId = hre.network.config.chainId;
 const chain = CHAIN_MAP[chainId];
+if (!chain) {
+  throw new Error(`Chain ${chainId} not supported. Supported chains: ${Object.keys(CHAIN_MAP).join(", ")}`);
+}
+
 const l1RpcUrl = hre.config.networks[chain.l1NetworkName].url;
+if (!l1RpcUrl) {
+  throw new Error(`L1 network "${chain.l1NetworkName}" not configured in hardhat.config`);
+}
 const l2RpcUrl = hre.network.config.url;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In contracts/tasks/relay-op.js around lines 40 to 43, the code performs
CHAIN_MAP and network lookups without validation which can cause unclear runtime
errors; after retrieving chainId and chain, add checks that chain is defined
(throw or exit with a clear error including the chainId), then verify
chain.l1NetworkName exists and that hre.config.networks[chain.l1NetworkName] is
defined (throw/exit with a message naming chain.l1NetworkName and chainId), and
finally validate hre.network.config.url and
hre.config.networks[chain.l1NetworkName].url exist before using them, providing
explicit error text for each missing value so failures are deterministic and
debuggable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants