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/bridger-cli #370

Closed
wants to merge 17 commits into from
Prev Previous commit
Next Next commit
chore: ethers v6 upgrade
Mani Brar authored and Mani Brar committed Jan 2, 2025
commit 590f6ff33e6222dd6a53d31bebcb1de40258012c
13 changes: 6 additions & 7 deletions bridger-cli/src/bridger.test.ts
Copy link
Contributor

Choose a reason for hiding this comment

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

Brilliantly resolved. I left a few comments but I am utterly happy with the job you did

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for all the help 🙌

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's a type mismatch issue for tests which include value : deposit used for mocking, the bot itself works. I wasn't able to solve it for now will raise an issue and fix later.

Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
require("dotenv").config();
import { assert } from "chai";
import { JsonRpcProvider } from "@ethersproject/providers";
import { BigNumber } from "ethers";
import { MockEmitter } from "./utils/emitter";
import { hashClaim } from "./utils/claim";
import { ClaimStruct, hashClaim } from "./utils/claim";
import { getVeaOutbox, getVeaInbox } from "./utils/ethers";
import { watch } from "./bridger";
import { ShutdownSignal } from "./utils/shutdown";
@@ -22,7 +21,7 @@ describe("bridger", function () {

let claimEpoch: number;
let epochPeriod: number;
let deposit: BigNumber;
let deposit: bigint;
let sequencerDelay: number;

const veaInbox = getVeaInbox(
@@ -101,9 +100,9 @@ describe("bridger", function () {

const bridger = `0x${claimData[0].topics[1].slice(26)}`;
const claimBlock = await outboxProvider.getBlock(claimData[0].blockNumber);
const claim = {
const claim: ClaimStruct = {
stateRoot: toBeClaimedStateRoot,
claimer: bridger,
claimer: bridger as `0x${string}`,
timestampClaimed: claimBlock.timestamp,
timestampVerification: 0,
blocknumberVerification: 0,
@@ -304,11 +303,11 @@ describe("bridger", function () {
await veaOutbox.verifySnapshot(claimEpoch, claim);

const balancePreWithdraw = await outboxProvider.getBalance(claimTxn.from);
const contractBalancePreWithdraw = await outboxProvider.getBalance(veaOutbox.address);
const contractBalancePreWithdraw = await outboxProvider.getBalance(process.env.VEAOUTBOX_ADDRESS);

await startBridgerWithTimeout(5000, claimEpoch);
const balancePostWithdraw = await outboxProvider.getBalance(claimTxn.from);
const contractBalancePostWithdraw = await outboxProvider.getBalance(veaOutbox.address);
const contractBalancePostWithdraw = await outboxProvider.getBalance(process.env.VEAOUTBOX_ADDRESS);

assert(balancePostWithdraw.gt(balancePreWithdraw), "Deposit was not withdrawn");
assert(contractBalancePostWithdraw.eq(contractBalancePreWithdraw.sub(deposit)), "Deposit was not withdrawn");
40 changes: 14 additions & 26 deletions bridger-cli/src/bridger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require("dotenv").config();
import { JsonRpcProvider } from "@ethersproject/providers";
import { ethers } from "ethers";
import { EventEmitter } from "events";
import { getLastClaimedEpoch } from "./utils/graphQueries";
@@ -20,12 +21,13 @@ export const watch = async (
emitter.emit(BotEvents.STARTED);
const chainId = Number(process.env.VEAOUTBOX_CHAIN_ID);
const veaInboxAddress = process.env.VEAINBOX_ADDRESS;
const veaInboxProviderURL = process.env.VEAINBOX_PROVIDER;
const veaOutboxAddress = process.env.VEAOUTBOX_ADDRESS;
const veaOutboxProviderURL = process.env.VEAOUTBOX_PROVIDER;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const veaInbox = getVeaInbox(veaInboxAddress, PRIVATE_KEY, veaInboxProviderURL, chainId);
const veaOutbox = getVeaOutbox(veaOutboxAddress, PRIVATE_KEY, veaOutboxProviderURL, chainId);
const veaInboxRPC = process.env.VEAINBOX_PROVIDER;
const veaOutboxRPC = process.env.VEAOUTBOX_PROVIDER;
const veaInbox = getVeaInbox(veaInboxAddress, PRIVATE_KEY, veaInboxRPC, chainId);
const veaOutbox = getVeaOutbox(veaOutboxAddress, PRIVATE_KEY, veaOutboxRPC, chainId);
const veaOutboxProvider = new JsonRpcProvider(veaOutboxRPC);
const epochs = await setEpochRange(veaOutbox, startEpoch);
Copy link
Contributor

Choose a reason for hiding this comment

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

From this line I understand that if the bot shuts down it will start from startEpoch. I am not ultra familiar with the Vea logic, so I just want to double check that doing it is safe.

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's no mechanism to handle bot shutting down yet. The startEpoch, sets the first epoch the bot tries for claim and verification on start up. This enables to set a dynamic start epoch and also helped in testing. That was my main motive.

let verifiableEpoch = epochs[epochs.length - 1] - 1;

@@ -38,27 +40,20 @@ export const watch = async (
emitter.emit(BotEvents.CHECKING, activeEpoch);
let claimableEpochHash = await veaOutbox.claimHashes(activeEpoch);
let outboxStateRoot = await veaOutbox.stateRoot();
const finalizedOutboxBlock = await veaOutbox.provider.getBlock("finalized");
const finalizedOutboxBlock = await veaOutboxProvider.getBlock("finalized");

if (claimableEpochHash == ethers.constants.HashZero && activeEpoch == verifiableEpoch) {
if (claimableEpochHash == ethers.ZeroAddress && activeEpoch == verifiableEpoch) {
// Claim can be made
const savedSnapshot = await veaInbox.snapshots(activeEpoch);
if (savedSnapshot != outboxStateRoot && savedSnapshot != ethers.constants.HashZero) {
if (savedSnapshot != outboxStateRoot && savedSnapshot != ethers.ZeroHash) {
// Its possible that a claim was made for previous epoch but its not verified yet
// Making claim if there are new messages or last claim was challenged.
const claimData = await getLastClaimedEpoch();

if (claimData.challenged || claimData.stateroot != savedSnapshot) {
// Making claim as either last claim was challenged or there are new messages
if (!transactionHandlers[activeEpoch]) {
transactionHandlers[activeEpoch] = new TransactionHandler(
chainId,
activeEpoch,
veaOutbox,
null,
null,
emitter
);
transactionHandlers[activeEpoch] = new TransactionHandler(chainId, activeEpoch, veaOutbox, null, emitter);
}
await transactionHandlers[activeEpoch].makeClaim(savedSnapshot);
} else {
@@ -68,25 +63,18 @@ export const watch = async (
continue;
}
} else {
if (savedSnapshot == ethers.constants.HashZero) {
if (savedSnapshot == ethers.ZeroHash) {
emitter.emit(BotEvents.NO_SNAPSHOT);
} else {
emitter.emit(BotEvents.NO_NEW_MESSAGES);
}
epochs.splice(i, 1);
i--;
}
} else if (claimableEpochHash != ethers.constants.HashZero) {
} else if (claimableEpochHash != ethers.ZeroHash) {
const claim = await fetchClaim(veaOutbox, activeEpoch);
if (!transactionHandlers[activeEpoch]) {
transactionHandlers[activeEpoch] = new TransactionHandler(
chainId,
activeEpoch,
veaOutbox,
claim,
null,
emitter
);
transactionHandlers[activeEpoch] = new TransactionHandler(chainId, activeEpoch, veaOutbox, claim, emitter);
} else {
transactionHandlers[activeEpoch].claim = claim;
}
@@ -107,7 +95,7 @@ export const watch = async (
epochs.splice(i, 1);
i--;
}
} else if (claim.challenger == ethers.constants.AddressZero) {
} else if (claim.challenger == ethers.ZeroAddress) {
// No verification started yet, check if we can start it
await transactionHandler.startVerification(finalizedOutboxBlock.timestamp);
} else {
8 changes: 3 additions & 5 deletions bridger-cli/src/consts/bridgeRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { BigNumber } from "ethers";

interface IBridge {
chain: string;
epochPeriod: number;
deposit: BigNumber;
deposit: bigint;
minChallengePeriod: number;
sequencerDelayLimit: number;
}
@@ -12,14 +10,14 @@ const bridges: { [chainId: number]: IBridge } = {
11155111: {
chain: "sepolia",
epochPeriod: 7200,
deposit: BigNumber.from("1000000000000000000"),
deposit: BigInt("1000000000000000000"),
minChallengePeriod: 10800,
sequencerDelayLimit: 86400,
},
10200: {
chain: "chiado",
epochPeriod: 3600,
deposit: BigNumber.from("1000000000000000000"),
deposit: BigInt("1000000000000000000"),
minChallengePeriod: 10800,
sequencerDelayLimit: 86400,
},
10 changes: 5 additions & 5 deletions bridger-cli/src/utils/claim.test.ts
Copy link
Contributor

Choose a reason for hiding this comment

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

Great job!

Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ describe("snapshotClaim", () => {
timestampVerification: 0,
blocknumberVerification: 0,
honest: 0,
challenger: ethers.constants.AddressZero,
challenger: ethers.ZeroAddress as `0x${string}`,
};
getClaimForEpoch = jest.fn().mockResolvedValue({
stateroot: mockClaim.stateRoot,
@@ -43,7 +43,7 @@ describe("snapshotClaim", () => {
it("should return a valid claim", async () => {
veaOutbox.queryFilter.mockImplementationOnce(() => Promise.resolve([]));
veaOutbox.queryFilter.mockImplementationOnce(() =>
Promise.resolve([{ blockHash: "0x1234", args: { challenger: ethers.constants.AddressZero } }])
Promise.resolve([{ blockHash: "0x1234", args: { challenger: ethers.ZeroAddress } }])
);

const claim = await fetchClaim(veaOutbox, epoch, getClaimForEpoch);
@@ -80,7 +80,7 @@ describe("snapshotClaim", () => {
mockClaim.timestampVerification = 1234;
mockClaim.blocknumberVerification = 1234;
veaOutbox.queryFilter.mockImplementationOnce(() =>
Promise.resolve([{ blockHash: "0x1234", args: { challenger: ethers.constants.AddressZero } }])
Promise.resolve([{ blockHash: "0x1234", args: { challenger: ethers.ZeroAddress } }])
);
veaOutbox.queryFilter.mockImplementationOnce(() => Promise.resolve([]));
getClaimForEpoch.mockResolvedValueOnce({
@@ -110,7 +110,7 @@ describe("snapshotClaim", () => {
{
blockNumber: 1234,
data: mockClaim.stateRoot,
topics: [ethers.constants.AddressZero, `0x${"0".repeat(24)}${mockClaim.claimer.slice(2)}`],
topics: [ethers.ZeroAddress, `0x${"0".repeat(24)}${mockClaim.claimer.slice(2)}`],
},
])
);
@@ -144,7 +144,7 @@ describe("snapshotClaim", () => {
timestampVerification: 0,
blocknumberVerification: 0,
honest: 0,
challenger: ethers.constants.AddressZero,
challenger: ethers.ZeroAddress as `0x${string}`,
};
// Pre calculated from the deployed contracts
const hashOfMockClaim = "0xfee47661ef0432da320c3b4706ff7d412f421b9d1531c33ce8f2e03bfe5dcfa2";
6 changes: 3 additions & 3 deletions bridger-cli/src/utils/claim.ts
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ const fetchClaim = async (
timestampVerification: 0,
blocknumberVerification: 0,
honest: 0,
challenger: ethers.constants.AddressZero,
challenger: ethers.ZeroAddress as `0x${string}`,
};
const [verifyLogs, challengeLogs] = await Promise.all([
veaOutbox.queryFilter(veaOutbox.filters.VerificationStarted(epoch)),
@@ -79,8 +79,8 @@ const fetchClaim = async (
* @example
* const claimHash = hashClaim(claim);
*/
const hashClaim = (claim) => {
return ethers.utils.solidityKeccak256(
const hashClaim = (claim: ClaimStruct) => {
return ethers.solidityPackedKeccak256(
["bytes32", "address", "uint32", "uint32", "uint32", "uint8", "address"],
[
claim.stateRoot,
14 changes: 4 additions & 10 deletions bridger-cli/src/utils/ethers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Wallet } from "@ethersproject/wallet";
import { JsonRpcProvider } from "@ethersproject/providers";
import { Wallet, JsonRpcProvider } from "ethers";
import {
VeaOutboxArbToEth__factory,
VeaOutboxArbToEthDevnet__factory,
@@ -20,16 +19,11 @@ function getWalletRPC(privateKey: string, rpc: JsonRpcProvider) {

// 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) {
const bridge = getBridgeConfig(chainId);
switch (bridge.chain) {
case "sepolia":
case "mainnet":
switch (chainId) {
case 11155111:
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
case "chiado":
case "gnosis":
case 10200:
return VeaInboxArbToGnosis__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
default:
throw new Error(`Unsupported chainId: ${chainId}`);
}
}

Loading
Loading