Skip to content
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

feat/arb-sep-to-sep-testnet #344

Draft
wants to merge 9 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
443 changes: 443 additions & 0 deletions contracts/deployments/arbitrumSepolia/VeaInboxArbToEthTestnet.json

Large diffs are not rendered by default.

1,426 changes: 1,426 additions & 0 deletions contracts/deployments/sepolia/VeaOutboxArbToEthTestnet.json

Large diffs are not rendered by default.

366 changes: 305 additions & 61 deletions contracts/test/integration/ArbToEth.ts

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion relayer-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"yarn": "4.2.2"
},
"scripts": {
"start-devnet-relayer": "npx ts-node ./src/devnetRelayExample.ts"
"start-devnet-relayer": "npx ts-node ./src/devnetRelayExample.ts",
"start-testnet-sepolia": "npx ts-node ./src/testnet/arbSepToSepRelayer.ts"
},
"dependencies": {
"@kleros/vea-contracts": "workspace:^",
Expand Down
57 changes: 7 additions & 50 deletions relayer-cli/src/devnetRelayExample.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { relayAllFrom } from "./utils/relay";
import * as fs from "fs";
import { initialize, updateStateFile } from "./utils/relayerHelpers";

// let chain_ids = [5, 10200];
let chain_ids = [11155111];
const epochPeriod = 1800; // 30 min
const _contract = require("@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthDevnet.json");
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use consistent module import syntax

You are mixing ES module import statements with CommonJS require statements. For consistency and to avoid potential issues, it's recommended to use import statements throughout the code.

Apply this diff to change the require statement to an import statement:

-const _contract = require("@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthDevnet.json");
+import _contract from "@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthDevnet.json";

Note: Ensure that your TypeScript configuration (tsconfig.json) has "resolveJsonModule": true and "esModuleInterop": true to support importing JSON files.

📝 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 _contract = require("@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthDevnet.json");
import _contract from "@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthDevnet.json";

const network = "devnet";

["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) =>
process.on(signal, async () => {
console.log("exit");
Expand All @@ -20,11 +24,11 @@ const epochPeriod = 1800; // 30 min
(async () => {
while (1) {
for (const chain_id of chain_ids) {
let nonce = await initialize(chain_id);
let nonce = await initialize(chain_id, network);
// This is libghtbulb switch address in arbitrum sepolia
const sender = "0x28d6D503F4c5734cD926E96b63C61527d975B382";
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Externalize the hardcoded sender address

Hardcoding the sender address reduces flexibility and may pose security risks. Consider externalizing it to a configuration file or an environment variable.

Apply this diff to externalize the sender address:

-  const sender = "0x28d6D503F4c5734cD926E96b63C61527d975B382";
+  const sender = process.env.SENDER_ADDRESS || "0x28d6D503F4c5734cD926E96b63C61527d975B382";

Note: You'll need to set the SENDER_ADDRESS environment variable in your deployment environment or configuration.

📝 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 sender = "0x28d6D503F4c5734cD926E96b63C61527d975B382";
const sender = process.env.SENDER_ADDRESS || "0x28d6D503F4c5734cD926E96b63C61527d975B382";

nonce = await relayAllFrom(chain_id, nonce, sender);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce);
nonce = await relayAllFrom(chain_id, nonce, sender, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);
}
const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
Expand All @@ -33,53 +37,6 @@ const epochPeriod = 1800; // 30 min
}
})();

async function initialize(chain_id: number): Promise<number> {
if (chain_id !== 11155111) throw new Error("Invalid chainid");

const lock_file_name = "./src/state/" + chain_id + ".pid";

if (fs.existsSync(lock_file_name)) {
console.log("Skipping chain with process already running, delete pid file to force", chain_id);
throw new Error("Already running");
}
fs.writeFileSync(lock_file_name, process.pid.toString(), { encoding: "utf8" });

// STATE_DIR is absolute path of the directory where the state files are stored
// STATE_DIR must have trailing slash
const state_file = process.env.STATE_DIR + chain_id + ".json";
if (!fs.existsSync(state_file)) {
// No state file so initialize starting now
const tsnow = Math.floor(Date.now() / 1000);
await updateStateFile(chain_id, tsnow, 0);
}

// print pwd for debugging
console.log(process.cwd());
var chain_state = require(state_file);

let nonce = 0;
if ("nonce" in chain_state) {
nonce = chain_state["nonce"];
}

return nonce;
}

async function updateStateFile(chain_id: number, createdTimestamp: number, nonceFrom: number) {
const chain_state_file = "./src/state/" + chain_id + ".json";
const json = {
ts: createdTimestamp,
nonce: nonceFrom,
};
fs.writeFileSync(chain_state_file, JSON.stringify(json), { encoding: "utf8" });
for (const chain_id of chain_ids) {
const lock_file_name = "./src/state/" + chain_id + ".pid";
if (fs.existsSync(lock_file_name)) {
fs.unlinkSync(lock_file_name);
}
}
}

function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
39 changes: 39 additions & 0 deletions relayer-cli/src/testnet/arbSepToChiadoRelayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as fs from "fs";
require("dotenv").config();
import { relayBatch } from "utils/relay";
import { initialize, updateStateFile } from "utils/relayerHelpers";
const _contract = require("@kleros/vea-contracts/deployments/chiado/VeaOutboxArbToGnosisTestnet.json");

let chain_id = 10200;
const epochPeriod = 7200; // 3 hrs
const batchSize = 10; // 10 messages per batch
const network = "testnet";

["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) =>
process.on(signal, async () => {
console.log("exit");
const lock_file_name = "./src/state/" + chain_id + ".pid";
if (fs.existsSync(lock_file_name)) {
fs.unlinkSync(lock_file_name);
}
process.exit(0);
})
);
Comment on lines +12 to +21
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove invalid signal 'MODULE_NOT_FOUND' from signal handlers

"MODULE_NOT_FOUND" is not a valid signal or event that can be caught using process.on. Including it in the signal handlers will have no effect and may cause confusion. If you need to handle module loading errors, consider adding proper error handling around your module imports.

Apply this diff to remove the invalid signal:

-["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) =>
+["SIGINT", "SIGTERM", "SIGQUIT", "EXIT"].forEach((signal) =>
📝 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
["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) =>
process.on(signal, async () => {
console.log("exit");
const lock_file_name = "./src/state/" + chain_id + ".pid";
if (fs.existsSync(lock_file_name)) {
fs.unlinkSync(lock_file_name);
}
process.exit(0);
})
);
["SIGINT", "SIGTERM", "SIGQUIT", "EXIT"].forEach((signal) =>
process.on(signal, async () => {
console.log("exit");
const lock_file_name = "./src/state/" + chain_id + ".pid";
if (fs.existsSync(lock_file_name)) {
fs.unlinkSync(lock_file_name);
}
process.exit(0);
})
);


(async () => {
while (1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use 'while (true)' instead of 'while (1)' for infinite loop

Using while (1) may trigger linting errors due to an unexpected constant condition. Replacing it with while (true) improves readability and aligns with JavaScript conventions.

Apply this diff to fix the issue:

-  while (1) {
+  while (true) {
📝 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
while (1) {
while (true) {
🧰 Tools
🪛 Biome

[error] 24-24: Unexpected constant condition.

(lint/correctness/noConstantCondition)

let nonce = await initialize(chain_id, network);
console.log("chain_id", chain_id, "nonce", nonce);
nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);

const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
await delay(delayAmount);
}
Comment on lines +24 to +34
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling inside the main loop to prevent crashes

Currently, any unhandled exceptions within the loop will cause the relayer to crash and exit. Wrapping the contents of the loop in a try...catch block ensures that the relayer continues running even if an error occurs, enhancing robustness.

Apply this diff to add error handling:

 (async () => {
   while (true) {
+    try {
     let nonce = await initialize(chain_id, network);
     console.log("chain_id", chain_id, "nonce", nonce);
     nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
     if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);

     const currentTS = Math.floor(Date.now() / 1000);
     const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
     console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
     await delay(delayAmount);
+    } catch (error) {
+      console.error("An error occurred in the main loop:", error);
+    }
   }
 })();
📝 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
while (1) {
let nonce = await initialize(chain_id, network);
console.log("chain_id", chain_id, "nonce", nonce);
nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);
const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
await delay(delayAmount);
}
while (true) {
try {
let nonce = await initialize(chain_id, network);
console.log("chain_id", chain_id, "nonce", nonce);
nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);
const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
await delay(delayAmount);
} catch (error) {
console.error("An error occurred in the main loop:", error);
}
}
🧰 Tools
🪛 Biome

[error] 24-24: Unexpected constant condition.

(lint/correctness/noConstantCondition)

})();

function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Comment on lines +37 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Extract 'delay' function into a shared utility module

The delay function is also defined in arbSepToSepRelayer.ts, indicating code duplication. Consider moving the delay function into a shared utility module to promote reusability and maintainability.

Move the delay function to a utility file, such as utils/delay.ts, and import it where needed:

+// In utils/delay.ts
+export function delay(ms: number) {
+  return new Promise((resolve) => setTimeout(resolve, ms));
+}

-// In arbSepToChiadoRelayer.ts
-function delay(ms: number) {
-  return new Promise((resolve) => setTimeout(resolve, ms));
-}

+// Replace with import statement
+import { delay } from "utils/delay";

Committable suggestion was skipped due to low confidence.

38 changes: 38 additions & 0 deletions relayer-cli/src/testnet/arbSepToSepRelayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as fs from "fs";
require("dotenv").config();
import { relayBatch } from "utils/relay";
import { initialize, updateStateFile } from "utils/relayerHelpers";

const _contract = require("@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthTestnet.json");
const network = "testnet";

let chain_id = 11155111;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use camelCase and const for chain_id

It's recommended to use camelCase for variable names in JavaScript/TypeScript. Also, since chain_id is not reassigned, it can be declared as const instead of let.

Apply this diff:

-let chain_id = 11155111;
+const chainId = 11155111;

Ensure you update all occurrences of chain_id to chainId throughout the code.

📝 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
let chain_id = 11155111;
const chainId = 11155111;

const epochPeriod = 7200; // 3 hrs
const batchSize = 10; // 10 messages per batch

["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) =>
process.on(signal, async () => {
console.log("exit");
const lock_file_name = "./src/state/" + chain_id + ".pid";
if (fs.existsSync(lock_file_name)) {
fs.unlinkSync(lock_file_name);
}
process.exit(0);
})
);
Comment on lines +13 to +22
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the list of signals in the signal handler

The signals 'EXIT' and 'MODULE_NOT_FOUND' are not valid signals in Node.js. 'exit' is an event, not a signal, and 'MODULE_NOT_FOUND' is an error code. Including them in process.on(signal, ...) may lead to unexpected behavior.

Consider modifying the signal handling as follows:

-["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) =>
+["SIGINT", "SIGTERM", "SIGQUIT"].forEach((signal) =>
  process.on(signal, async () => {
    console.log("exit");
    const lock_file_name = "./src/state/" + chainId + ".pid";
    if (fs.existsSync(lock_file_name)) {
      fs.unlinkSync(lock_file_name);
    }
    process.exit(0);
  })
);

If you need to handle the 'exit' event, add a separate listener:

process.on('exit', () => {
  console.log("Process exiting");
  const lockFileName = "./src/state/" + chainId + ".pid";
  if (fs.existsSync(lockFileName)) {
    fs.unlinkSync(lockFileName);
  }
});

Please note that asynchronous code inside an 'exit' event handler may not complete before the process exits. Keep the 'exit' handler synchronous.


(async () => {
while (1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace constant condition while (1) with while (true)

Using while (1) can cause linting errors for unexpected constant conditions. Replace it with while (true) for better readability and to satisfy linting rules.

Apply this diff:

-(async () => {
-  while (1) {
+(async () => {
+  while (true) {
📝 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
while (1) {
(async () => {
while (true) {
🧰 Tools
🪛 Biome

[error] 25-25: Unexpected constant condition.

(lint/correctness/noConstantCondition)

let nonce = await initialize(chain_id, network);
nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);
const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
await delay(delayAmount);
}
})();
Comment on lines +25 to +34
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling in the main loop

If an error occurs during initialize, relayBatch, or updateStateFile, the exception will exit the loop and stop the relayer. To ensure the relayer runs continuously, wrap the loop contents in a try-catch block.

Apply this diff:

  while (true) {
+   try {
      let nonce = await initialize(chainId, network);
      nonce = await relayBatch(chainId, nonce, batchSize, _contract);
      if (nonce != null) await updateStateFile(chainId, Math.floor(Date.now() / 1000), nonce, network);
      const currentTS = Math.floor(Date.now() / 1000);
      const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
      console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
      await delay(delayAmount);
+   } catch (error) {
+     console.error("An error occurred during the relay loop:", error);
+     // Optionally, handle the error, retry, or perform cleanup
+   }
  }

This will log the error and allow the loop to continue running.

📝 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
while (1) {
let nonce = await initialize(chain_id, network);
nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);
const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
await delay(delayAmount);
}
})();
while (true) {
try {
let nonce = await initialize(chain_id, network);
nonce = await relayBatch(chain_id, nonce, batchSize, _contract);
if (nonce != null) await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce, network);
const currentTS = Math.floor(Date.now() / 1000);
const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 100 * 1000;
console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds");
await delay(delayAmount);
} catch (error) {
console.error("An error occurred during the relay loop:", error);
// Optionally, handle the error, retry, or perform cleanup
}
}
})();
🧰 Tools
🪛 Biome

[error] 25-25: Unexpected constant condition.

(lint/correctness/noConstantCondition)


function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Comment on lines +36 to +38
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor delay function to a shared utility module

The delay function is commonly used and might exist in other files. Consider moving it to a shared utility module for reusability and to avoid code duplication.

Create a utils/delay.ts file:

export function delay(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

Then import it where needed:

+import { delay } from "utils/delay";

53 changes: 32 additions & 21 deletions relayer-cli/src/utils/ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { JsonRpcProvider } from "@ethersproject/providers";
import {
VeaOutboxArbToEth__factory,
VeaOutboxArbToEthDevnet__factory,
VeaOutboxArbToGnosisDevnet__factory,
VeaInboxArbToEth__factory,
VeaInboxArbToGnosis__factory,
VeaOutboxArbToGnosis__factory,
} from "@kleros/vea-contracts/typechain-types";

function getWallet(privateKey: string, web3ProviderURL: string) {
Expand All @@ -15,20 +16,37 @@ function getWalletRPC(privateKey: string, rpc: JsonRpcProvider) {
return new Wallet(privateKey, rpc);
}

function getVeaInboxArbToEth(veaInboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
// Using destination chainId as identifier, Ex: Arbitrum One (42161) -> Ethereum Mainnet (1): Use "1" as chainId
function getVeaInbox(veaInboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
if (chainId == 11155111) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
} else if (chainId == 10200) {
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
}
Comment on lines +19 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle unexpected chainId values in getVeaInbox function

The getVeaInbox function does not handle cases where chainId is neither 11155111 nor 10200. This could lead to undefined being returned, causing runtime errors when the returned value is used.

Consider adding an else clause to handle unexpected chainId values by throwing an error:

  } else if (chainId == 10200) {
    return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
+ } else {
+   throw new Error(`Unsupported chainId: ${chainId}`);
  }
📝 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
// Using destination chainId as identifier, Ex: Arbitrum One (42161) -> Ethereum Mainnet (1): Use "1" as chainId
function getVeaInbox(veaInboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
if (chainId == 11155111) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
} else if (chainId == 10200) {
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
}
// Using destination chainId as identifier, Ex: Arbitrum One (42161) -> Ethereum Mainnet (1): Use "1" as chainId
function getVeaInbox(veaInboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
if (chainId == 11155111) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
} else if (chainId == 10200) {
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
} else {
throw new Error(`Unsupported chainId: ${chainId}`);
}
}

}

function getVeaInboxArbToEthProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
function getVeaInboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
}
Comment on lines +28 to +33
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure getVeaInboxProvider handles all possible chainId values

Similar to getVeaInbox, the getVeaInboxProvider function lacks handling for unexpected chainId values, which may result in runtime issues.

Add an else clause to handle unsupported chainId values:

  } else if (chainId == 10200) {
    return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
+ } else {
+   throw new Error(`Unsupported chainId: ${chainId}`);
  }
📝 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
function getVeaInboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
}
function getVeaInboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else {
throw new Error(`Unsupported chainId: ${chainId}`);
}
}

}

function getVeaOutboxArbToEthProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
function getVeaOutbox(veaInboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
}
Comment on lines +36 to +41
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct parameter name to veaOutboxAddress in getVeaOutbox

The parameter veaInboxAddress should be renamed to veaOutboxAddress to accurately reflect its purpose in the getVeaOutbox function.

Apply this diff to correct the parameter name:

-function getVeaOutbox(veaInboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
+function getVeaOutbox(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
  if (chainId == 11155111) {
-   return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
+   return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
  } else if (chainId == 10200) {
-   return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
+   return VeaOutboxArbToGnosis__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
  } else {
    throw new Error(`Unsupported chainId: ${chainId}`);
  }
}
📝 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
function getVeaOutbox(veaInboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
}
function getVeaOutbox(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
} else {
throw new Error(`Unsupported chainId: ${chainId}`);
}
}

}

function getVeaOutboxArbToEth(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
function getVeaOutboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
}
Comment on lines +44 to +49
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct parameter name to veaOutboxAddress in getVeaOutboxProvider

The parameter veaInboxAddress should be renamed to veaOutboxAddress in the getVeaOutboxProvider function for clarity.

Apply this diff:

-function getVeaOutboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
+function getVeaOutboxProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
  if (chainId == 11155111) {
-   return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
+   return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
  } else if (chainId == 10200) {
-   return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
+   return VeaOutboxArbToGnosis__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
  } else {
    throw new Error(`Unsupported chainId: ${chainId}`);
  }
}
📝 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
function getVeaOutboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
}
function getVeaOutboxProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
} else {
throw new Error(`Unsupported chainId: ${chainId}`);
}
}

⚠️ Potential issue

Handle unexpected chainId values in getVeaOutboxProvider

The getVeaOutboxProvider function should handle cases where chainId does not match expected values to prevent unintended behavior.

Add an else clause to manage unsupported chainId values:

  } else if (chainId == 10200) {
    return VeaOutboxArbToGnosis__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
+ } else {
+   throw new Error(`Unsupported chainId: ${chainId}`);
  }
📝 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
function getVeaOutboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
}
function getVeaOutboxProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider, chainId: number) {
if (chainId == 11155111) {
return VeaOutboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
} else if (chainId == 10200) {
return VeaOutboxArbToGnosis__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
} else {
throw new Error(`Unsupported chainId: ${chainId}`);
}
}

}

function getVeaOutboxArbToEthDevnetProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
Expand All @@ -39,19 +57,12 @@ function getVeaOutboxArbToEthDevnet(veaOutboxAddress: string, privateKey: string
return VeaOutboxArbToEthDevnet__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
}

function getVeaOutboxArbToGnosisProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
return VeaOutboxArbToGnosisDevnet__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
}

function getVeaOutboxArbToGnosis(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaOutboxArbToGnosisDevnet__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
}

export {
getVeaOutboxArbToEth,
getWalletRPC,
getVeaOutboxArbToEthDevnetProvider,
getVeaInboxArbToEth,
getVeaInboxArbToEthProvider,
getVeaOutboxArbToEthProvider,
getVeaOutbox,
getVeaInbox,
getVeaOutboxProvider,
getVeaInboxProvider,
getVeaOutboxArbToEthDevnet,
};
57 changes: 32 additions & 25 deletions relayer-cli/src/utils/relay.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { getProofAtCount, getMessageDataToRelay } from "./proof";
import { getVeaOutboxArbToEth } from "./ethers";
import { getVeaOutbox } from "./ethers";
import request from "graphql-request";
import { VeaOutboxArbToEth } from "@kleros/vea-contracts/typechain-types";
import { VeaOutboxArbToEth, VeaOutboxArbToGnosis } from "@kleros/vea-contracts/typechain-types";
import { getBridgeConfig, getInboxSubgraph } from "../consts/bridgeRoutes";
const fs = require("fs");

require("dotenv").config();

const Web3 = require("web3");
const _batchedSend = require("web3-batched-send");
const _contract = require("@kleros/vea-contracts/deployments/sepolia/VeaOutboxArbToEthDevnet.json");

const getCount = async (veaOutbox: VeaOutboxArbToEth, chainid: number): Promise<number> => {
const getCount = async (veaOutbox: VeaOutboxArbToEth | VeaOutboxArbToGnosis, chainid: number): Promise<number> => {
const subgraph = getInboxSubgraph(chainid);
const stateRoot = await veaOutbox.stateRoot();

Expand All @@ -31,8 +30,7 @@ const getCount = async (veaOutbox: VeaOutboxArbToEth, chainid: number): Promise<

const relay = async (chainid: number, nonce: number) => {
const routeParams = getBridgeConfig(chainid);

const veaOutbox = getVeaOutboxArbToEth(routeParams.veaOutbox, process.env.PRIVATE_KEY, routeParams.rpcOutbox);
const veaOutbox = getVeaOutbox(routeParams.veaOutbox, process.env.PRIVATE_KEY, routeParams.rpcOutbox, chainid);
const count = await getCount(veaOutbox, chainid);

const proof = await getProofAtCount(chainid, nonce, count);
Expand All @@ -42,32 +40,41 @@ const relay = async (chainid: number, nonce: number) => {
await txn.wait();
};

const relayBatch = async (chainid: number, nonce: number, iterations: number) => {
const relayBatch = async (chainid: number, nonce: number, maxBatchSize: number, _contract: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update all calls to relayBatch with new _contract parameter

The relayBatch function signature now includes an additional _contract parameter. Ensure that all invocations of this function pass the required contract instance to prevent runtime errors.

const routeParams = getBridgeConfig(chainid);

const web3 = new Web3(routeParams.rpcOutbox);
const batchedSend = _batchedSend(web3, routeParams.rpcOutbox, process.env.PRIVATE_KEY, 0);

const batchedSend = _batchedSend(web3, routeParams.batcher, process.env.PRIVATE_KEY, 0);
const contract = new web3.eth.Contract(_contract.abi, routeParams.veaOutbox);
const veaOutbox = getVeaOutboxArbToEth(routeParams.veaOutbox, process.env.PRIVATE_KEY, routeParams.rpcOutbox);
const veaOutbox = getVeaOutbox(routeParams.veaOutbox, process.env.PRIVATE_KEY, routeParams.rpcOutbox, chainid);
const count = await getCount(veaOutbox, chainid);

let txns = [];

for (let i = 0; i < iterations; i++) {
const proof = await getProofAtCount(chainid, nonce + i, count);
const [to, data] = await getMessageDataToRelay(chainid, nonce + i);
txns.push({
args: [proof, nonce + i, to, data],
method: contract.methods.sendMessage,
to: contract.options.address,
});
while (nonce <= count) {
let batchMessages = 0;
let txns = [];
for (let i = 0; batchMessages < maxBatchSize && nonce + i < count; i++) {
const isMsgRelayed = await veaOutbox.isMsgRelayed(nonce + i);
if (isMsgRelayed) continue;
const proof = await getProofAtCount(chainid, nonce + i, count);
const [to, data] = await getMessageDataToRelay(chainid, nonce + i);
txns.push({
args: [proof, nonce + i, to, data],
method: contract.methods.sendMessage,
to: contract.options.address,
});
batchMessages += 1;
}
try {
await batchedSend(txns);
// Updating nonce to the next message to be sent
nonce += batchMessages + 1;
} catch (error) {
console.error(`Unable to execute messgae batch(${batchMessages} msgs) from nonce:${nonce} `, error);
}
}

await batchedSend(txns);
return nonce;
};

const relayAllFrom = async (chainid: number, nonce: number, msgSender: string): Promise<number> => {
const relayAllFrom = async (chainid: number, nonce: number, msgSender: string, _contract: any): Promise<number> => {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Synchronize function signature changes for relayAllFrom

The relayAllFrom function now includes an additional _contract parameter. Ensure that all calls to this function are updated accordingly and that any related documentation reflects this change.

const routeParams = getBridgeConfig(chainid);

const web3 = new Web3(routeParams.rpcOutbox);
Expand All @@ -80,7 +87,7 @@ const relayAllFrom = async (chainid: number, nonce: number, msgSender: string):
);

const contract = new web3.eth.Contract(_contract.abi, routeParams.veaOutbox);
const veaOutbox = getVeaOutboxArbToEth(routeParams.veaOutbox, process.env.PRIVATE_KEY, routeParams.rpcOutbox);
const veaOutbox = getVeaOutbox(routeParams.veaOutbox, process.env.PRIVATE_KEY, routeParams.rpcOutbox, chainid);
const count = await getCount(veaOutbox, chainid);

if (!count) return null;
Expand Down
Loading
Loading