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

Netlify relayer #275

Open
wants to merge 20 commits into
base: dev
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img alt="Vea" src="https://user-images.githubusercontent.com/22213980/200396519-895ad6ed-2950-476f-89e9-2a648f0fdbce.png?raw=true" width="128">
</a>
</p>

<p align="center">
<b style="font-size: 32px;">VEA</b>
</p>
Expand Down
9 changes: 9 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[build.environment]
RPC_URL_MAINNET="https://rpc.ankr.com/eth"
RPC_GOERLI="https://goerli.infura.io/v3/"
RPC_ARB_GOERLI="https://goerli-rollup.arbitrum.io/rpc"
RPC_CHIADO="https://rpc.eu-central-2.gateway.fm/v3/gnosis/archival/chiado"
VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS="0x9481b3A49ac67d03D9022E6200eFD81850BADDB4"
VEAINBOX_ARBGOERLI_TO_CHIADO_ADDRESS="0xf38b8739635d2F4cb38Bd453453AB9d41fD16300"
VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS="0xDa528e9BE20a8A22437D28Ed6C63bb6d00Ad0032"
VEAINBOX_ARBGOERLI_TO_GOERLI_ADDRESS="0xE99C6177CD8731DE6F108443CcAf7449074f6aED"
15 changes: 6 additions & 9 deletions relayer-cli/.env.dist
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
PRIVATE_KEY=

RPC_CHIADO=https://rpc.chiadochain.net
RPC_GOERLI=
RPC_CHIADO=https://rpc.chiado.gnosis.gateway.fm
RPC_GOERLI=https://goerli.infura.io/v3/

VEAINBOX_ARBGOERLI_TO_GOERLI_ADDRESS=0x906dE43dBef27639b1688Ac46532a16dc07Ce410
VEAINBOX_ARBGOERLI_TO_CHIADO_ADDRESS=0xAb53e341121448Ae259Da8fa17f216Cb0e21199C
VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS=0x906dE43dBef27639b1688Ac46532a16dc07Ce410
VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS=0xAb53e341121448Ae259Da8fa17f216Cb0e21199C

TRANSACTION_BATCHER_CONTRACT_ADDRESS_GOERLI=0xe7953da7751063d0a41ba727c32c762d3523ade8
TRANSACTION_BATCHER_CONTRACT_ADDRESS_CHIADO=0xcC0a08D4BCC5f91ee9a1587608f7a2975EA75d73
VEAINBOX_ARBGOERLI_TO_GOERLI_ADDRESS=0xE99C6177CD8731DE6F108443CcAf7449074f6aED
VEAINBOX_ARBGOERLI_TO_CHIADO_ADDRESS=0xf38b8739635d2F4cb38Bd453453AB9d41fD16300
VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS=0xDa528e9BE20a8A22437D28Ed6C63bb6d00Ad0032
VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS=0x9481b3A49ac67d03D9022E6200eFD81850BADDB4
9 changes: 9 additions & 0 deletions relayer-cli/codegen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
overwrite: true
generates:
./generated/vea-inbox.ts:
schema: "https://api.thegraph.com/subgraphs/name/shotaronowhere/vea-inbox-arbitrum"
documents: "graphql/inbox/*.gql"
plugins:
- "typescript"
- "typescript-operations"
- "typescript-graphql-request"
16 changes: 0 additions & 16 deletions relayer-cli/ecosystem.config.js

This file was deleted.

19 changes: 19 additions & 0 deletions relayer-cli/functions/relayer-devnet-chiado-background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { schedule } from "@netlify/functions";
import { relayAllFor } from "./utils/relay";
import { StatusCodes } from "http-status-codes";

const relayer = async () => {
try {
await relayAllFor(10200, 0, "0xe6aC8CfF97199A67b8121a3Ce3aC98772f90B94b");
} catch (e) {
console.error(e);
return {
statusCode: StatusCodes.BAD_REQUEST,
};
}

return {
statusCode: StatusCodes.OK,
};
};
export const handler = schedule("@hourly", relayer);
19 changes: 19 additions & 0 deletions relayer-cli/functions/relayer-devnet-goerli-background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { schedule } from "@netlify/functions";
import { relayAllFor } from "./utils/relay";
import { StatusCodes } from "http-status-codes";

const relayer = async () => {
try {
await relayAllFor(5, 0, "0xe6aC8CfF97199A67b8121a3Ce3aC98772f90B94b");
} catch (e) {
console.error(e);
return {
statusCode: StatusCodes.BAD_REQUEST,
};
}

return {
statusCode: StatusCodes.OK,
};
};
export const handler = schedule("@hourly", relayer);
48 changes: 48 additions & 0 deletions relayer-cli/functions/utils/ethers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Wallet } from "@ethersproject/wallet";
import { JsonRpcProvider } from "@ethersproject/providers";
import {
VeaOutboxArbToEth__factory,
VeaOutboxArbToEthDevnet__factory,
VeaOutboxArbToGnosisDevnet__factory,
VeaInboxArbToEth__factory,
} from "../../typechain-types";

export function getWallet(privateKey: string, web3ProviderURL: string) {
return new Wallet(privateKey, new JsonRpcProvider(web3ProviderURL));
}

export function getWalletRPC(privateKey: string, rpc: JsonRpcProvider) {
return new Wallet(privateKey, rpc);
}

export function getVeaInboxArbToEth(veaInboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL));
}

export function getVeaInboxArbToEthProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc));
}

export function getVeaOutboxArbToEthProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
}

export function getVeaOutboxArbToEth(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
}

export function getVeaOutboxArbToEthDevnetProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) {
return VeaOutboxArbToEthDevnet__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc));
}

export function getVeaOutboxArbToEthDevnet(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaOutboxArbToEthDevnet__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
}

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

export function getVeaOutboxArbToGnosis(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) {
return VeaOutboxArbToGnosisDevnet__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL));
}
38 changes: 38 additions & 0 deletions relayer-cli/functions/utils/proof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Supported } from "../../types";
import { getProof, supportedChainIdsOutbox } from "./subgraph";

const getProofAtCount = async (
chainid: Supported<typeof supportedChainIdsOutbox>,
nonce: number,
count: number
): Promise<string[]> => {
const proofIndices = getProofIndices(nonce, count);
const rawProof = await getProof(chainid, { proofIndices });
//rawproof is ordered by id, we want same order as proofIndices
rawProof.nodes.sort((a, b) => proofIndices.indexOf(a.id) - proofIndices.indexOf(b.id));
return rawProof.nodes.reduce((acc, node) => {
acc.push(node);
return acc;
}, []);
};

const getProofIndices = (nonce: number, count: number): string[] => {
let proof: string[] = [];
if (nonce >= count) return proof;

const treeDepth = Math.ceil(Math.log2(count));

for (let i = 0; i < treeDepth; i++) {
if (i == 0 && (nonce ^ 1) < count) proof.push((nonce ^ 1).toString()); // sibling
else {
const low = ((nonce >> i) ^ 1) << i;
const high = Math.min(low + Math.pow(2, i) - 1, count - 1);
if (low < count - 1) proof.push(low.toString() + "," + high.toString());
else if (low == count - 1) proof.push(low.toString());
}
}

return proof;
};

export { getProofAtCount };
36 changes: 36 additions & 0 deletions relayer-cli/functions/utils/relay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { getProofAtCount } from "./proof";
import {
getInboxCount,
supportedChainIdsOutbox,
getInboxMsgData,
getInboxNonceFromSender,
rpcUrlOutbox,
veaOutboxFromArbGoerliTo,
} from "./subgraph";
import { getVeaOutboxArbToEth } from "./ethers";
import { Supported } from "../../types";

export const relay = async (chainIdOutbox: Supported<typeof supportedChainIdsOutbox>, nonce: number) => {
const veaOutbox = getVeaOutboxArbToEth(
veaOutboxFromArbGoerliTo[chainIdOutbox],
process.env.PRIVATE_KEY,
rpcUrlOutbox[chainIdOutbox]
);
const stateRoot = await veaOutbox.stateRoot();
const count = await getInboxCount(chainIdOutbox, { stateRoot });
const proof = await getProofAtCount(chainIdOutbox, nonce, count.snapshotSaveds[0].count);
const msgData = await getInboxMsgData(chainIdOutbox, { nonce: [nonce] });
const txn = await veaOutbox.sendMessage(proof, nonce, msgData.messageSents[0].to.id, msgData.messageSents[0].data);
await txn.wait();
};

export const relayAllFor = async (
chainIdOutbox: Supported<typeof supportedChainIdsOutbox>,
nonce: number,
sender: string
) => {
const nonces = await getInboxNonceFromSender(chainIdOutbox, { nonce, msgSender: sender });
for (const n of nonces.messageSents) {
await relay(chainIdOutbox, n.nonce);
}
};
108 changes: 108 additions & 0 deletions relayer-cli/functions/utils/subgraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { GraphQLClient } from "graphql-request";
import { arbitrumGoerli, goerli, gnosisChiado } from "viem/chains";
import {
GetCountQuery,
GetMsgDataQuery,
GetProofQuery,
GetNonceFromQuery,
Sdk,
getSdk,
} from "../../generated/vea-inbox";

import { Supported } from "../../types";

const subgraphInboxArbGoerliFrom = {
[arbitrumGoerli.id]: "https://api.thegraph.com/subgraphs/name/shotaronowhere/vea-inbox-arbgoerli-to-goerli",
[gnosisChiado.id]: "https://api.thegraph.com/subgraphs/name/shotaronowhere/vea-inbox-arbgoerli-to-chiado",
} as const;

export const sdksInboxArbGoerliFrom = Object.entries(subgraphInboxArbGoerliFrom).reduce((acc, [chainId, url]) => {
return {
...acc,
[+chainId]: getSdk(new GraphQLClient(url)),
};
}, {} as Record<Supported<(keyof typeof subgraphInboxArbGoerliFrom)[]>, Sdk>);

export const supportedChainIdsInbox = [arbitrumGoerli.id];

export const supportedChainIdsOutbox = [gnosisChiado.id, goerli.id];

require("dotenv").config();
const {
RPC_URL_CHIADO,
RPC_URL_GOERLI,
RPC_URL_ARB_GOERLI,
VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS,
VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS,
} = process.env;

export const rpcUrlInbox = {
[arbitrumGoerli.id]: RPC_URL_ARB_GOERLI,
};

export const rpcUrlOutbox = {
[gnosisChiado.id]: RPC_URL_CHIADO,
[goerli.id]: RPC_URL_GOERLI,
};

export const veaOutboxFromArbGoerliTo = {
[gnosisChiado.id]: VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS,
[goerli.id]: VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS,
};

export const getInboxCount = async (chainId: Supported<typeof supportedChainIdsOutbox>, params: { stateRoot: any }) => {
let res: GetCountQuery | undefined = undefined;
try {
res = await sdksInboxArbGoerliFrom[chainId as Supported<(keyof typeof subgraphInboxArbGoerliFrom)[]>]["GetCount"](
params
);
} catch (e) {
console.error(e);
}
return res;
};

export const getInboxMsgData = async (
chainId: Supported<typeof supportedChainIdsOutbox>,
params: { nonce: number[] }
) => {
let res: GetMsgDataQuery | undefined = undefined;
try {
res = await sdksInboxArbGoerliFrom[chainId as Supported<(keyof typeof subgraphInboxArbGoerliFrom)[]>]["GetMsgData"](
params
);
} catch (e) {
console.error(e);
}
return res;
};

export const getInboxNonceFromSender = async (
chainId: Supported<typeof supportedChainIdsOutbox>,
params: { nonce: any; msgSender: any }
) => {
let res: GetNonceFromQuery | undefined = undefined;
try {
res = await sdksInboxArbGoerliFrom[chainId as Supported<(keyof typeof subgraphInboxArbGoerliFrom)[]>][
"GetNonceFrom"
](params);
} catch (e) {
console.error(e);
}
return res;
};

export const getProof = async (
chainId: Supported<typeof supportedChainIdsOutbox>,
params: { proofIndices: string | string[] }
) => {
let res: GetProofQuery | undefined = undefined;
try {
res = await sdksInboxArbGoerliFrom[chainId as Supported<(keyof typeof subgraphInboxArbGoerliFrom)[]>]["GetProof"](
params
);
} catch (e) {
console.error(e);
}
return res;
};
Loading
Loading