From cf964b03b75e27d2cad16d968d3938363a5da3c1 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:54:19 +0100 Subject: [PATCH 1/5] improve(relayer): Read SpokePool addresses from contracts repo This skips an RPC call per SpokePool listener in the fast relayer configuration, and permits each SpokePool listener instance to communicate with only a single chain, instead of having to always query mainnet to resolve the current SpokePool address. --- src/libexec/RelayerSpokePoolIndexer.ts | 3 ++- src/libexec/SpokePoolListenerExperimental.ts | 3 ++- src/utils/ContractUtils.ts | 27 ++++++++++++-------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/libexec/RelayerSpokePoolIndexer.ts b/src/libexec/RelayerSpokePoolIndexer.ts index e6b211734..6bfdbf406 100644 --- a/src/libexec/RelayerSpokePoolIndexer.ts +++ b/src/libexec/RelayerSpokePoolIndexer.ts @@ -10,6 +10,7 @@ import { isDefined, getBlockForTimestamp, getChainQuorum, + getDeployedContract, getDeploymentBlockNumber, getNetworkName, getOriginFromURL, @@ -161,7 +162,7 @@ async function run(argv: string[]): Promise { }; logger.debug({ at: "RelayerSpokePoolIndexer::run", message: `Starting ${chain} SpokePool Indexer.`, opts }); - const spokePool = await utils.getSpokePoolContract(chainId); + const spokePool = getDeployedContract("SpokePool", chainId); process.on("SIGHUP", () => { logger.debug({ at: "Relayer#run", message: `Received SIGHUP in ${chain} listener, stopping...` }); diff --git a/src/libexec/SpokePoolListenerExperimental.ts b/src/libexec/SpokePoolListenerExperimental.ts index 5236575c7..94697d726 100644 --- a/src/libexec/SpokePoolListenerExperimental.ts +++ b/src/libexec/SpokePoolListenerExperimental.ts @@ -14,6 +14,7 @@ import { getBlockForTimestamp, getChainQuorum, getDeploymentBlockNumber, + getDeployedContract, getNetworkName, getNodeUrlList, getOriginFromURL, @@ -206,7 +207,7 @@ async function run(argv: string[]): Promise { }; logger.debug({ at: "RelayerSpokePoolListener::run", message: `Starting ${chain} SpokePool Indexer.`, opts }); - const spokePool = await utils.getSpokePoolContract(chainId); + const spokePool = getDeployedContract("SpokePool", chainId); process.on("SIGHUP", () => { logger.debug({ at: "Relayer#run", message: `Received SIGHUP in ${chain} listener, stopping...` }); diff --git a/src/utils/ContractUtils.ts b/src/utils/ContractUtils.ts index 31bf09e60..972310a7c 100644 --- a/src/utils/ContractUtils.ts +++ b/src/utils/ContractUtils.ts @@ -1,4 +1,4 @@ -import { getNetworkName, Contract, Signer, getDeployedAddress, getDeployedBlockNumber } from "."; +import { CHAIN_IDs, getNetworkName, Contract, Signer, getDeployedAddress, getDeployedBlockNumber } from "."; import * as typechain from "@across-protocol/contracts"; // TODO: refactor once we've fixed export from contract repo @@ -7,26 +7,31 @@ export function getDeployedContract(contractName: string, networkId: number, sig try { const address = getDeployedAddress(contractName, networkId); // If the contractName is SpokePool then we need to modify it to find the correct contract factory artifact. - const factoryName = contractName === "SpokePool" ? castSpokePoolName(networkId) : contractName; - const artifact = typechain[`${[factoryName.replace("_", "")]}__factory`]; + const factoryName = `${contractName === "SpokePool" ? castSpokePoolName(networkId) : contractName}__factory`; + const artifact = typechain[factoryName]; return new Contract(address, artifact.abi, signer); } catch (error) { - throw new Error(`Could not find address for contract ${contractName} on ${networkId}`); + throw new Error(`Could not find address for contract ${contractName} on ${networkId} (${error})`); } } // If the name of the contract is SpokePool then we need to apply a transformation on the name to get the correct // contract factory name. For example, if the network is "mainnet" then the contract is called Ethereum_SpokePool. export function castSpokePoolName(networkId: number): string { - let networkName = getNetworkName(networkId); - if (networkName == "Mainnet" || networkName == "Rinkeby" || networkName == "Kovan" || networkName == "Goerli") { - return "Ethereum_SpokePool"; + let networkName: string; + switch (networkId) { + case CHAIN_IDs.MAINNET: + case CHAIN_IDs.SEPOLIA: + return "Ethereum_SpokePool"; + case CHAIN_IDs.ARBITRUM: + return "Arbitrum_SpokePool"; + case CHAIN_IDs.ZK_SYNC: + return "ZkSync_SpokePool"; + default: + networkName = getNetworkName(networkId); } - if (networkName.includes("-")) { - networkName = networkName.substring(0, networkName.indexOf("-")); - } - return `${networkName}_SpokePool`; + return `${networkName.replace(" ", "")}_SpokePool`; } export function getParamType(contractName: string, functionName: string, paramName: string): string { From 7e91ac099ac6276ca672c091bd06ca3bc6c8222b Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:15:34 +0100 Subject: [PATCH 2/5] Update --- src/libexec/RelayerSpokePoolIndexer.ts | 21 +++++++++++++++----- src/libexec/SpokePoolListenerExperimental.ts | 21 +++++++++++++++----- src/utils/ContractUtils.ts | 10 ++++++++-- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/libexec/RelayerSpokePoolIndexer.ts b/src/libexec/RelayerSpokePoolIndexer.ts index 6bfdbf406..3fdfc980a 100644 --- a/src/libexec/RelayerSpokePoolIndexer.ts +++ b/src/libexec/RelayerSpokePoolIndexer.ts @@ -10,12 +10,12 @@ import { isDefined, getBlockForTimestamp, getChainQuorum, - getDeployedContract, getDeploymentBlockNumber, getNetworkName, getOriginFromURL, getProvider, getRedisCache, + getSpokePool, getWSProviders, Logger, winston, @@ -118,11 +118,11 @@ async function listen( */ async function run(argv: string[]): Promise { const minimistOpts = { - string: ["lookback", "relayer"], + string: ["lookback", "blockrange", "quorum", "relayer", "spokepool"], }; const args = minimist(argv, minimistOpts); - const { chainId, lookback, relayer = null, maxBlockRange = 10_000 } = args; + const { chainId, lookback, relayer = null, blockrange: maxBlockRange = 10_000 } = args; assert(Number.isInteger(chainId), "chainId must be numeric "); assert(Number.isInteger(maxBlockRange), "maxBlockRange must be numeric"); assert(!isDefined(relayer) || ethersUtils.isAddress(relayer), `relayer address is invalid (${relayer})`); @@ -130,6 +130,12 @@ async function run(argv: string[]): Promise { const { quorum = getChainQuorum(chainId) } = args; assert(Number.isInteger(quorum), "quorum must be numeric "); + let { spokepool: spokePoolAddr } = args; + assert( + !isDefined(spokePoolAddr) || ethersUtils.isAddress(spokePoolAddr), + `Invalid SpokePool address (${spokePoolAddr})` + ); + chain = getNetworkName(chainId); const quorumProvider = await getProvider(chainId); @@ -153,16 +159,21 @@ async function run(argv: string[]): Promise { logger.debug({ at: "RelayerSpokePoolIndexer::run", message: `Skipping lookback on ${chain}.` }); } + const spokePool = getSpokePool(chainId, spokePoolAddr); + if (!isDefined(spokePoolAddr)) { + ({ address: spokePoolAddr } = spokePool); + } + const opts = { - quorum, + spokePool: spokePoolAddr, deploymentBlock, lookback: latestBlock.number - startBlock, maxBlockRange, filterArgs: getEventFilterArgs(relayer), + quorum, }; logger.debug({ at: "RelayerSpokePoolIndexer::run", message: `Starting ${chain} SpokePool Indexer.`, opts }); - const spokePool = getDeployedContract("SpokePool", chainId); process.on("SIGHUP", () => { logger.debug({ at: "Relayer#run", message: `Received SIGHUP in ${chain} listener, stopping...` }); diff --git a/src/libexec/SpokePoolListenerExperimental.ts b/src/libexec/SpokePoolListenerExperimental.ts index 94697d726..aa4b1d97d 100644 --- a/src/libexec/SpokePoolListenerExperimental.ts +++ b/src/libexec/SpokePoolListenerExperimental.ts @@ -14,11 +14,11 @@ import { getBlockForTimestamp, getChainQuorum, getDeploymentBlockNumber, - getDeployedContract, getNetworkName, getNodeUrlList, getOriginFromURL, getProvider, + getSpokePool, getRedisCache, Logger, winston, @@ -162,12 +162,12 @@ async function listen(eventMgr: EventManager, spokePool: Contract, eventNames: s */ async function run(argv: string[]): Promise { const minimistOpts = { - string: ["lookback", "relayer"], + string: ["lookback", "blockrange", "quorum", "relayer", "spokepool"], }; const args = minimist(argv, minimistOpts); ({ chainId } = args); - const { lookback, relayer = null, maxBlockRange = 10_000 } = args; + const { lookback, relayer = null, blockrange: maxBlockRange = 10_000 } = args; assert(Number.isInteger(chainId), "chainId must be numeric "); assert(Number.isInteger(maxBlockRange), "maxBlockRange must be numeric"); assert(!isDefined(relayer) || ethersUtils.isAddress(relayer), `relayer address is invalid (${relayer})`); @@ -175,6 +175,12 @@ async function run(argv: string[]): Promise { const { quorum = getChainQuorum(chainId) } = args; assert(Number.isInteger(quorum), "quorum must be numeric "); + let { spokepool: spokePoolAddr } = args; + assert( + !isDefined(spokePoolAddr) || ethersUtils.isAddress(spokePoolAddr), + `Invalid SpokePool address (${spokePoolAddr})` + ); + chain = getNetworkName(chainId); const quorumProvider = await getProvider(chainId); @@ -198,16 +204,21 @@ async function run(argv: string[]): Promise { logger.debug({ at: "RelayerSpokePoolListener::run", message: `Skipping lookback on ${chain}.` }); } + const spokePool = getSpokePool(chainId, spokePoolAddr); + if (!isDefined(spokePoolAddr)) { + ({ address: spokePoolAddr } = spokePool); + } + const opts = { - quorum, + spokePool: spokePoolAddr, deploymentBlock, lookback: latestBlock.number - startBlock, maxBlockRange, filterArgs: getEventFilterArgs(relayer), + quorum, }; logger.debug({ at: "RelayerSpokePoolListener::run", message: `Starting ${chain} SpokePool Indexer.`, opts }); - const spokePool = getDeployedContract("SpokePool", chainId); process.on("SIGHUP", () => { logger.debug({ at: "Relayer#run", message: `Received SIGHUP in ${chain} listener, stopping...` }); diff --git a/src/utils/ContractUtils.ts b/src/utils/ContractUtils.ts index 972310a7c..faafdab89 100644 --- a/src/utils/ContractUtils.ts +++ b/src/utils/ContractUtils.ts @@ -1,6 +1,5 @@ -import { CHAIN_IDs, getNetworkName, Contract, Signer, getDeployedAddress, getDeployedBlockNumber } from "."; - import * as typechain from "@across-protocol/contracts"; // TODO: refactor once we've fixed export from contract repo +import { CHAIN_IDs, getNetworkName, Contract, Signer, getDeployedAddress, getDeployedBlockNumber } from "."; // Return an ethers contract instance for a deployed contract, imported from the Across-protocol contracts repo. export function getDeployedContract(contractName: string, networkId: number, signer?: Signer): Contract { @@ -34,6 +33,13 @@ export function castSpokePoolName(networkId: number): string { return `${networkName.replace(" ", "")}_SpokePool`; } +// For a chain ID and optional SpokePool address, return a Contract instance with the corresponding ABI. +export function getSpokePool(chainId: number, address?: string): Contract { + const factoryName = castSpokePoolName(chainId); + const artifact = typechain[factoryName]; + return new Contract(address ?? getDeployedAddress("SpokePool", chainId), artifact.abi); +} + export function getParamType(contractName: string, functionName: string, paramName: string): string { const artifact = typechain[`${[contractName]}__factory`]; const fragment = artifact.abi.find((fragment: { name: string }) => fragment.name === functionName); From ea073dfa46ad6b7f085a03cf142f0bb62d59017f Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:19:12 +0100 Subject: [PATCH 3/5] Add SpokePool address --- src/clients/SpokePoolClient.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clients/SpokePoolClient.ts b/src/clients/SpokePoolClient.ts index dc2518cb6..8241eb247 100644 --- a/src/clients/SpokePoolClient.ts +++ b/src/clients/SpokePoolClient.ts @@ -78,9 +78,10 @@ export class IndexedSpokePoolClient extends clients.SpokePoolClient { */ protected startWorker(): void { const { - eventSearchConfig: { fromBlock, maxBlockLookBack: blockRange }, + eventSearchConfig: { fromBlock, maxBlockLookBack: blockrange }, + spokePool: { address: spokepool }, } = this; - const opts = { blockRange, lookback: `@${fromBlock}` }; + const opts = { spokepool, blockrange, lookback: `@${fromBlock}` }; const args = Object.entries(opts) .map(([k, v]) => [`--${k}`, `${v}`]) From 86709a534573088b97ad7954fb210e2e1ef93e79 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:42:44 +0100 Subject: [PATCH 4/5] fix --- src/libexec/RelayerSpokePoolIndexer.ts | 2 +- src/libexec/SpokePoolListenerExperimental.ts | 2 +- src/utils/ContractUtils.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexec/RelayerSpokePoolIndexer.ts b/src/libexec/RelayerSpokePoolIndexer.ts index 3fdfc980a..20c75664c 100644 --- a/src/libexec/RelayerSpokePoolIndexer.ts +++ b/src/libexec/RelayerSpokePoolIndexer.ts @@ -118,7 +118,7 @@ async function listen( */ async function run(argv: string[]): Promise { const minimistOpts = { - string: ["lookback", "blockrange", "quorum", "relayer", "spokepool"], + string: ["lookback", "relayer", "spokepool"], }; const args = minimist(argv, minimistOpts); diff --git a/src/libexec/SpokePoolListenerExperimental.ts b/src/libexec/SpokePoolListenerExperimental.ts index aa4b1d97d..1e3e892f5 100644 --- a/src/libexec/SpokePoolListenerExperimental.ts +++ b/src/libexec/SpokePoolListenerExperimental.ts @@ -162,7 +162,7 @@ async function listen(eventMgr: EventManager, spokePool: Contract, eventNames: s */ async function run(argv: string[]): Promise { const minimistOpts = { - string: ["lookback", "blockrange", "quorum", "relayer", "spokepool"], + string: ["lookback", "relayer", "spokepool"], }; const args = minimist(argv, minimistOpts); diff --git a/src/utils/ContractUtils.ts b/src/utils/ContractUtils.ts index faafdab89..d4e4206b0 100644 --- a/src/utils/ContractUtils.ts +++ b/src/utils/ContractUtils.ts @@ -36,7 +36,7 @@ export function castSpokePoolName(networkId: number): string { // For a chain ID and optional SpokePool address, return a Contract instance with the corresponding ABI. export function getSpokePool(chainId: number, address?: string): Contract { const factoryName = castSpokePoolName(chainId); - const artifact = typechain[factoryName]; + const artifact = typechain[`${factoryName}__factory`]; return new Contract(address ?? getDeployedAddress("SpokePool", chainId), artifact.abi); } From 9e2c5dfd9132ff4b86cc1228317d35e79b5e1309 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Sat, 30 Nov 2024 07:54:41 +0000 Subject: [PATCH 5/5] Align arguments --- src/clients/SpokePoolClient.ts | 2 +- src/common/Constants.ts | 2 +- src/libexec/RelayerSpokePoolIndexer.ts | 2 +- src/libexec/SpokePoolListenerExperimental.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clients/SpokePoolClient.ts b/src/clients/SpokePoolClient.ts index 8241eb247..cb6b51e52 100644 --- a/src/clients/SpokePoolClient.ts +++ b/src/clients/SpokePoolClient.ts @@ -86,7 +86,7 @@ export class IndexedSpokePoolClient extends clients.SpokePoolClient { const args = Object.entries(opts) .map(([k, v]) => [`--${k}`, `${v}`]) .flat(); - this.worker = spawn("node", [this.indexerPath, "--chainId", this.chainId.toString(), ...args], { + this.worker = spawn("node", [this.indexerPath, "--chainid", this.chainId.toString(), ...args], { stdio: ["ignore", "inherit", "inherit", "ipc"], }); diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 73e67b531..59fe6b34e 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -127,7 +127,7 @@ export const CHAIN_MAX_BLOCK_LOOKBACK = { [CHAIN_IDs.BOBA]: 4990, [CHAIN_IDs.LINEA]: 5000, [CHAIN_IDs.LISK]: 10000, - [CHAIN_IDs.MAINNET]: 10000, + [CHAIN_IDs.MAINNET]: 5000, [CHAIN_IDs.MODE]: 10000, [CHAIN_IDs.OPTIMISM]: 10000, // Quick [CHAIN_IDs.POLYGON]: 10000, diff --git a/src/libexec/RelayerSpokePoolIndexer.ts b/src/libexec/RelayerSpokePoolIndexer.ts index 20c75664c..cca604523 100644 --- a/src/libexec/RelayerSpokePoolIndexer.ts +++ b/src/libexec/RelayerSpokePoolIndexer.ts @@ -122,7 +122,7 @@ async function run(argv: string[]): Promise { }; const args = minimist(argv, minimistOpts); - const { chainId, lookback, relayer = null, blockrange: maxBlockRange = 10_000 } = args; + const { chainId: chainId, lookback, relayer = null, blockrange: maxBlockRange = 10_000 } = args; assert(Number.isInteger(chainId), "chainId must be numeric "); assert(Number.isInteger(maxBlockRange), "maxBlockRange must be numeric"); assert(!isDefined(relayer) || ethersUtils.isAddress(relayer), `relayer address is invalid (${relayer})`); diff --git a/src/libexec/SpokePoolListenerExperimental.ts b/src/libexec/SpokePoolListenerExperimental.ts index 1e3e892f5..0a14c9d4f 100644 --- a/src/libexec/SpokePoolListenerExperimental.ts +++ b/src/libexec/SpokePoolListenerExperimental.ts @@ -166,7 +166,7 @@ async function run(argv: string[]): Promise { }; const args = minimist(argv, minimistOpts); - ({ chainId } = args); + ({ chainid: chainId } = args); const { lookback, relayer = null, blockrange: maxBlockRange = 10_000 } = args; assert(Number.isInteger(chainId), "chainId must be numeric "); assert(Number.isInteger(maxBlockRange), "maxBlockRange must be numeric");