From 4efde21e7a215a83f647aca083b15e37d5aab1d2 Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 5 Nov 2025 19:27:41 +1000 Subject: [PATCH 1/3] chore: bridging scripts update --- contracts/.env.example | 1 + contracts/hardhat.config.ts | 38 ++++- contracts/hardhat.config.zksync.ts | 1 - contracts/package.json | 14 +- contracts/scripts/execute_proof.js | 52 ++++--- contracts/scripts/get_event_properties.js | 4 +- contracts/tasks/relay-arbitrum.js | 15 +- contracts/tasks/relay-op.js | 121 +++++++++++++++ yarn.lock | 177 +++++++++++++++++++++- 9 files changed, 376 insertions(+), 47 deletions(-) create mode 100644 contracts/tasks/relay-op.js diff --git a/contracts/.env.example b/contracts/.env.example index 411e16b..93d765f 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -1,4 +1,5 @@ PRIVATE_KEY='<0x encoded private key>' +ALCHEMY_API_KEY='' INFURA_API_KEY='' ETHERSCAN_API_KEY='' GNOSISSCAN_API_KEY='' diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 7ba2a4a..5c38e90 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -4,6 +4,7 @@ import "hardhat-deploy"; import "hardhat-deploy-ethers"; import "./tasks/generate-metaevidence"; import "./tasks/relay-arbitrum"; +import "./tasks/relay-op"; import "./tasks/find-dispute-id"; import "./tasks/update-deployments"; @@ -84,7 +85,7 @@ const config: HardhatUserConfig = { }, 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}`, // 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"], @@ -149,7 +150,7 @@ const config: HardhatUserConfig = { }, verify: { etherscan: { - apiUrl: "https://api.etherscan.io/api", + apiUrl: "https://api.etherscan.io/v2/api?chainid=1", apiKey: process.env.ETHERSCAN_API_KEY, }, }, @@ -186,7 +187,7 @@ const config: HardhatUserConfig = { }, optimism: { chainId: 10, - url: `https://optimism-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + url: `https://opt-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, accounts: [process.env.PRIVATE_KEY as string], tags: ["home"], companionNetworks: { @@ -216,7 +217,7 @@ const config: HardhatUserConfig = { }, base: { chainId: 8453, - url: `https://base-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + url: `https://base-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, accounts: [process.env.PRIVATE_KEY as string], tags: ["home"], companionNetworks: { @@ -254,12 +255,39 @@ const config: HardhatUserConfig = { }, verify: { etherscan: { - apiUrl: "https://api.polygonscan.com/api", + apiUrl: "https://api.etherscan.io/v2/api?chainid=137", apiKey: process.env.POLYGONSCAN_API_KEY, }, }, }, }, + 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! + }, + customChains: [ + { + network: "base", + chainId: 8453, + urls: { + apiURL: "https://api.etherscan.io/v2/api?chainid=8453", + browserURL: "https://basescan.org/" + } + }, + { + network: "polygon", + chainId: 137, + urls: { + apiURL: "https://api.etherscan.io/v2/api?chainid=137", + browserURL: "https://base.blockscout.com" + } + }, + ] + }, namedAccounts: { deployer: { default: 0, diff --git a/contracts/hardhat.config.zksync.ts b/contracts/hardhat.config.zksync.ts index fac48e6..3c72cc4 100644 --- a/contracts/hardhat.config.zksync.ts +++ b/contracts/hardhat.config.zksync.ts @@ -5,7 +5,6 @@ import "@matterlabs/hardhat-zksync-solc"; import "@matterlabs/hardhat-zksync-verify"; import "hardhat-deploy"; import "./tasks/update-deployments"; -// import "./tasks/generate-metaevidence"; import type { HardhatUserConfig } from "hardhat/config"; diff --git a/contracts/package.json b/contracts/package.json index d0a2794..a3470c4 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -14,6 +14,14 @@ "hardhat-zksync": "hardhat --config hardhat.config.zksync.ts", "zksync:proof:staging": "yarn hardhat-zksync run ./scripts/execute_proof.js --network zkSyncSepolia", "zksync:proof:production": "yarn hardhat-zksync run ./scripts/execute_proof.js --network zkSyncMainnet", + "relay:staging": "hardhat relay-arbitrum --network arbitrumSepolia", + "relay:production": "hardhat relay-arbitrum --network arbitrum", + "relay-op:optimism": "hardhat relay-op --network optimism", + "relay-op:redstone": "hardhat relay-op --network redstone", + "relay-op:base": "hardhat relay-op --network base", + "relay-op:unichain": "hardhat relay-op --network unichain", + "relay-op:opSepolia": "hardhat relay-op --network optimismSepolia", + "relay-op:uniSepolia": "hardhat relay-op --network unichainSepolia", "etherscan-verify": "hardhat etherscan-verify", "format:js": "biome format --write scripts deploy tasks", "check:js": "biome check --write scripts deploy tasks", @@ -90,6 +98,7 @@ "@types/node": "^22.14.0", "chai": "^4.5.0", "ethers": "^6.13.5", + "ethers5": "npm:ethers@5.7.2", "hardhat": "^2.22.18", "hardhat-deploy": "^0.14.0", "hardhat-deploy-ethers": "^0.4.2", @@ -106,11 +115,12 @@ }, "dependencies": { "@arbitrum/nitro-contracts": "^1.3.0", - "@arbitrum/sdk": "^v3.1.9", + "@arbitrum/sdk": "^4.0.4", "@kleros/dispute-resolver-interface-contract-0.7": "npm:@kleros/dispute-resolver-interface-contract@^2.0.0", "@kleros/dispute-resolver-interface-contract-0.8": "npm:@kleros/dispute-resolver-interface-contract@^8.0.0", "@kleros/ethereum-libraries": "^7.0.0", "@matterlabs/zksync-contracts": "^0.6.1", - "dotenv": "^16.4.7" + "dotenv": "^16.4.7", + "viem": "^2.38.3" } } diff --git a/contracts/scripts/execute_proof.js b/contracts/scripts/execute_proof.js index 6eea5b5..55e716e 100644 --- a/contracts/scripts/execute_proof.js +++ b/contracts/scripts/execute_proof.js @@ -1,25 +1,24 @@ const hre = require("hardhat"); -const { Provider, utils } = require("zksync-web3"); +const ethers = require("ethers"); +const { Provider, utils } = require("zksync-ethers"); const { getL1MessageSentEvent, getCalldata } = require("./get_event_properties"); -const RealitioForeignArbitrationProxy = require("@kleros/cross-chain-realitio-contracts/artifacts-zk/src/zkRealitioForeignProxy.sol/zkRealitioForeignProxy.json"); -const RealitioHomeArbitrationProxy = require("@kleros/cross-chain-realitio-contracts/artifacts-zk/src/zkRealitioHomeProxy.sol/zkRealitioHomeProxy.json"); +const RealitioForeignArbitrationProxy = require("@kleros/cross-chain-realitio-contracts/artifacts-zk/src/0.8/RealitioForeignProxyZkSync.sol/RealitioForeignProxyZkSync.json"); +const RealitioHomeArbitrationProxy = require("@kleros/cross-chain-realitio-contracts/artifacts-zk/src/0.8/RealitioHomeProxyZkSync.sol/RealitioHomeProxyZkSync.json"); async function executeProof() { - // https://era.zksync.io/docs/dev/how-to/send-message-l2-l1.html + // https://code.zksync.io/tutorials/how-to-send-l2-l1-message const txHash = ""; - const { providers } = ethers; const foreignNetworks = { 324: hre.config.networks.mainnet, 300: hre.config.networks.sepolia, }; const chainId = hre.network.config.chainId; - const url = foreignNetworks[chainId]; - const l1Provider = new Provider(hre.network.config.url); - const l2Provider = new providers.JsonRpcProvider(url); + const l1Provider = new ethers.JsonRpcProvider(foreignNetworks[chainId]?.url); + const l2Provider = new Provider(hre.network.config.url); - const l1MessageSentEvent = await getL1MessageSentEvent(txHash, utils.L1_MESSENGER, l1Provider); + const l1MessageSentEvent = await getL1MessageSentEvent(txHash, utils.L1_MESSENGER, l2Provider); if (!l1MessageSentEvent) { throw new Error("No L1MessageSent event found in the transaction."); @@ -28,13 +27,13 @@ async function executeProof() { 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( await homeProxyContract.foreignProxy(), RealitioForeignArbitrationProxy.abi, - l2Provider + l1Provider ); console.log(`Event: ${l1MessageSentEvent.name}`); @@ -43,14 +42,18 @@ async function executeProof() { console.log("Hash:", msgHash); console.log("Message:", eventData); - const proof = await getL1MessageProof(blockNumber, l1Provider, homeProxy, msgHash); - console.log("Proof is: ", proof); - const { l1BatchNumber, l1BatchTxIndex } = await l1Provider.getTransactionReceipt(txHash); - + const l2Receipt = await l2Provider.getTransactionReceipt(txHash); + console.log(l2Receipt); + const logIndex = l2Receipt.l2ToL1Logs[0].logIndex; + console.log(`L2 transaction included in block ${l2Receipt.blockNumber} with log index ${logIndex}`); + const { l1BatchNumber, l1BatchTxIndex } = l2Receipt; console.log("L1 Index for Tx in block :>> ", l1BatchTxIndex); console.log("L1 Batch for block :>> ", l1BatchNumber); - const result = await proveL1MessageInclusion( + const proof = await getLogProof(txHash, logIndex, l2Provider); + console.log("Proof is: ", proof); + + const result = await proveL2MessageInclusion( l1BatchNumber, proof, l1BatchTxIndex, @@ -63,7 +66,7 @@ async function executeProof() { console.log("Result is :>> ", result); if (result) { - const signer = new ethers.Wallet(process.env.PRIVATE_KEY, l2Provider); + const signer = new ethers.Wallet(process.env.PRIVATE_KEY, l1Provider); try { await foreignProxyContract .connect(signer) @@ -78,17 +81,16 @@ async function executeProof() { process.exit(); } -async function getL1MessageProof(blockNumber, l1Provider, homeProxy, msgHash) { - console.log(`Getting L1 message proof for block ${blockNumber}`); - return await l1Provider.getMessageProof(blockNumber, homeProxy, msgHash); +async function getLogProof(txHash, l2TxIndex, l2Provider) { + return await l2Provider.getLogProof(txHash, l2TxIndex); } -async function proveL1MessageInclusion(l1BatchNumber, proof, trxIndex, l1Provider, l2Provider, homeProxy, message) { - const zkAddress = await l1Provider.getMainContractAddress(); +async function proveL2MessageInclusion(l1BatchNumber, proof, trxIndex, l1Provider, l2Provider, homeProxy, message) { + const zkAddress = await l2Provider.getMainContractAddress(); - const mailboxL1Contract = new ethers.Contract(zkAddress, utils.ZKSYNC_MAIN_ABI, l2Provider); + const mailboxL1Contract = new ethers.Contract(zkAddress, utils.ZKSYNC_MAIN_ABI, l1Provider); const messageInfo = { - txNumberInBlock: trxIndex, + txNumberInBatch: trxIndex, sender: homeProxy, data: message, }; diff --git a/contracts/scripts/get_event_properties.js b/contracts/scripts/get_event_properties.js index e78ed9d..e78f3f3 100644 --- a/contracts/scripts/get_event_properties.js +++ b/contracts/scripts/get_event_properties.js @@ -26,7 +26,7 @@ async function getL1MessageSentEvent(transactionHash, contractInterface, provide } function getFunctionSelector(functionSignature) { - const hash = ethers.utils.id(functionSignature); + const hash = ethers.id(functionSignature); const selector = hash.slice(0, 10); // 0x + first 4 bytes return selector; @@ -40,7 +40,7 @@ function encodeWithSelector(selector, ...params) { } // Otherwise, encode using defaultAbiCoder - return ethers.utils.defaultAbiCoder.encode([param.type], [param.value]).slice(2); + return ethers.AbiCoder.defaultAbiCoder().encode([param.type], [param.value]).slice(2); }); const encodedData = selector + encodedParams.join(""); diff --git a/contracts/tasks/relay-arbitrum.js b/contracts/tasks/relay-arbitrum.js index 08c7716..5f801b7 100644 --- a/contracts/tasks/relay-arbitrum.js +++ b/contracts/tasks/relay-arbitrum.js @@ -1,5 +1,5 @@ -const { providers, Wallet } = require("ethers"); -const { L2TransactionReceipt, L2ToL1MessageStatus } = require("@arbitrum/sdk"); +const { providers, Wallet } = require("ethers5"); +const { ChildTransactionReceipt, ChildToParentMessageStatus } = require('@arbitrum/sdk'); const { task } = require("hardhat/config"); /** @@ -8,7 +8,10 @@ const { task } = require("hardhat/config"); const walletPrivateKey = process.env.PRIVATE_KEY; -task("exec", "Execute msg on L1") +// https://docs.arbitrum.io/build-decentralized-apps/cross-chain-messaging +// https://github.com/OffchainLabs/arbitrum-tutorials/blob/master/packages/outbox-execute/scripts/exec.js + +task("relay-arbitrum", "Execute msg on L1") .addParam("txhash", "Hash of txn that triggered and L2 to L1 message") .setAction(async (taskArgs, hre) => { const { txhash } = taskArgs; @@ -38,19 +41,19 @@ task("exec", "Execute msg on L1") * First, let's find the Arbitrum txn from the txn hash provided */ const receipt = await l2Provider.getTransactionReceipt(txhash); - const l2Receipt = new L2TransactionReceipt(receipt); + const l2Receipt = new ChildTransactionReceipt(receipt); /** * Note that in principle, a single transaction could trigger any number of outgoing messages; the common case will be there's only one. * For the sake of this script, we assume there's only one / just grad the first one. */ - const messages = await l2Receipt.getL2ToL1Messages(l1Wallet); + const messages = await l2Receipt.getChildToParentMessages(l1Wallet); const l2ToL1Msg = messages[0]; /** * Check if already executed */ - if ((await l2ToL1Msg.status(l2Provider)) === L2ToL1MessageStatus.EXECUTED) { + if ((await l2ToL1Msg.status(l2Provider)) === ChildToParentMessageStatus.EXECUTED) { console.log("Message already executed! Nothing else to do here"); process.exit(1); } diff --git a/contracts/tasks/relay-op.js b/contracts/tasks/relay-op.js new file mode 100644 index 0000000..be6f894 --- /dev/null +++ b/contracts/tasks/relay-op.js @@ -0,0 +1,121 @@ +const { task } = require("hardhat/config"); + +const { + createPublicClient, + createWalletClient, + http, +} = require("viem"); + +const { privateKeyToAccount } = require("viem/accounts"); + +const { + publicActionsL1, + publicActionsL2, + walletActionsL1, + walletActionsL2, +} = require("viem/op-stack"); +const { mainnet, sepolia, base, optimism, optimismSepolia, unichain, unichainSepolia, redstone } = require("viem/chains"); + +// https://docs.optimism.io/app-developers/tutorials/bridging/cross-dom-solidity#interact-with-the-l2-greeter + +task("relay-op", "Relays a withdrawal on OP Stack") + .addParam("txhash", "The withdrawal tx hash from L2") + .setAction(async ({ txhash }, hre) => { + console.log(`Relaying OP-stack withdrawal: ${txhash}`); + + const rawPk = process.env.PRIVATE_KEY; + if (!rawPk) throw new Error("PRIVATE_KEY missing in .env"); + + const PRIVATE_KEY = rawPk.startsWith("0x") ? rawPk : `0x${rawPk}`; + const CHAIN_MAP = { + 8453: { l2: base, l1: mainnet, l1NetworkName: "mainnet" }, + 690: { l2: redstone, l1: mainnet, l1NetworkName: "mainnet" }, + 10: { l2: optimism, l1: mainnet, l1NetworkName: "mainnet" }, + 130: { l2: unichain, l1: mainnet, l1NetworkName: "mainnet" }, + 11155420: { l2: optimismSepolia, l1: sepolia, l1NetworkName: "sepolia" }, + 1301: { l2: unichainSepolia, l1: sepolia, l1NetworkName: "sepolia" }, + }; + + 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; + + const account = privateKeyToAccount(PRIVATE_KEY); + + // L1 clients + const l1Public = createPublicClient({ + chain: chain.l1, + transport: http(l1RpcUrl), + }).extend(publicActionsL1()); + + 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 ✅"); + } else if (status === "waiting-to-finalize") { + console.log("Not ready to finalize yet"); + } + + if (status === "finalized") { + console.log("Already finalized. Nothing to do."); + return; + } + }); diff --git a/yarn.lock b/yarn.lock index 9ef9d10..e17c4f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36,6 +36,13 @@ __metadata: languageName: node linkType: hard +"@adraffy/ens-normalize@npm:^1.11.0": + version: 1.11.1 + resolution: "@adraffy/ens-normalize@npm:1.11.1" + checksum: e8b17fcc730ccc45a956e1fbb09edfe42be41c291079512082e9964f8ef4287e67913183cdd02fff71d2e215340d5b98a9bbbd9be32c5d36fad4ba2c1ec33ff2 + languageName: node + linkType: hard + "@alloc/quick-lru@npm:^5.2.0": version: 5.2.0 resolution: "@alloc/quick-lru@npm:5.2.0" @@ -65,16 +72,16 @@ __metadata: languageName: node linkType: hard -"@arbitrum/sdk@npm:^v3.1.9": - version: 3.7.1 - resolution: "@arbitrum/sdk@npm:3.7.1" +"@arbitrum/sdk@npm:^4.0.4": + version: 4.0.4 + resolution: "@arbitrum/sdk@npm:4.0.4" dependencies: "@ethersproject/address": ^5.0.8 "@ethersproject/bignumber": ^5.1.1 "@ethersproject/bytes": ^5.0.8 async-mutex: ^0.4.0 ethers: ^5.1.0 - checksum: 2da5e69bc6e903dcaccf4905a68b8490a88081e29769509aecaa3f9438065cf98cd2262b88430c0f742b63df3116e8e8aee435e80dc8c53b1abb120e2edb509e + checksum: c7d13f7e9c40ea78e6526bc8211ddc3aeafa5cd40fc02ede0245f128613248b3d2c0b5a8176552dc8523ac150108f5f427436a85120a99460026ace93024a8a1 languageName: node linkType: hard @@ -3208,7 +3215,7 @@ __metadata: resolution: "@kleros/cross-chain-realitio-contracts@workspace:contracts" dependencies: "@arbitrum/nitro-contracts": ^1.3.0 - "@arbitrum/sdk": ^v3.1.9 + "@arbitrum/sdk": ^4.0.4 "@biomejs/biome": 1.9.4 "@kleros/dispute-resolver-interface-contract-0.7": "npm:@kleros/dispute-resolver-interface-contract@^2.0.0" "@kleros/dispute-resolver-interface-contract-0.8": "npm:@kleros/dispute-resolver-interface-contract@^8.0.0" @@ -3234,6 +3241,7 @@ __metadata: chai: ^4.5.0 dotenv: ^16.4.7 ethers: ^6.13.5 + ethers5: "npm:ethers@5.7.2" hardhat: ^2.22.18 hardhat-deploy: ^0.14.0 hardhat-deploy-ethers: ^0.4.2 @@ -3246,6 +3254,7 @@ __metadata: ts-node: ^10.9.2 typechain: ^8.3.2 typescript: ^5.8.3 + viem: ^2.38.3 zksync-ethers: ^6.15.4 languageName: unknown linkType: soft @@ -3461,6 +3470,13 @@ __metadata: languageName: node linkType: hard +"@noble/ciphers@npm:^1.3.0": + version: 1.3.0 + resolution: "@noble/ciphers@npm:1.3.0" + checksum: 19722c35475df9bc78db60d261d0b5ef8a6d722561efc2135453f943eaa421b492195dc666e3e4df2b755bca3739e04f04b9c660198559f5dd05d3cfbf1b9e92 + languageName: node + linkType: hard + "@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" @@ -3488,6 +3504,24 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:1.9.1": + version: 1.9.1 + resolution: "@noble/curves@npm:1.9.1" + dependencies: + "@noble/hashes": 1.8.0 + checksum: 4f3483a1001538d2f55516cdcb19319d1eaef79550633f670e7d570b989cdbc0129952868b72bb67643329746b8ffefe8e4cd791c8cc35574e05a37f873eef42 + languageName: node + linkType: hard + +"@noble/curves@npm:~1.9.0": + version: 1.9.7 + resolution: "@noble/curves@npm:1.9.7" + dependencies: + "@noble/hashes": 1.8.0 + checksum: 65acad44ac6944ab96471109087d6cfcbcaa251faad6295961be9a5ace220634f4b7c74a96d1ee2274ad3880ea953d8e8259893ed8c906c831ef29f5c04ec9cc + languageName: node + linkType: hard + "@noble/hashes@npm:1.2.0, @noble/hashes@npm:~1.2.0": version: 1.2.0 resolution: "@noble/hashes@npm:1.2.0" @@ -3516,6 +3550,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1.8.0, @noble/hashes@npm:~1.8.0": + version: 1.8.0 + resolution: "@noble/hashes@npm:1.8.0" + checksum: c94e98b941963676feaba62475b1ccfa8341e3f572adbb3b684ee38b658df44100187fa0ef4220da580b13f8d27e87d5492623c8a02ecc61f23fb9960c7918f5 + languageName: node + linkType: hard + "@noble/hashes@npm:^1.4.0": version: 1.7.0 resolution: "@noble/hashes@npm:1.7.0" @@ -4402,6 +4443,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.2.5": + version: 1.2.6 + resolution: "@scure/base@npm:1.2.6" + checksum: 1058cb26d5e4c1c46c9cc0ae0b67cc66d306733baf35d6ebdd8ddaba242b80c3807b726e3b48cb0411bb95ec10d37764969063ea62188f86ae9315df8ea6b325 + languageName: node + linkType: hard + "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -4446,6 +4494,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.7.0, @scure/bip32@npm:^1.7.0": + version: 1.7.0 + resolution: "@scure/bip32@npm:1.7.0" + dependencies: + "@noble/curves": ~1.9.0 + "@noble/hashes": ~1.8.0 + "@scure/base": ~1.2.5 + checksum: c83adca5a74ec5c4ded8ba93900d0065e4767c4759cf24c2674923aef01d45ba56f171574e3519f2341be99f53a333f01b674eb6cfeb6fa8379607c6d1bc90b5 + languageName: node + linkType: hard + "@scure/bip39@npm:1.1.1": version: 1.1.1 resolution: "@scure/bip39@npm:1.1.1" @@ -4486,6 +4545,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.6.0, @scure/bip39@npm:^1.6.0": + version: 1.6.0 + resolution: "@scure/bip39@npm:1.6.0" + dependencies: + "@noble/hashes": ~1.8.0 + "@scure/base": ~1.2.5 + checksum: 96d46420780473d6c6c9700254a0eceec60302f61d7f9d7f29024e90c7acff3e8e40a5ee52dfaf104db539a10462e531996aaf9e69f082b8540b0a25870545fc + languageName: node + linkType: hard + "@selderee/plugin-htmlparser2@npm:^0.6.0": version: 0.6.0 resolution: "@selderee/plugin-htmlparser2@npm:0.6.0" @@ -5811,6 +5880,36 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.1.0": + version: 1.1.0 + resolution: "abitype@npm:1.1.0" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 55f724d038a60cc5e4ce4913298f912f0c34c53e13240cd3b97b272f4122bdf4c84541d85d1e3bb36f6e8dab6685f232c69600718fad62ccc389bea3f63ed7e4 + languageName: node + linkType: hard + +"abitype@npm:^1.0.9": + version: 1.1.1 + resolution: "abitype@npm:1.1.1" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 8df4754f73e0552c5aad228e2ae62368ab858855ffc99ff27698696c27d60883c9655b8d0615448474b8cdecf1733dc188318e4bbd7b18d5f87eaf895fcb5ceb + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -10407,7 +10506,7 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^5.1.0, ethers@npm:^5.7.0, ethers@npm:~5.7.0": +"ethers5@npm:ethers@5.7.2, ethers@npm:^5.1.0, ethers@npm:^5.7.0, ethers@npm:~5.7.0": version: 5.7.2 resolution: "ethers@npm:5.7.2" dependencies: @@ -13540,6 +13639,15 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.7": + version: 1.0.7 + resolution: "isows@npm:1.0.7" + peerDependencies: + ws: "*" + checksum: 044b949b369872882af07b60b613b5801ae01b01a23b5b72b78af80c8103bbeed38352c3e8ceff13a7834bc91fd2eb41cf91ec01d59a041d8705680e6b0ec546 + languageName: node + linkType: hard + "isstream@npm:~0.1.2": version: 0.1.2 resolution: "isstream@npm:0.1.2" @@ -16221,6 +16329,27 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.9.6": + version: 0.9.6 + resolution: "ox@npm:0.9.6" + dependencies: + "@adraffy/ens-normalize": ^1.11.0 + "@noble/ciphers": ^1.3.0 + "@noble/curves": 1.9.1 + "@noble/hashes": ^1.8.0 + "@scure/bip32": ^1.7.0 + "@scure/bip39": ^1.6.0 + abitype: ^1.0.9 + eventemitter3: 5.0.1 + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 5f5094502cab9b135f3de3dfe60691fc312a1e534b3a9ef03bd867bfe0921245360c78dcb59bb438f6d66316b7da29506da4b46633f48cd8f7c4f37f56a76e4c + languageName: node + linkType: hard + "p-cancelable@npm:^1.0.0": version: 1.1.0 resolution: "p-cancelable@npm:1.1.0" @@ -21757,6 +21886,27 @@ __metadata: languageName: node linkType: hard +"viem@npm:^2.38.3": + version: 2.38.6 + resolution: "viem@npm:2.38.6" + dependencies: + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 + "@scure/bip32": 1.7.0 + "@scure/bip39": 1.6.0 + abitype: 1.1.0 + isows: 1.0.7 + ox: 0.9.6 + ws: 8.18.3 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 19b1d3fab009731c99d6bca7db3d0615de0232716e279026cd69d3495c977c19abc2d05cbb1227a8fc74a332d15108c96e988f4a6acc57a673eccc0a2ec61312 + languageName: node + linkType: hard + "vite-node@npm:1.6.1": version: 1.6.1 resolution: "vite-node@npm:1.6.1" @@ -22745,6 +22895,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.18.3": + version: 8.18.3 + resolution: "ws@npm:8.18.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: d64ef1631227bd0c5fe21b3eb3646c9c91229402fb963d12d87b49af0a1ef757277083af23a5f85742bae1e520feddfb434cb882ea59249b15673c16dc3f36e0 + languageName: node + linkType: hard + "ws@npm:^3.0.0": version: 3.3.3 resolution: "ws@npm:3.3.3" From d66b925e63579b4459321f8b502279c9d2b32f9b Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 5 Nov 2025 19:28:18 +1000 Subject: [PATCH 2/3] docs: l2 bridging doc --- contracts/docs/Bridging.md | 97 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 contracts/docs/Bridging.md diff --git a/contracts/docs/Bridging.md b/contracts/docs/Bridging.md new file mode 100644 index 0000000..164e50b --- /dev/null +++ b/contracts/docs/Bridging.md @@ -0,0 +1,97 @@ +# Manual Actions for L2 → L1 Bridging +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. + + +## Gnosis +After sending a transaction from L2 (e.g., `handleNotifiedRequest`), refer to this page: +https://docs.gnosischain.com/bridges/using-amb + +It's recommended to use **Blockscout** instead of GnosisScan due to possible encoding issues. + +Before using `getSignatures` on the `AMBHelper` contract, wait until the message is processed +(usually within an hour). After that, `getSignatures` will return a result you can pass into +`executeSignatures`. + + +## Polygon +After sending the L2 transaction you'll need to manually call `receiveMessage` +on `foreignProxy` contract. + +The argument for the function can be obtained from this template URL (usually generated in **1–3 hours**): + +``` +https://proof-generator.polygon.technology/api/v1/matic/exit-payload/?eventSignature=0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036 +``` + +This URL contains: +- Your L2 tx hash that you need to manually insert +- The event signature required by Polygon API (same for every tx, belongs to `messageSent` event) + +More details: +https://github.com/0xPolygon/fx-portal?tab=readme-ov-file#proof-generation + + +## zkSync +After sending the tx to L1, check its status on zkSync Etherscan. +When the status becomes **"Executed"** (usually in 2-4 hours), use the script: + +https://github.com/kleros/cross-chain-realitio-proxy/blob/master/contracts/scripts/execute_proof.js + +Steps: +1. Insert your `txHash` into the script. +2. Run `yarn zksync:proof:production` from `contracts` folder. + +This script retrieves the proof and executes the tx on L1 automatically. + +Requirements: +- `yarn install` +- `.env` file setup (`PRIVATE_KEY`, `INFURA_API_KEY`) + +zkSync docs: +https://code.zksync.io/tutorials/how-to-send-l2-l1-message + +If the status is "Executed" but proof is `null`, wait longer. +Once executed successfully, a dispute will be created on KlerosCourt with `ForeignProxy` as arbitrable. + + +## Arbitrum +Execution occurs only after the **one-week challenge period** has passed, thus a week after sending a tx to L1, navigate to `contracts` folder and run: + +``` +yarn relay:production --txhash +``` + +Requirements: +- `yarn install` +- `.env` file setup (`PRIVATE_KEY`, `INFURA_API_KEY`) + +Note: this task currently works only with **ethers v5** + +Official docs page: +https://docs.arbitrum.io/build-decentralized-apps/cross-chain-messaging +https://github.com/OffchainLabs/arbitrum-tutorials/blob/master/packages/outbox-execute/scripts/exec.js + + +## Optimism (Base, Redstone, Unichain, etc.) +After sending the L2 tx run the command corresponding to the chosen chain, e.g.: + +``` +yarn relay-op:base --txhash +``` + +You must run this command **twice**: +1. Shortly after sending the tx (usually within an hour), to prove the message. Console should show `Proven` if successful +2. One week later, to finalize it + +Requirements: +- `yarn install` +- `.env` file setup (`PRIVATE_KEY`, `INFURA_API_KEY`, `ALCHEMY_API_KEY`) + +Extra notes: +- Optimism stack requires `eth_getProof`, unsupported by Infura therefore **Alchemy** is used for L2 RPC. +- L1 RPC can still use Infura. + +Official docs page: +https://docs.optimism.io/app-developers/tutorials/bridging/cross-dom-solidity#interact-with-the-l2-greeter From ec06dd43fc1d088d05c1b8cb04932e4d9e0b20c4 Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 5 Nov 2025 19:34:40 +1000 Subject: [PATCH 3/3] fix(Config): polygon link fix --- contracts/hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 5c38e90..a048fe3 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -283,7 +283,7 @@ const config: HardhatUserConfig = { chainId: 137, urls: { apiURL: "https://api.etherscan.io/v2/api?chainid=137", - browserURL: "https://base.blockscout.com" + browserURL: "https://polygonscan.com/" } }, ]