diff --git a/relayer-cli/.env.dist b/relayer-cli/.env.dist index 8ab6443d..39a8f711 100644 --- a/relayer-cli/.env.dist +++ b/relayer-cli/.env.dist @@ -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 \ No newline at end of file +VEAINBOX_ARBGOERLI_TO_GOERLI_ADDRESS=0xA3FefC6FeE3fc66B9d9a8BEE794736ab71a74c55 +VEAINBOX_ARBGOERLI_TO_CHIADO_ADDRESS=0x660daB9A6436A814a6ae3a6f27b309356a4bE78c +VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS=0x9235A379950B9f01fb3e2961C06912A96DCcef0e +VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS=0xdFd7aDEb43d46FA3f16FB3e27F7fe85c3f5BD89D \ No newline at end of file diff --git a/relayer-cli/codegen.yml b/relayer-cli/codegen.yml new file mode 100644 index 00000000..7e3c04c7 --- /dev/null +++ b/relayer-cli/codegen.yml @@ -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" \ No newline at end of file diff --git a/relayer-cli/ecosystem.config.js b/relayer-cli/ecosystem.config.js deleted file mode 100644 index 57298058..00000000 --- a/relayer-cli/ecosystem.config.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - apps: [ - { - name: "devnet-relayer", - script: "yarn", - args: "start-devnet-relayer", - interpreter: "/bin/bash", - log_date_format: "YYYY-MM-DD HH:mm Z", - watch: false, - autorestart: false, - env: { - NODE_ENV: "development", - }, - }, - ], -}; diff --git a/relayer-cli/functions/relayer-devnet-chiado-background.ts b/relayer-cli/functions/relayer-devnet-chiado-background.ts new file mode 100644 index 00000000..de1156ca --- /dev/null +++ b/relayer-cli/functions/relayer-devnet-chiado-background.ts @@ -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); diff --git a/relayer-cli/functions/relayer-devnet-goerli-background.ts b/relayer-cli/functions/relayer-devnet-goerli-background.ts new file mode 100644 index 00000000..91d1182e --- /dev/null +++ b/relayer-cli/functions/relayer-devnet-goerli-background.ts @@ -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); diff --git a/relayer-cli/src/utils/ethers.ts b/relayer-cli/functions/utils/ethers.ts similarity index 51% rename from relayer-cli/src/utils/ethers.ts rename to relayer-cli/functions/utils/ethers.ts index 4edae0e3..7616be6c 100644 --- a/relayer-cli/src/utils/ethers.ts +++ b/relayer-cli/functions/utils/ethers.ts @@ -7,51 +7,42 @@ import { VeaInboxArbToEth__factory, } from "@kleros/vea-contracts/typechain-types"; -function getWallet(privateKey: string, web3ProviderURL: string) { +export function getWallet(privateKey: string, web3ProviderURL: string) { return new Wallet(privateKey, new JsonRpcProvider(web3ProviderURL)); } -function getWalletRPC(privateKey: string, rpc: JsonRpcProvider) { +export function getWalletRPC(privateKey: string, rpc: JsonRpcProvider) { return new Wallet(privateKey, rpc); } -function getVeaInboxArbToEth(veaInboxAddress: string, privateKey: string, web3ProviderURL: string) { +export function getVeaInboxArbToEth(veaInboxAddress: string, privateKey: string, web3ProviderURL: string) { return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWallet(privateKey, web3ProviderURL)); } -function getVeaInboxArbToEthProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { +export function getVeaInboxArbToEthProvider(veaInboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { return VeaInboxArbToEth__factory.connect(veaInboxAddress, getWalletRPC(privateKey, rpc)); } -function getVeaOutboxArbToEthProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { +export function getVeaOutboxArbToEthProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc)); } -function getVeaOutboxArbToEth(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) { +export function getVeaOutboxArbToEth(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) { return VeaOutboxArbToEth__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL)); } -function getVeaOutboxArbToEthDevnetProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { +export function getVeaOutboxArbToEthDevnetProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { return VeaOutboxArbToEthDevnet__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc)); } -function getVeaOutboxArbToEthDevnet(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) { +export function getVeaOutboxArbToEthDevnet(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) { return VeaOutboxArbToEthDevnet__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL)); } -function getVeaOutboxArbToGnosisProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { +export function getVeaOutboxArbToGnosisProvider(veaOutboxAddress: string, privateKey: string, rpc: JsonRpcProvider) { return VeaOutboxArbToGnosisDevnet__factory.connect(veaOutboxAddress, getWalletRPC(privateKey, rpc)); } -function getVeaOutboxArbToGnosis(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) { +export function getVeaOutboxArbToGnosis(veaOutboxAddress: string, privateKey: string, web3ProviderURL: string) { return VeaOutboxArbToGnosisDevnet__factory.connect(veaOutboxAddress, getWallet(privateKey, web3ProviderURL)); } - -export { - getVeaOutboxArbToEth, - getWalletRPC, - getVeaOutboxArbToEthDevnetProvider, - getVeaInboxArbToEth, - getVeaInboxArbToEthProvider, - getVeaOutboxArbToEthProvider, -}; diff --git a/relayer-cli/functions/utils/proof.ts b/relayer-cli/functions/utils/proof.ts new file mode 100644 index 00000000..488b8f4b --- /dev/null +++ b/relayer-cli/functions/utils/proof.ts @@ -0,0 +1,38 @@ +import { Supported } from "../../types"; +import { getProof, supportedChainIdsOutbox } from "./subgraph"; + +const getProofAtCount = async ( + chainid: Supported, + nonce: number, + count: number +): Promise => { + 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 }; diff --git a/relayer-cli/functions/utils/relay.ts b/relayer-cli/functions/utils/relay.ts new file mode 100644 index 00000000..57c1f81d --- /dev/null +++ b/relayer-cli/functions/utils/relay.ts @@ -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, 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, + nonce: number, + sender: string +) => { + const nonces = await getInboxNonceFromSender(chainIdOutbox, { nonce, msgSender: sender }); + for (const n of nonces.messageSents) { + await relay(chainIdOutbox, n.nonce); + } +}; diff --git a/relayer-cli/functions/utils/subgraph.ts b/relayer-cli/functions/utils/subgraph.ts new file mode 100644 index 00000000..fbfc52f6 --- /dev/null +++ b/relayer-cli/functions/utils/subgraph.ts @@ -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, 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, 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, + 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, + 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, + 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; +}; diff --git a/relayer-cli/generated/vea-inbox.ts b/relayer-cli/generated/vea-inbox.ts new file mode 100644 index 00000000..670ee7bc --- /dev/null +++ b/relayer-cli/generated/vea-inbox.ts @@ -0,0 +1,882 @@ +import { GraphQLClient } from "graphql-request"; +import { GraphQLClientRequestHeaders } from "graphql-request/build/cjs/types"; +import gql from "graphql-tag"; +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends " $fragmentName" | "__typename" ? T[P] : never }; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: { input: string; output: string }; + String: { input: string; output: string }; + Boolean: { input: boolean; output: boolean }; + Int: { input: number; output: number }; + Float: { input: number; output: number }; + BigDecimal: { input: any; output: any }; + BigInt: { input: any; output: any }; + Bytes: { input: any; output: any }; + Int8: { input: any; output: any }; +}; + +export type BlockChangedFilter = { + number_gte: Scalars["Int"]["input"]; +}; + +export type Block_Height = { + hash?: InputMaybe; + number?: InputMaybe; + number_gte?: InputMaybe; +}; + +export type MessageSent = { + __typename?: "MessageSent"; + blockNumber: Scalars["BigInt"]["output"]; + blockTimestamp: Scalars["BigInt"]["output"]; + data: Scalars["Bytes"]["output"]; + epoch: Scalars["BigInt"]["output"]; + id: Scalars["Bytes"]["output"]; + msgSender: Sender; + node: Node; + nonce: Scalars["BigInt"]["output"]; + to: Receiver; + transactionHash: Scalars["Bytes"]["output"]; +}; + +export type MessageSent_Filter = { + /** Filter for the block changed event. */ + _change_block?: InputMaybe; + and?: InputMaybe>>; + blockNumber?: InputMaybe; + blockNumber_gt?: InputMaybe; + blockNumber_gte?: InputMaybe; + blockNumber_in?: InputMaybe>; + blockNumber_lt?: InputMaybe; + blockNumber_lte?: InputMaybe; + blockNumber_not?: InputMaybe; + blockNumber_not_in?: InputMaybe>; + blockTimestamp?: InputMaybe; + blockTimestamp_gt?: InputMaybe; + blockTimestamp_gte?: InputMaybe; + blockTimestamp_in?: InputMaybe>; + blockTimestamp_lt?: InputMaybe; + blockTimestamp_lte?: InputMaybe; + blockTimestamp_not?: InputMaybe; + blockTimestamp_not_in?: InputMaybe>; + data?: InputMaybe; + data_contains?: InputMaybe; + data_gt?: InputMaybe; + data_gte?: InputMaybe; + data_in?: InputMaybe>; + data_lt?: InputMaybe; + data_lte?: InputMaybe; + data_not?: InputMaybe; + data_not_contains?: InputMaybe; + data_not_in?: InputMaybe>; + epoch?: InputMaybe; + epoch_gt?: InputMaybe; + epoch_gte?: InputMaybe; + epoch_in?: InputMaybe>; + epoch_lt?: InputMaybe; + epoch_lte?: InputMaybe; + epoch_not?: InputMaybe; + epoch_not_in?: InputMaybe>; + id?: InputMaybe; + id_contains?: InputMaybe; + id_gt?: InputMaybe; + id_gte?: InputMaybe; + id_in?: InputMaybe>; + id_lt?: InputMaybe; + id_lte?: InputMaybe; + id_not?: InputMaybe; + id_not_contains?: InputMaybe; + id_not_in?: InputMaybe>; + msgSender?: InputMaybe; + msgSender_?: InputMaybe; + msgSender_contains?: InputMaybe; + msgSender_contains_nocase?: InputMaybe; + msgSender_ends_with?: InputMaybe; + msgSender_ends_with_nocase?: InputMaybe; + msgSender_gt?: InputMaybe; + msgSender_gte?: InputMaybe; + msgSender_in?: InputMaybe>; + msgSender_lt?: InputMaybe; + msgSender_lte?: InputMaybe; + msgSender_not?: InputMaybe; + msgSender_not_contains?: InputMaybe; + msgSender_not_contains_nocase?: InputMaybe; + msgSender_not_ends_with?: InputMaybe; + msgSender_not_ends_with_nocase?: InputMaybe; + msgSender_not_in?: InputMaybe>; + msgSender_not_starts_with?: InputMaybe; + msgSender_not_starts_with_nocase?: InputMaybe; + msgSender_starts_with?: InputMaybe; + msgSender_starts_with_nocase?: InputMaybe; + node?: InputMaybe; + node_?: InputMaybe; + node_contains?: InputMaybe; + node_contains_nocase?: InputMaybe; + node_ends_with?: InputMaybe; + node_ends_with_nocase?: InputMaybe; + node_gt?: InputMaybe; + node_gte?: InputMaybe; + node_in?: InputMaybe>; + node_lt?: InputMaybe; + node_lte?: InputMaybe; + node_not?: InputMaybe; + node_not_contains?: InputMaybe; + node_not_contains_nocase?: InputMaybe; + node_not_ends_with?: InputMaybe; + node_not_ends_with_nocase?: InputMaybe; + node_not_in?: InputMaybe>; + node_not_starts_with?: InputMaybe; + node_not_starts_with_nocase?: InputMaybe; + node_starts_with?: InputMaybe; + node_starts_with_nocase?: InputMaybe; + nonce?: InputMaybe; + nonce_gt?: InputMaybe; + nonce_gte?: InputMaybe; + nonce_in?: InputMaybe>; + nonce_lt?: InputMaybe; + nonce_lte?: InputMaybe; + nonce_not?: InputMaybe; + nonce_not_in?: InputMaybe>; + or?: InputMaybe>>; + to?: InputMaybe; + to_?: InputMaybe; + to_contains?: InputMaybe; + to_contains_nocase?: InputMaybe; + to_ends_with?: InputMaybe; + to_ends_with_nocase?: InputMaybe; + to_gt?: InputMaybe; + to_gte?: InputMaybe; + to_in?: InputMaybe>; + to_lt?: InputMaybe; + to_lte?: InputMaybe; + to_not?: InputMaybe; + to_not_contains?: InputMaybe; + to_not_contains_nocase?: InputMaybe; + to_not_ends_with?: InputMaybe; + to_not_ends_with_nocase?: InputMaybe; + to_not_in?: InputMaybe>; + to_not_starts_with?: InputMaybe; + to_not_starts_with_nocase?: InputMaybe; + to_starts_with?: InputMaybe; + to_starts_with_nocase?: InputMaybe; + transactionHash?: InputMaybe; + transactionHash_contains?: InputMaybe; + transactionHash_gt?: InputMaybe; + transactionHash_gte?: InputMaybe; + transactionHash_in?: InputMaybe>; + transactionHash_lt?: InputMaybe; + transactionHash_lte?: InputMaybe; + transactionHash_not?: InputMaybe; + transactionHash_not_contains?: InputMaybe; + transactionHash_not_in?: InputMaybe>; +}; + +export enum MessageSent_OrderBy { + BlockNumber = "blockNumber", + BlockTimestamp = "blockTimestamp", + Data = "data", + Epoch = "epoch", + Id = "id", + MsgSender = "msgSender", + MsgSenderId = "msgSender__id", + Node = "node", + NodeHash = "node__hash", + NodeId = "node__id", + Nonce = "nonce", + To = "to", + ToId = "to__id", + TransactionHash = "transactionHash", +} + +export type Node = { + __typename?: "Node"; + hash: Scalars["Bytes"]["output"]; + id: Scalars["ID"]["output"]; +}; + +export type Node_Filter = { + /** Filter for the block changed event. */ + _change_block?: InputMaybe; + and?: InputMaybe>>; + hash?: InputMaybe; + hash_contains?: InputMaybe; + hash_gt?: InputMaybe; + hash_gte?: InputMaybe; + hash_in?: InputMaybe>; + hash_lt?: InputMaybe; + hash_lte?: InputMaybe; + hash_not?: InputMaybe; + hash_not_contains?: InputMaybe; + hash_not_in?: InputMaybe>; + id?: InputMaybe; + id_gt?: InputMaybe; + id_gte?: InputMaybe; + id_in?: InputMaybe>; + id_lt?: InputMaybe; + id_lte?: InputMaybe; + id_not?: InputMaybe; + id_not_in?: InputMaybe>; + or?: InputMaybe>>; +}; + +export enum Node_OrderBy { + Hash = "hash", + Id = "id", +} + +/** Defines the order direction, either ascending or descending */ +export enum OrderDirection { + Asc = "asc", + Desc = "desc", +} + +export type Query = { + __typename?: "Query"; + /** Access to subgraph metadata */ + _meta?: Maybe<_Meta_>; + messageSent?: Maybe; + messageSents: Array; + node?: Maybe; + nodes: Array; + receiver?: Maybe; + receivers: Array; + sender?: Maybe; + senders: Array; + snapshotSaved?: Maybe; + snapshotSaveds: Array; + snapshotSent?: Maybe; + snapshotSents: Array; +}; + +export type Query_MetaArgs = { + block?: InputMaybe; +}; + +export type QueryMessageSentArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type QueryMessageSentsArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type QueryNodeArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type QueryNodesArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type QueryReceiverArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type QueryReceiversArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type QuerySenderArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type QuerySendersArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type QuerySnapshotSavedArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type QuerySnapshotSavedsArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type QuerySnapshotSentArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type QuerySnapshotSentsArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type Receiver = { + __typename?: "Receiver"; + id: Scalars["Bytes"]["output"]; + messages: Array; +}; + +export type ReceiverMessagesArgs = { + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + where?: InputMaybe; +}; + +export type Receiver_Filter = { + /** Filter for the block changed event. */ + _change_block?: InputMaybe; + and?: InputMaybe>>; + id?: InputMaybe; + id_contains?: InputMaybe; + id_gt?: InputMaybe; + id_gte?: InputMaybe; + id_in?: InputMaybe>; + id_lt?: InputMaybe; + id_lte?: InputMaybe; + id_not?: InputMaybe; + id_not_contains?: InputMaybe; + id_not_in?: InputMaybe>; + messages_?: InputMaybe; + or?: InputMaybe>>; +}; + +export enum Receiver_OrderBy { + Id = "id", + Messages = "messages", +} + +export type Sender = { + __typename?: "Sender"; + id: Scalars["Bytes"]["output"]; + messages: Array; +}; + +export type SenderMessagesArgs = { + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + where?: InputMaybe; +}; + +export type Sender_Filter = { + /** Filter for the block changed event. */ + _change_block?: InputMaybe; + and?: InputMaybe>>; + id?: InputMaybe; + id_contains?: InputMaybe; + id_gt?: InputMaybe; + id_gte?: InputMaybe; + id_in?: InputMaybe>; + id_lt?: InputMaybe; + id_lte?: InputMaybe; + id_not?: InputMaybe; + id_not_contains?: InputMaybe; + id_not_in?: InputMaybe>; + messages_?: InputMaybe; + or?: InputMaybe>>; +}; + +export enum Sender_OrderBy { + Id = "id", + Messages = "messages", +} + +export type SnapshotSaved = { + __typename?: "SnapshotSaved"; + blockNumber: Scalars["BigInt"]["output"]; + blockTimestamp: Scalars["BigInt"]["output"]; + count: Scalars["BigInt"]["output"]; + epoch: Scalars["BigInt"]["output"]; + id: Scalars["Bytes"]["output"]; + stateRoot: Scalars["Bytes"]["output"]; + transactionHash: Scalars["Bytes"]["output"]; +}; + +export type SnapshotSaved_Filter = { + /** Filter for the block changed event. */ + _change_block?: InputMaybe; + and?: InputMaybe>>; + blockNumber?: InputMaybe; + blockNumber_gt?: InputMaybe; + blockNumber_gte?: InputMaybe; + blockNumber_in?: InputMaybe>; + blockNumber_lt?: InputMaybe; + blockNumber_lte?: InputMaybe; + blockNumber_not?: InputMaybe; + blockNumber_not_in?: InputMaybe>; + blockTimestamp?: InputMaybe; + blockTimestamp_gt?: InputMaybe; + blockTimestamp_gte?: InputMaybe; + blockTimestamp_in?: InputMaybe>; + blockTimestamp_lt?: InputMaybe; + blockTimestamp_lte?: InputMaybe; + blockTimestamp_not?: InputMaybe; + blockTimestamp_not_in?: InputMaybe>; + count?: InputMaybe; + count_gt?: InputMaybe; + count_gte?: InputMaybe; + count_in?: InputMaybe>; + count_lt?: InputMaybe; + count_lte?: InputMaybe; + count_not?: InputMaybe; + count_not_in?: InputMaybe>; + epoch?: InputMaybe; + epoch_gt?: InputMaybe; + epoch_gte?: InputMaybe; + epoch_in?: InputMaybe>; + epoch_lt?: InputMaybe; + epoch_lte?: InputMaybe; + epoch_not?: InputMaybe; + epoch_not_in?: InputMaybe>; + id?: InputMaybe; + id_contains?: InputMaybe; + id_gt?: InputMaybe; + id_gte?: InputMaybe; + id_in?: InputMaybe>; + id_lt?: InputMaybe; + id_lte?: InputMaybe; + id_not?: InputMaybe; + id_not_contains?: InputMaybe; + id_not_in?: InputMaybe>; + or?: InputMaybe>>; + stateRoot?: InputMaybe; + stateRoot_contains?: InputMaybe; + stateRoot_gt?: InputMaybe; + stateRoot_gte?: InputMaybe; + stateRoot_in?: InputMaybe>; + stateRoot_lt?: InputMaybe; + stateRoot_lte?: InputMaybe; + stateRoot_not?: InputMaybe; + stateRoot_not_contains?: InputMaybe; + stateRoot_not_in?: InputMaybe>; + transactionHash?: InputMaybe; + transactionHash_contains?: InputMaybe; + transactionHash_gt?: InputMaybe; + transactionHash_gte?: InputMaybe; + transactionHash_in?: InputMaybe>; + transactionHash_lt?: InputMaybe; + transactionHash_lte?: InputMaybe; + transactionHash_not?: InputMaybe; + transactionHash_not_contains?: InputMaybe; + transactionHash_not_in?: InputMaybe>; +}; + +export enum SnapshotSaved_OrderBy { + BlockNumber = "blockNumber", + BlockTimestamp = "blockTimestamp", + Count = "count", + Epoch = "epoch", + Id = "id", + StateRoot = "stateRoot", + TransactionHash = "transactionHash", +} + +export type SnapshotSent = { + __typename?: "SnapshotSent"; + blockNumber: Scalars["BigInt"]["output"]; + blockTimestamp: Scalars["BigInt"]["output"]; + epochSent: Scalars["BigInt"]["output"]; + id: Scalars["Bytes"]["output"]; + ticketId: Scalars["Bytes"]["output"]; + transactionHash: Scalars["Bytes"]["output"]; +}; + +export type SnapshotSent_Filter = { + /** Filter for the block changed event. */ + _change_block?: InputMaybe; + and?: InputMaybe>>; + blockNumber?: InputMaybe; + blockNumber_gt?: InputMaybe; + blockNumber_gte?: InputMaybe; + blockNumber_in?: InputMaybe>; + blockNumber_lt?: InputMaybe; + blockNumber_lte?: InputMaybe; + blockNumber_not?: InputMaybe; + blockNumber_not_in?: InputMaybe>; + blockTimestamp?: InputMaybe; + blockTimestamp_gt?: InputMaybe; + blockTimestamp_gte?: InputMaybe; + blockTimestamp_in?: InputMaybe>; + blockTimestamp_lt?: InputMaybe; + blockTimestamp_lte?: InputMaybe; + blockTimestamp_not?: InputMaybe; + blockTimestamp_not_in?: InputMaybe>; + epochSent?: InputMaybe; + epochSent_gt?: InputMaybe; + epochSent_gte?: InputMaybe; + epochSent_in?: InputMaybe>; + epochSent_lt?: InputMaybe; + epochSent_lte?: InputMaybe; + epochSent_not?: InputMaybe; + epochSent_not_in?: InputMaybe>; + id?: InputMaybe; + id_contains?: InputMaybe; + id_gt?: InputMaybe; + id_gte?: InputMaybe; + id_in?: InputMaybe>; + id_lt?: InputMaybe; + id_lte?: InputMaybe; + id_not?: InputMaybe; + id_not_contains?: InputMaybe; + id_not_in?: InputMaybe>; + or?: InputMaybe>>; + ticketId?: InputMaybe; + ticketId_contains?: InputMaybe; + ticketId_gt?: InputMaybe; + ticketId_gte?: InputMaybe; + ticketId_in?: InputMaybe>; + ticketId_lt?: InputMaybe; + ticketId_lte?: InputMaybe; + ticketId_not?: InputMaybe; + ticketId_not_contains?: InputMaybe; + ticketId_not_in?: InputMaybe>; + transactionHash?: InputMaybe; + transactionHash_contains?: InputMaybe; + transactionHash_gt?: InputMaybe; + transactionHash_gte?: InputMaybe; + transactionHash_in?: InputMaybe>; + transactionHash_lt?: InputMaybe; + transactionHash_lte?: InputMaybe; + transactionHash_not?: InputMaybe; + transactionHash_not_contains?: InputMaybe; + transactionHash_not_in?: InputMaybe>; +}; + +export enum SnapshotSent_OrderBy { + BlockNumber = "blockNumber", + BlockTimestamp = "blockTimestamp", + EpochSent = "epochSent", + Id = "id", + TicketId = "ticketId", + TransactionHash = "transactionHash", +} + +export type Subscription = { + __typename?: "Subscription"; + /** Access to subgraph metadata */ + _meta?: Maybe<_Meta_>; + messageSent?: Maybe; + messageSents: Array; + node?: Maybe; + nodes: Array; + receiver?: Maybe; + receivers: Array; + sender?: Maybe; + senders: Array; + snapshotSaved?: Maybe; + snapshotSaveds: Array; + snapshotSent?: Maybe; + snapshotSents: Array; +}; + +export type Subscription_MetaArgs = { + block?: InputMaybe; +}; + +export type SubscriptionMessageSentArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type SubscriptionMessageSentsArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type SubscriptionNodeArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type SubscriptionNodesArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type SubscriptionReceiverArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type SubscriptionReceiversArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type SubscriptionSenderArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type SubscriptionSendersArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type SubscriptionSnapshotSavedArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type SubscriptionSnapshotSavedsArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type SubscriptionSnapshotSentArgs = { + block?: InputMaybe; + id: Scalars["ID"]["input"]; + subgraphError?: _SubgraphErrorPolicy_; +}; + +export type SubscriptionSnapshotSentsArgs = { + block?: InputMaybe; + first?: InputMaybe; + orderBy?: InputMaybe; + orderDirection?: InputMaybe; + skip?: InputMaybe; + subgraphError?: _SubgraphErrorPolicy_; + where?: InputMaybe; +}; + +export type _Block_ = { + __typename?: "_Block_"; + /** The hash of the block */ + hash?: Maybe; + /** The block number */ + number: Scalars["Int"]["output"]; + /** Integer representation of the timestamp stored in blocks for the chain */ + timestamp?: Maybe; +}; + +/** The type for the top-level _meta field */ +export type _Meta_ = { + __typename?: "_Meta_"; + /** + * Information about a specific subgraph block. The hash of the block + * will be null if the _meta field has a block constraint that asks for + * a block number. It will be filled if the _meta field has no block constraint + * and therefore asks for the latest block + * + */ + block: _Block_; + /** The deployment ID */ + deployment: Scalars["String"]["output"]; + /** If `true`, the subgraph encountered indexing errors at some past block */ + hasIndexingErrors: Scalars["Boolean"]["output"]; +}; + +export enum _SubgraphErrorPolicy_ { + /** Data will be returned even if the subgraph has indexing errors */ + Allow = "allow", + /** If the subgraph has indexing errors, data will be omitted. The default. */ + Deny = "deny", +} + +export type GetCountQueryVariables = Exact<{ + stateRoot: Scalars["Bytes"]["input"]; +}>; + +export type GetCountQuery = { + __typename?: "Query"; + snapshotSaveds: Array<{ __typename?: "SnapshotSaved"; count: any }>; +}; + +export type GetMsgDataQueryVariables = Exact<{ + nonce: Array | Scalars["BigInt"]["input"]; +}>; + +export type GetMsgDataQuery = { + __typename?: "Query"; + messageSents: Array<{ __typename?: "MessageSent"; nonce: any; data: any; to: { __typename?: "Receiver"; id: any } }>; +}; + +export type GetNonceFromQueryVariables = Exact<{ + nonce: Scalars["BigInt"]["input"]; + msgSender: Scalars["Bytes"]["input"]; +}>; + +export type GetNonceFromQuery = { + __typename?: "Query"; + messageSents: Array<{ __typename?: "MessageSent"; nonce: any }>; +}; + +export type GetProofQueryVariables = Exact<{ + proofIndices: Array | Scalars["ID"]["input"]; +}>; + +export type GetProofQuery = { __typename?: "Query"; nodes: Array<{ __typename?: "Node"; id: string; hash: any }> }; + +export const GetCountDocument = gql` + query GetCount($stateRoot: Bytes!) { + snapshotSaveds(where: { stateRoot: $stateRoot }) { + count + } + } +`; +export const GetMsgDataDocument = gql` + query GetMsgData($nonce: [BigInt!]!) { + messageSents(first: 5, where: { nonce_in: nonce }) { + nonce + to { + id + } + data + } + } +`; +export const GetNonceFromDocument = gql` + query GetNonceFrom($nonce: BigInt!, $msgSender: Bytes!) { + messageSents( + first: 1000 + where: { nonce_gte: $nonce, msgSender_: { id: $msgSender } } + orderBy: nonce + orderDirection: asc + ) { + nonce + } + } +`; +export const GetProofDocument = gql` + query GetProof($proofIndices: [ID!]!) { + nodes(first: 100, where: { id_in: $proofIndices }) { + id + hash + } + } +`; + +export type SdkFunctionWrapper = ( + action: (requestHeaders?: Record) => Promise, + operationName: string, + operationType?: string +) => Promise; + +const defaultWrapper: SdkFunctionWrapper = (action, _operationName, _operationType) => action(); + +export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) { + return { + GetCount(variables: GetCountQueryVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper( + (wrappedRequestHeaders) => + client.request(GetCountDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), + "GetCount", + "query" + ); + }, + GetMsgData( + variables: GetMsgDataQueryVariables, + requestHeaders?: GraphQLClientRequestHeaders + ): Promise { + return withWrapper( + (wrappedRequestHeaders) => + client.request(GetMsgDataDocument, variables, { + ...requestHeaders, + ...wrappedRequestHeaders, + }), + "GetMsgData", + "query" + ); + }, + GetNonceFrom( + variables: GetNonceFromQueryVariables, + requestHeaders?: GraphQLClientRequestHeaders + ): Promise { + return withWrapper( + (wrappedRequestHeaders) => + client.request(GetNonceFromDocument, variables, { + ...requestHeaders, + ...wrappedRequestHeaders, + }), + "GetNonceFrom", + "query" + ); + }, + GetProof(variables: GetProofQueryVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper( + (wrappedRequestHeaders) => + client.request(GetProofDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), + "GetProof", + "query" + ); + }, + }; +} +export type Sdk = ReturnType; diff --git a/relayer-cli/graphql/inbox/count.gql b/relayer-cli/graphql/inbox/count.gql new file mode 100644 index 00000000..a6e1c441 --- /dev/null +++ b/relayer-cli/graphql/inbox/count.gql @@ -0,0 +1,7 @@ +query GetCount( + $stateRoot: Bytes! + ){ + snapshotSaveds(where: { stateRoot: $stateRoot }) { + count + } +} \ No newline at end of file diff --git a/relayer-cli/graphql/inbox/msgData.gql b/relayer-cli/graphql/inbox/msgData.gql new file mode 100644 index 00000000..a666451a --- /dev/null +++ b/relayer-cli/graphql/inbox/msgData.gql @@ -0,0 +1,11 @@ +query GetMsgData( + $nonce: [BigInt!]! + ){ + messageSents(first: 5, where: {nonce_in: nonce}) { + nonce + to { + id + } + data + } +} \ No newline at end of file diff --git a/relayer-cli/graphql/inbox/nonce.gql b/relayer-cli/graphql/inbox/nonce.gql new file mode 100644 index 00000000..ce864037 --- /dev/null +++ b/relayer-cli/graphql/inbox/nonce.gql @@ -0,0 +1,8 @@ +query GetNonceFrom( + $nonce: BigInt!, + $msgSender: Bytes! + ){ + messageSents(first: 1000, where: {nonce_gte: $nonce, msgSender_: {id: $msgSender} }, orderBy: nonce, orderDirection: asc) { + nonce + } +} \ No newline at end of file diff --git a/relayer-cli/graphql/inbox/proof.gql b/relayer-cli/graphql/inbox/proof.gql new file mode 100644 index 00000000..08960ab5 --- /dev/null +++ b/relayer-cli/graphql/inbox/proof.gql @@ -0,0 +1,8 @@ +query GetProof( + $proofIndices: [ID!]! + ){ + nodes(first: 100, where: {id_in: $proofIndices}) { + id + hash + } +} \ No newline at end of file diff --git a/relayer-cli/package.json b/relayer-cli/package.json index 6eeac6c9..23143210 100644 --- a/relayer-cli/package.json +++ b/relayer-cli/package.json @@ -10,14 +10,22 @@ "yarn": "3.3.1" }, "scripts": { - "start-devnet-relayer": "npx ts-node ./src/devnetRelayExample.ts" + "start-devnet-relayer": "npx ts-node ./src/devnetRelayExample.ts", + "generate": "graphql-codegen --config codegen.yml" }, "dependencies": { + "@graphql-codegen/cli": "^5.0.0", + "@graphql-codegen/typescript": "^4.0.1", + "@graphql-codegen/typescript-graphql-request": "^6.0.0", + "@graphql-codegen/typescript-operations": "^4.0.1", "@kleros/vea-contracts": "workspace:^", + "@netlify/functions": "^2.3.0", "@typechain/ethers-v5": "^10.2.0", "dotenv": "^16.0.3", + "http-status-codes": "^2.3.0", "pm2": "^5.2.2", "typescript": "^4.9.5", + "viem": "^1.16.6", "web3": "^1.8.2", "web3-batched-send": "^1.0.3" } diff --git a/relayer-cli/src/devnetRelayExample.ts b/relayer-cli/src/devnetRelayExample.ts deleted file mode 100644 index 140aaa37..00000000 --- a/relayer-cli/src/devnetRelayExample.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { relayAllFrom } from "./utils/relay"; -import * as fs from "fs"; -require("dotenv").config(); - -let chain_ids = [5, 10200]; -const epochPeriod = 1800; // 30 min -["SIGINT", "SIGTERM", "SIGQUIT", "EXIT", "MODULE_NOT_FOUND"].forEach((signal) => - process.on(signal, async () => { - console.log("exit"); - 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); - } - } - process.exit(0); - }) -); - -(async () => { - while (1) { - for (const chain_id of chain_ids) { - let nonce = await initialize(chain_id); - const senderGateway = - chain_id === 10200 - ? "0xe6aC8CfF97199A67b8121a3Ce3aC98772f90B94b" - : "0x177AfBF3cda970024Efa901516735aF9c3B894a4"; - nonce = await relayAllFrom(chain_id, nonce, senderGateway); - await updateStateFile(chain_id, Math.floor(Date.now() / 1000), nonce); - } - const currentTS = Math.floor(Date.now() / 1000); - const delayAmount = (epochPeriod - (currentTS % epochPeriod)) * 1000 + 300 * 1000; - console.log("waiting for the next epoch. . .", Math.floor(delayAmount / 1000), "seconds"); - await delay(delayAmount); - } -})(); - -async function initialize(chain_id: number): Promise { - if (chain_id !== 5 && chain_id !== 10200) 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" }); - - const state_file_name = "./state/" + chain_id + ".json"; - if (!fs.existsSync(state_file_name)) { - // 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_name); - - 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)); -} diff --git a/relayer-cli/src/state/10200.json b/relayer-cli/src/state/10200.json deleted file mode 100644 index f55420f7..00000000 --- a/relayer-cli/src/state/10200.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "ts": 1683895122, - "nonce": "6" -} diff --git a/relayer-cli/src/state/5.json b/relayer-cli/src/state/5.json deleted file mode 100644 index b97567ec..00000000 --- a/relayer-cli/src/state/5.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "ts": 1683895113, - "nonce": "6" -} diff --git a/relayer-cli/src/utils/proof.ts b/relayer-cli/src/utils/proof.ts deleted file mode 100644 index 2dcd12fa..00000000 --- a/relayer-cli/src/utils/proof.ts +++ /dev/null @@ -1,85 +0,0 @@ -import request from "graphql-request"; - -const getMessageDataToRelay = async (chainid: number, nonce: number) => { - try { - const subgraph = getSubgraph(chainid); - - const result = await request( - `https://api.thegraph.com/subgraphs/name/shotaronowhere/${subgraph}`, - `{ - messageSents(first: 5, where: {nonce: ${nonce}}) { - nonce - to { - id - } - data - } - }` - ); - - return [result[`messageSents`][0].to.id, result[`messageSents`][0].data]; - } catch (e) { - console.log(e); - } -}; - -const getProofAtCount = async (chainid: number, nonce: number, count: number): Promise => { - const proofIndices = getProofIndices(nonce, count); - - let query = "{"; - for (let i = 0; i < proofIndices.length; i++) { - query += `layer${i}: nodes(first: 1, where: {id: "${proofIndices[i]}"}) { - hash - }`; - } - query += "}"; - - try { - const subgraph = getSubgraph(chainid); - - const result = await request(`https://api.thegraph.com/subgraphs/name/shotaronowhere/${subgraph}`, query); - - const proof = []; - - for (let i = 0; i < proofIndices.length; i++) { - proof.push(result[`layer${i}`][0].hash); - } - - return proof; - } catch (e) { - console.log(e); - return []; - } -}; - -const getProofIndices = (nonce: number, count: number) => { - let proof = []; - 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; -}; - -const getSubgraph = (chainid: number): string => { - switch (chainid) { - case 5: - return "vea-inbox-arbgoerli-to-goerli"; - case 10200: - return "vea-inbox-arbgoerli-to-chiado"; - default: - throw new Error("Invalid chainid"); - } -}; - -export { getProofAtCount, getSubgraph, getMessageDataToRelay }; diff --git a/relayer-cli/src/utils/relay.ts b/relayer-cli/src/utils/relay.ts deleted file mode 100644 index c6f6d76b..00000000 --- a/relayer-cli/src/utils/relay.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { getProofAtCount, getMessageDataToRelay, getSubgraph } from "./proof"; -import { getVeaOutboxArbToEth } from "./ethers"; -import request from "graphql-request"; -import { VeaInboxArbToEth, VeaOutboxArbToEth } from "@kleros/vea-contracts/typechain-types"; -const fs = require("fs"); - -require("dotenv").config(); - -const Web3 = require("web3"); -const _batchedSend = require("web3-batched-send"); -const _contract = require("@kleros/vea-contracts/deployments/goerli/VeaOutboxArbToEthDevnet.json"); - -const getParams = (chainid: number): [string, string, string] => { - if (chainid !== 5 && chainid !== 10200) throw new Error("Invalid chainid"); - - return chainid === 5 - ? [ - process.env.TRANSACTION_BATCHER_CONTRACT_ADDRESS_GOERLI, - process.env.VEAOUTBOX_ARBGOERLI_TO_GOERLI_ADDRESS, - process.env.RPC_GOERLI, - ] - : [ - process.env.TRANSACTION_BATCHER_CONTRACT_ADDRESS_CHIADO, - process.env.VEAOUTBOX_ARBGOERLI_TO_CHIADO_ADDRESS, - process.env.RPC_CHIADO, - ]; -}; - -const getCount = async (veaOutbox: VeaOutboxArbToEth, chainid: number): Promise => { - const subgraph = getSubgraph(chainid); - const stateRoot = await veaOutbox.stateRoot(); - - const result = await request( - `https://api.thegraph.com/subgraphs/name/shotaronowhere/${subgraph}`, - `{ - snapshotSaveds(first: 1, where: { stateRoot: "${stateRoot}" }) { - count - } - }` - ); - - if (result["snapshotSaveds"].length == 0) throw new Error("No snapshot found"); - - return Number(result["snapshotSaveds"][0].count); -}; - -const relay = async (chainid: number, nonce: number) => { - const [TRANSACTION_BATCHER_CONTRACT_ADDRESS, VEAOUTBOX_ADDRESS, RPC_VEAOUTBOX] = getParams(chainid); - - const veaOutbox = getVeaOutboxArbToEth(VEAOUTBOX_ADDRESS, process.env.PRIVATE_KEY, RPC_VEAOUTBOX); - const count = await getCount(veaOutbox, chainid); - - const proof = await getProofAtCount(chainid, nonce, count); - const [to, data] = await getMessageDataToRelay(chainid, nonce); - - const txn = await veaOutbox.sendMessage(proof, nonce, to, data); - await txn.wait(); -}; - -const relayBatch = async (chainid: number, nonce: number, iterations: number) => { - const [TRANSACTION_BATCHER_CONTRACT_ADDRESS, VEAOUTBOX_ADDRESS, RPC_VEAOUTBOX] = getParams(chainid); - - const web3 = new Web3(RPC_VEAOUTBOX); - const batchedSend = _batchedSend(web3, TRANSACTION_BATCHER_CONTRACT_ADDRESS, process.env.PRIVATE_KEY, 0); - - const contract = new web3.eth.Contract(_contract.abi, VEAOUTBOX_ADDRESS); - const veaOutbox = getVeaOutboxArbToEth(VEAOUTBOX_ADDRESS, process.env.PRIVATE_KEY, RPC_VEAOUTBOX); - 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, - }); - } - - console.log(txns); - await batchedSend(txns); -}; - -const relayAllFrom = async (chainid: number, nonce: number, msgSender: string): Promise => { - const [TRANSACTION_BATCHER_CONTRACT_ADDRESS, VEAOUTBOX_ADDRESS, RPC_VEAOUTBOX] = getParams(chainid); - - const web3 = new Web3(RPC_VEAOUTBOX); - const batchedSend = _batchedSend( - web3, // Your web3 object. - // The address of the transaction batcher contract you wish to use. The addresses for the different networks are listed below. If the one you need is missing, feel free to deploy it yourself and make a PR to save the address here for others to use. - TRANSACTION_BATCHER_CONTRACT_ADDRESS, - process.env.PRIVATE_KEY, // The private key of the account you want to send transactions from. - 0 // The debounce timeout period in milliseconds in which transactions are batched. - ); - - const contract = new web3.eth.Contract(_contract.abi, VEAOUTBOX_ADDRESS); - const veaOutbox = getVeaOutboxArbToEth(VEAOUTBOX_ADDRESS, process.env.PRIVATE_KEY, RPC_VEAOUTBOX); - const count = await getCount(veaOutbox, chainid); - - let txns = []; - - const nonces = await getNonceFrom(chainid, nonce, msgSender); - - for (const x of nonces) { - const proof = await getProofAtCount(chainid, x, count); - const [to, data] = await getMessageDataToRelay(chainid, x); - txns.push({ - args: [proof, x, to, data], - method: contract.methods.sendMessage, - to: contract.options.address, - }); - } - - await batchedSend(txns); - - return nonces[nonces.length - 1]; -}; - -const getNonceFrom = async (chainid: number, nonce: number, msgSender: string) => { - try { - const sugbraph = getSubgraph(chainid); - - const result = await request( - `https://api.thegraph.com/subgraphs/name/shotaronowhere/${sugbraph}`, - `{ - messageSents(first: 1000, where: {nonce_gte: ${nonce}, msgSender_: {id: "${msgSender}"}}, orderBy: nonce, orderDirection: asc) { - nonce - } - }` - ); - - return result[`messageSents`].map((a) => a.nonce); - } catch (e) { - console.log(e); - } -}; - -export { relayAllFrom, relay, relayBatch }; diff --git a/relayer-cli/types/index.ts b/relayer-cli/types/index.ts new file mode 100644 index 00000000..3a1c66c3 --- /dev/null +++ b/relayer-cli/types/index.ts @@ -0,0 +1,5 @@ +export type ArrayElement = ArrayType extends readonly (infer ElementType)[] + ? ElementType + : never; + +export type Supported = ArrayElement;