From 18683d576ebb11831a146b25c6632af5656d31a6 Mon Sep 17 00:00:00 2001 From: 0xyaco Date: Wed, 9 Oct 2024 10:22:28 +0200 Subject: [PATCH] fix: query mainnet block numbers (#58) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 🤖 Linear Closes GRT-190 ## Description Uses L1 block numbers for epoch blocks. --- apps/agent/config.example.yml | 11 +- apps/agent/src/config/index.ts | 10 +- apps/agent/src/config/schemas.ts | 21 +- .../src/exceptions/errorHandler.ts | 35 ++- .../src/providers/protocolProvider.ts | 235 ++++++++++-------- .../src/services/eboActor.ts | 10 +- .../tests/mocks/eboActor.mocks.ts | 16 +- .../tests/mocks/eboProcessor.mocks.ts | 16 +- .../tests/services/protocolProvider.spec.ts | 74 ++++-- 9 files changed, 259 insertions(+), 169 deletions(-) diff --git a/apps/agent/config.example.yml b/apps/agent/config.example.yml index 3a09782..cfa70f5 100644 --- a/apps/agent/config.example.yml +++ b/apps/agent/config.example.yml @@ -1,8 +1,13 @@ protocolProvider: rpcsConfig: - transactionReceiptConfirmations: 1 - timeout: 10000 - retryInterval: 150 + l1: + transactionReceiptConfirmations: 1 + timeout: 10000 + retryInterval: 150 + l2: + transactionReceiptConfirmations: 1 + timeout: 10000 + retryInterval: 150 contracts: oracle: "0x1234567890123456789012345678901234567890" epochManager: "0x1234567890123456789012345678901234567890" diff --git a/apps/agent/src/config/index.ts b/apps/agent/src/config/index.ts index 1fb29c1..c39590d 100644 --- a/apps/agent/src/config/index.ts +++ b/apps/agent/src/config/index.ts @@ -48,8 +48,14 @@ export const config = { protocolProvider: { ...configData.protocolProvider, rpcsConfig: { - ...configData.protocolProvider.rpcsConfig, - urls: envData.PROTOCOL_PROVIDER_RPC_URLS, + l1: { + ...configData.protocolProvider.rpcsConfig.l1, + urls: envData.PROTOCOL_PROVIDER_L1_RPC_URLS, + }, + l2: { + ...configData.protocolProvider.rpcsConfig.l2, + urls: envData.PROTOCOL_PROVIDER_L2_RPC_URLS, + }, }, privateKey: envData.PROTOCOL_PROVIDER_PRIVATE_KEY, }, diff --git a/apps/agent/src/config/schemas.ts b/apps/agent/src/config/schemas.ts index 6e5a896..a22d6a3 100644 --- a/apps/agent/src/config/schemas.ts +++ b/apps/agent/src/config/schemas.ts @@ -18,24 +18,31 @@ const chainRpcUrlSchema = z .record(chainIdSchema, z.array(z.string().url())) .transform((records) => new Map(Object.entries(records) as [Caip2ChainId, string[]][])); +const rpcUrlsSchema = z + .string() + .transform((str) => str.split(",")) + .refine((arr) => arr.every((url) => z.string().url().safeParse(url).success)); + export const envSchema = z.object({ PROTOCOL_PROVIDER_PRIVATE_KEY: z.string().refine((key) => isHex(key)), - PROTOCOL_PROVIDER_RPC_URLS: z - .string() - .transform((str) => str.split(",")) - .refine((arr) => arr.every((url) => z.string().url().safeParse(url).success)), + PROTOCOL_PROVIDER_L1_RPC_URLS: rpcUrlsSchema, + PROTOCOL_PROVIDER_L2_RPC_URLS: rpcUrlsSchema, BLOCK_NUMBER_RPC_URLS_MAP: stringToJSONSchema.pipe(chainRpcUrlSchema), BLOCK_NUMBER_BLOCKMETA_TOKEN: z.string(), EBO_AGENT_CONFIG_FILE_PATH: z.string(), }); const addressSchema = z.string().refine((address) => isAddress(address)); +const rpcConfigSchema = z.object({ + transactionReceiptConfirmations: z.number().int().positive(), + timeout: z.number().int().positive(), + retryInterval: z.number().int().positive(), +}); const protocolProviderConfigSchema = z.object({ rpcsConfig: z.object({ - transactionReceiptConfirmations: z.number().int().positive(), - timeout: z.number().int().positive(), - retryInterval: z.number().int().positive(), + l1: rpcConfigSchema, + l2: rpcConfigSchema, }), contracts: z.object({ oracle: addressSchema, diff --git a/packages/automated-dispute/src/exceptions/errorHandler.ts b/packages/automated-dispute/src/exceptions/errorHandler.ts index 68f4b9c..76e49ad 100644 --- a/packages/automated-dispute/src/exceptions/errorHandler.ts +++ b/packages/automated-dispute/src/exceptions/errorHandler.ts @@ -1,34 +1,31 @@ -import { Logger } from "@ebo-agent/shared"; +import { ILogger } from "@ebo-agent/shared"; import { CustomContractError } from "../exceptions/index.js"; import { ErrorContext } from "../types/index.js"; export class ErrorHandler { - private static logger = Logger.getInstance(); - - public static async handle(error: CustomContractError): Promise { + public static async handle(error: CustomContractError, logger: ILogger): Promise { const strategy = error.strategy; const context = error.getContext(); - this.logger.error(`Error occurred: ${error.message}`); - - if (strategy.shouldNotify) { - await this.notifyError(error, context); - } + logger.error(`Error occurred: ${error.message}`); try { await error.executeCustomAction(); } catch (actionError) { - this.logger.error(`Error executing custom action: ${actionError}`); - // Continue without rethrowing - } - - if (strategy.shouldReenqueue && context.reenqueueEvent) { - context.reenqueueEvent(); - } - - if (strategy.shouldTerminate && context.terminateActor) { - context.terminateActor(); + logger.error(`Error executing custom action: ${actionError}`); + } finally { + if (strategy.shouldNotify) { + await this.notifyError(error, context); + } + + if (strategy.shouldReenqueue && context.reenqueueEvent) { + context.reenqueueEvent(); + } + + if (strategy.shouldTerminate && context.terminateActor) { + context.terminateActor(); + } } } diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index b3659d0..5cb0eed 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -2,6 +2,7 @@ import { Caip2ChainId } from "@ebo-agent/blocknumber/src/index.js"; import { Address, BaseError, + Chain, ContractFunctionRevertedError, createPublicClient, createWalletClient, @@ -18,7 +19,7 @@ import { WalletClient, } from "viem"; import { privateKeyToAccount } from "viem/accounts"; -import { arbitrum } from "viem/chains"; +import { arbitrum, mainnet } from "viem/chains"; import type { Dispute, @@ -51,12 +52,18 @@ import { ProtocolContractsAddresses, } from "../interfaces/index.js"; -type ProtocolRpcConfig = { +type RpcConfig = { urls: string[]; transactionReceiptConfirmations: number; timeout: number; retryInterval: number; }; + +type ProtocolRpcConfig = { + l1: RpcConfig; + l2: RpcConfig; +}; + export const REQUEST_RESPONSE_MODULE_DATA_ABI_FIELDS = [ { name: "accountingExtension", type: "address" }, { name: "bondToken", type: "address" }, @@ -78,32 +85,37 @@ export const REQUEST_DISPUTE_MODULE_DATA_ABI_FIELDS = [ export const RESPONSE_ABI_FIELDS = [{ name: "block", type: "uint256" }] as const; export class ProtocolProvider implements IProtocolProvider { - private readClient: PublicClient>; - private writeClient: WalletClient>; + private l1ReadClient: PublicClient>; + private l2ReadClient: PublicClient>; + private l2WriteClient: WalletClient>; + private oracleContract: GetContractReturnType< typeof oracleAbi, - typeof this.writeClient, + typeof this.l2WriteClient, Address >; + private epochManagerContract: GetContractReturnType< typeof epochManagerAbi, - typeof this.readClient, + typeof this.l2ReadClient, Address >; + private eboRequestCreatorContract: GetContractReturnType< typeof eboRequestCreatorAbi, - typeof this.writeClient, + typeof this.l2WriteClient, Address >; + private bondEscalationContract: GetContractReturnType< typeof bondEscalationModuleAbi, - typeof this.writeClient, + typeof this.l2WriteClient, Address >; private horizonAccountingExtensionContract: GetContractReturnType< typeof horizonAccountingExtensionAbi, - typeof this.writeClient, + typeof this.l2WriteClient, Address >; @@ -118,72 +130,43 @@ export class ProtocolProvider implements IProtocolProvider { contracts: ProtocolContractsAddresses, privateKey: Hex, ) { - const { urls, timeout, retryInterval } = rpcConfig; - - if (urls.length === 0) { - throw new RpcUrlsEmpty(); - } - - this.readClient = createPublicClient({ - chain: arbitrum, - transport: fallback( - urls.map((url) => - http(url, { - timeout: timeout, - retryDelay: retryInterval, - }), - ), - ), - }); - - const account = privateKeyToAccount(privateKey); - - this.writeClient = createWalletClient({ - chain: arbitrum, - transport: fallback( - urls.map((url) => - http(url, { - timeout: timeout, - retryDelay: retryInterval, - }), - ), - ), - account: account, - }); + this.l1ReadClient = this.createReadClient(rpcConfig.l1, mainnet); + this.l2ReadClient = this.createReadClient(rpcConfig.l2, arbitrum); + this.l2WriteClient = this.createWriteClient(rpcConfig.l2, arbitrum, privateKey); // Instantiate all the protocol contracts this.oracleContract = getContract({ address: contracts.oracle, abi: oracleAbi, - client: this.writeClient, + client: this.l2WriteClient, }); this.epochManagerContract = getContract({ address: contracts.epochManager, abi: epochManagerAbi, - client: this.readClient, + client: this.l2ReadClient, }); this.eboRequestCreatorContract = getContract({ address: contracts.eboRequestCreator, abi: eboRequestCreatorAbi, client: { - public: this.readClient, - wallet: this.writeClient, + public: this.l2ReadClient, + wallet: this.l2WriteClient, }, }); this.bondEscalationContract = getContract({ address: contracts.bondEscalationModule, abi: bondEscalationModuleAbi, client: { - public: this.readClient, - wallet: this.writeClient, + public: this.l2ReadClient, + wallet: this.l2WriteClient, }, }); this.horizonAccountingExtensionContract = getContract({ address: contracts.horizonAccountingExtension, abi: horizonAccountingExtensionAbi, client: { - public: this.readClient, - wallet: this.writeClient, + public: this.l2ReadClient, + wallet: this.l2WriteClient, }, }); } @@ -211,6 +194,51 @@ export class ProtocolProvider implements IProtocolProvider { getApprovedModules: this.getApprovedModules.bind(this), }; + private createReadClient( + config: RpcConfig, + chain: Chain, + ): PublicClient> { + const { urls, timeout, retryInterval } = config; + + if (urls.length === 0) { + throw new RpcUrlsEmpty(); + } + + return createPublicClient({ + chain: chain, + transport: fallback( + urls.map((url) => + http(url, { + timeout: timeout, + retryDelay: retryInterval, + }), + ), + ), + }); + } + + private createWriteClient( + config: RpcConfig, + chain: Chain, + privateKey: Hex, + ): WalletClient> { + const { urls, timeout, retryInterval } = config; + const account = privateKeyToAccount(privateKey); + + return createWalletClient({ + chain: chain, + transport: fallback( + urls.map((url) => + http(url, { + timeout: timeout, + retryDelay: retryInterval, + }), + ), + ), + account: account, + }); + } + /** * Returns the address of the account used for transactions. * @@ -218,15 +246,17 @@ export class ProtocolProvider implements IProtocolProvider { * @throws {InvalidAccountOnClient} Throws if the write client does not have an assigned account. */ public getAccountAddress(): Address { - if (!this.writeClient.account) { + if (!this.l2WriteClient.account) { throw new InvalidAccountOnClient(); } - return this.writeClient.account.address; + return this.l2WriteClient.account.address; } /** * Gets the current epoch, including the block number and its timestamp. * + * NOTE: this method works on L1 block numbers as EpochManager contract uses blocks from L1. + * * @returns {Promise} The current epoch, its block number, and its timestamp. */ async getCurrentEpoch(): Promise { @@ -235,7 +265,7 @@ export class ProtocolProvider implements IProtocolProvider { this.epochManagerContract.read.currentEpochBlock(), ]); - const epochFirstBlock = await this.readClient.getBlock({ + const epochFirstBlock = await this.l1ReadClient.getBlock({ blockNumber: epochFirstBlockNumber, }); @@ -252,7 +282,7 @@ export class ProtocolProvider implements IProtocolProvider { * @returns {Promise} The block number of the last finalized block. */ async getLastFinalizedBlock(): Promise { - const { number } = await this.readClient.getBlock({ blockTag: "finalized" }); + const { number } = await this.l2ReadClient.getBlock({ blockTag: "finalized" }); return number; } @@ -334,19 +364,20 @@ export class ProtocolProvider implements IProtocolProvider { * @returns {Promise} A promise that resolves when the module is approved. */ async approveModule(module: Address): Promise { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.horizonAccountingExtensionContract.address, abi: horizonAccountingExtensionAbi, functionName: "approveModule", args: [module], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -460,19 +491,20 @@ export class ProtocolProvider implements IProtocolProvider { * @returns {Promise} A promise that resolves when the request is successfully created. */ async createRequest(epoch: bigint, chain: Caip2ChainId): Promise { - const { request } = await this.readClient.simulateContract({ + const { request } = await this.l2ReadClient.simulateContract({ address: this.eboRequestCreatorContract.address, abi: eboRequestCreatorAbi, functionName: "createRequest", args: [epoch, chain], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(request); + const hash = await this.l2WriteClient.writeContract(request); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -493,19 +525,20 @@ export class ProtocolProvider implements IProtocolProvider { request: Request["prophetData"], response: Response["prophetData"], ): Promise { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "proposeResponse", args: [request, response], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -528,19 +561,20 @@ export class ProtocolProvider implements IProtocolProvider { response: Response["prophetData"], dispute: Dispute["prophetData"], ): Promise { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "disputeResponse", args: [request, response, dispute], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -562,19 +596,20 @@ export class ProtocolProvider implements IProtocolProvider { dispute: Dispute["prophetData"], ): Promise { try { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.bondEscalationContract.address, abi: bondEscalationModuleAbi, functionName: "pledgeForDispute", args: [request, dispute], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -608,19 +643,20 @@ export class ProtocolProvider implements IProtocolProvider { dispute: Dispute["prophetData"], ): Promise { try { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.bondEscalationContract.address, abi: bondEscalationModuleAbi, functionName: "pledgeAgainstDispute", args: [request, dispute], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -656,19 +692,20 @@ export class ProtocolProvider implements IProtocolProvider { dispute: Dispute["prophetData"], ): Promise { try { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.bondEscalationContract.address, abi: bondEscalationModuleAbi, functionName: "settleBondEscalation", args: [request, response, dispute], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -708,19 +745,20 @@ export class ProtocolProvider implements IProtocolProvider { response: Response["prophetData"], dispute: Dispute["prophetData"], ): Promise { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "escalateDispute", args: [request, response, dispute], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { @@ -747,19 +785,20 @@ export class ProtocolProvider implements IProtocolProvider { request: Request["prophetData"], response: Response["prophetData"], ): Promise { - const { request: simulatedRequest } = await this.readClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "finalize", args: [request, response], - account: this.writeClient.account, + account: this.l2WriteClient.account, }); - const hash = await this.writeClient.writeContract(simulatedRequest); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const receipt = await this.readClient.waitForTransactionReceipt({ + const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, - confirmations: this.rpcConfig.transactionReceiptConfirmations, + confirmations: transactionReceiptConfirmations, }); if (receipt.status !== "success") { diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index 80c065d..f973326 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -169,7 +169,7 @@ export class EboActor { }, ); - await ErrorHandler.handle(err); + await ErrorHandler.handle(err, this.logger); if (err.strategy.shouldNotify) { // TODO: add notification logic @@ -403,7 +403,7 @@ export class EboActor { ); this.logger.info(`Dispute ${dispute.id} escalated.`); - await ErrorHandler.handle(customError); + await ErrorHandler.handle(customError, this.logger); } catch (escalationError) { this.logger.error( `Failed to escalate dispute ${dispute.id}: ${escalationError}`, @@ -619,7 +619,7 @@ export class EboActor { }; customError.setContext(context); - await ErrorHandler.handle(customError); + await ErrorHandler.handle(customError, this.logger); this.logger.warn( `Block ${responseBody.block} for epoch ${request.epoch} and ` + @@ -807,7 +807,7 @@ export class EboActor { }; customError.setContext(context); - await ErrorHandler.handle(customError); + await ErrorHandler.handle(customError, this.logger); } else { throw err; } @@ -845,7 +845,7 @@ export class EboActor { }; customError.setContext(context); - await ErrorHandler.handle(customError); + await ErrorHandler.handle(customError, this.logger); } else { throw err; } diff --git a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts index bc107ce..ecc4d19 100644 --- a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts @@ -23,10 +23,18 @@ export function buildEboActor(request: Request, logger: ILogger) { const protocolProvider = new ProtocolProvider( { - urls: ["http://localhost:8545"], - retryInterval: 1, - timeout: 100, - transactionReceiptConfirmations: 1, + l1: { + urls: ["http://localhost:8545"], + retryInterval: 1, + timeout: 100, + transactionReceiptConfirmations: 1, + }, + l2: { + urls: ["http://localhost:8546"], + retryInterval: 1, + timeout: 100, + transactionReceiptConfirmations: 1, + }, }, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, diff --git a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts index ec89a9e..f46d86f 100644 --- a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts @@ -20,10 +20,18 @@ export function buildEboProcessor( ) { const protocolProvider = new ProtocolProvider( { - urls: ["http://localhost:8538"], - retryInterval: 1, - timeout: 100, - transactionReceiptConfirmations: 1, + l1: { + urls: ["http://localhost:8538"], + retryInterval: 1, + timeout: 100, + transactionReceiptConfirmations: 1, + }, + l2: { + urls: ["http://localhost:8539"], + retryInterval: 1, + timeout: 100, + transactionReceiptConfirmations: 1, + }, }, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index 5cf574b..25d95a6 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -49,10 +49,18 @@ vi.mock("viem", async () => { describe("ProtocolProvider", () => { const mockRpcConfig = { - urls: ["http://localhost:8545"], - retryInterval: 1, - timeout: 100, - transactionReceiptConfirmations: 1, + l1: { + urls: ["http://localhost:8545"], + retryInterval: 1, + timeout: 100, + transactionReceiptConfirmations: 1, + }, + l2: { + urls: ["http://localhost:8546"], + retryInterval: 1, + timeout: 100, + transactionReceiptConfirmations: 1, + }, }; const mockContractAddress: ProtocolContractsAddresses = { @@ -157,7 +165,7 @@ describe("ProtocolProvider", () => { expect(createPublicClient).toHaveBeenCalledWith({ chain: arbitrum, transport: fallback( - mockRpcConfig.urls.map((url) => + mockRpcConfig.l2.urls.map((url) => http(url, { timeout: protocolProvider["TIMEOUT"], retryDelay: protocolProvider["RETRY_INTERVAL"], @@ -169,7 +177,7 @@ describe("ProtocolProvider", () => { expect(createWalletClient).toHaveBeenCalledWith({ chain: arbitrum, transport: fallback( - mockRpcConfig.urls.map((url) => + mockRpcConfig.l2.urls.map((url) => http(url, { timeout: protocolProvider["TIMEOUT"], retryDelay: protocolProvider["RETRY_INTERVAL"], @@ -190,20 +198,32 @@ describe("ProtocolProvider", () => { expect(getContract).toHaveBeenCalledWith({ address: mockContractAddress.oracle, abi: oracleAbi, - client: protocolProvider["writeClient"], + client: protocolProvider["l2WriteClient"], }); expect(getContract).toHaveBeenCalledWith({ address: mockContractAddress.epochManager, abi: epochManagerAbi, - client: protocolProvider["readClient"], + client: protocolProvider["l2ReadClient"], }); }); - it("throws if rpcUrls are empty", () => { + + it("throws if L1 rpcUrls are empty", () => { + expect( + () => + new ProtocolProvider( + { ...mockRpcConfig, l1: { ...mockRpcConfig.l1, urls: [] } }, + mockContractAddress, + mockedPrivateKey, + ), + ).toThrowError(RpcUrlsEmpty); + }); + + it("throws if L2 rpcUrls are empty", () => { expect( () => new ProtocolProvider( - { ...mockRpcConfig, urls: [] }, + { ...mockRpcConfig, l2: { ...mockRpcConfig.l2, urls: [] } }, mockContractAddress, mockedPrivateKey, ), @@ -340,7 +360,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -359,7 +379,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["writeClient"].writeContract as Mock).mockRejectedValue( + (protocolProvider["l2WriteClient"].writeContract as Mock).mockRejectedValue( new Error("Transaction couldn't be confirmed"), ); @@ -378,7 +398,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].simulateContract as Mock).mockRejectedValue( + (protocolProvider["l2ReadClient"].simulateContract as Mock).mockRejectedValue( new ContractFunctionRevertedError({ abi: eboRequestCreatorAbi, functionName: "proposeResponse", @@ -400,7 +420,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockRejectedValue( + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockRejectedValue( new WaitForTransactionReceiptTimeoutError({ hash: "0xmockedTransactionHash" }), ); @@ -441,7 +461,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -487,7 +507,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -538,7 +558,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -563,13 +583,13 @@ describe("ProtocolProvider", () => { const mockChain: Caip2ChainId = "eip155:42161"; const mockWriteContractResponse = "0xmockedTransactionHash"; - (protocolProvider["writeClient"].writeContract as Mock).mockResolvedValue( + (protocolProvider["l2WriteClient"].writeContract as Mock).mockResolvedValue( mockWriteContractResponse, ); await protocolProvider.createRequest(mockEpoch, mockChain); - expect(protocolProvider["readClient"].simulateContract).toHaveBeenCalledWith({ + expect(protocolProvider["l2ReadClient"].simulateContract).toHaveBeenCalledWith({ address: mockContractAddress.eboRequestCreator, abi: eboRequestCreatorAbi, functionName: "createRequest", @@ -577,7 +597,7 @@ describe("ProtocolProvider", () => { account: expect.any(Object), }); - expect(protocolProvider["writeClient"].writeContract).toHaveBeenCalledWith( + expect(protocolProvider["l2WriteClient"].writeContract).toHaveBeenCalledWith( expect.objectContaining({ functionName: "createRequest", args: [mockEpoch, mockChain], @@ -605,7 +625,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["writeClient"] as any).account = undefined; + (protocolProvider["l2WriteClient"] as any).account = undefined; expect(() => protocolProvider.getAccountAddress()).toThrow(InvalidAccountOnClient); }); @@ -634,7 +654,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -673,7 +693,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -717,7 +737,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", }); @@ -747,7 +767,7 @@ describe("ProtocolProvider", () => { await expect(protocolProvider.approveModule(mockModuleAddress)).resolves.not.toThrow(); - expect(protocolProvider["readClient"].simulateContract).toHaveBeenCalledWith({ + expect(protocolProvider["l2ReadClient"].simulateContract).toHaveBeenCalledWith({ address: mockContractAddress.horizonAccountingExtension, abi: horizonAccountingExtensionAbi, functionName: "approveModule", @@ -755,7 +775,7 @@ describe("ProtocolProvider", () => { account: expect.any(Object), }); - expect(protocolProvider["writeClient"].writeContract).toHaveBeenCalledWith( + expect(protocolProvider["l2WriteClient"].writeContract).toHaveBeenCalledWith( expect.objectContaining({ functionName: "approveModule", args: [mockModuleAddress], @@ -770,7 +790,7 @@ describe("ProtocolProvider", () => { mockedPrivateKey, ); - (protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ + (protocolProvider["l2ReadClient"].waitForTransactionReceipt as Mock).mockResolvedValue({ status: "reverted", });