From 1d0d9ae19993c410c408ff9d397e1b3b1c6e9ba6 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Tue, 19 Nov 2024 19:51:37 -0500 Subject: [PATCH 01/14] feat: add service provider address to config --- apps/agent/.env.example | 4 + apps/agent/README.md | 1 + apps/agent/src/config/index.ts | 1 + apps/agent/src/config/schemas.ts | 9 ++ packages/automated-dispute/src/config.ts | 19 +++ .../src/providers/protocolProvider.ts | 25 ++- .../tests/services/protocolProvider.spec.ts | 152 +++++++++++++----- .../src/services/blockNumberService.ts | 8 +- 8 files changed, 170 insertions(+), 49 deletions(-) diff --git a/apps/agent/.env.example b/apps/agent/.env.example index ec54d70..fdf5d6a 100644 --- a/apps/agent/.env.example +++ b/apps/agent/.env.example @@ -18,3 +18,7 @@ EBO_AGENT_CONFIG_FILE_PATH="./config.example.yml" # Discord webhook notifications DISCORD_WEBHOOK=YOUR_DISCORD_WEBHOOK + +# Optional: Service Provider Address for Access Control Modules +# If not specified, it will be derived from PROTOCOL_PROVIDER_PRIVATE_KEY +SERVICE_PROVIDER_ADDRESS=0xYourServiceProviderAddressHere diff --git a/apps/agent/README.md b/apps/agent/README.md index 53b33cc..08e577a 100644 --- a/apps/agent/README.md +++ b/apps/agent/README.md @@ -38,6 +38,7 @@ cp .env.example .env | `BLOCK_NUMBER_BLOCKMETA_TOKEN` | Bearer token for the Blockmeta service (see notes below on how to obtain) | Yes | | `EBO_AGENT_CONFIG_FILE_PATH` | Path to the agent YAML configuration file | Yes | | `DISCORD_WEBHOOK` | Your Discord channel webhook for notifications [Learn how to create a Discord webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) | No | +| `SERVICE_PROVIDER_ADDRESS` | Service provider address to use with access control modules | No | **Notes:** diff --git a/apps/agent/src/config/index.ts b/apps/agent/src/config/index.ts index 96fea4d..6a845e1 100644 --- a/apps/agent/src/config/index.ts +++ b/apps/agent/src/config/index.ts @@ -68,4 +68,5 @@ export const config = { }, processor: { ...configData.processor }, DISCORD_WEBHOOK: envData.DISCORD_WEBHOOK ?? "", + SERVICE_PROVIDER_ADDRESS: envData.SERVICE_PROVIDER_ADDRESS ?? "", } as const; diff --git a/apps/agent/src/config/schemas.ts b/apps/agent/src/config/schemas.ts index 3712ebb..8e20d12 100644 --- a/apps/agent/src/config/schemas.ts +++ b/apps/agent/src/config/schemas.ts @@ -23,6 +23,13 @@ const rpcUrlsSchema = z .transform((str) => str.split(",")) .refine((arr) => arr.every((url) => z.string().url().safeParse(url).success)); +const serviceProviderAddressSchema = z + .string() + .refine((val) => isAddress(val), { + message: "serviceProviderAddress must be a valid blockchain address", + }) + .optional(); + export const envSchema = z.object({ PROTOCOL_PROVIDER_PRIVATE_KEY: z.string().refine((key) => isHex(key)), PROTOCOL_PROVIDER_L1_RPC_URLS: rpcUrlsSchema, @@ -31,9 +38,11 @@ export const envSchema = z.object({ BLOCK_NUMBER_BLOCKMETA_TOKEN: z.string(), EBO_AGENT_CONFIG_FILE_PATH: z.string(), DISCORD_WEBHOOK: z.string().optional(), + SERVICE_PROVIDER_ADDRESS: serviceProviderAddressSchema, }); const addressSchema = z.string().refine((address) => isAddress(address)); + const rpcConfigSchema = z.object({ chainId: z .string() diff --git a/packages/automated-dispute/src/config.ts b/packages/automated-dispute/src/config.ts index 14d9f5c..49d8da0 100644 --- a/packages/automated-dispute/src/config.ts +++ b/packages/automated-dispute/src/config.ts @@ -1,7 +1,26 @@ +import { isAddress } from "viem"; import { z } from "zod"; const ConfigSchema = z.object({ DISCORD_WEBHOOK: z.string().url().optional(), + accessControl: z + .object({ + /** + * The service provider address used with access control modules. + * This is an optional field. If not provided, it will be derived from the PRIVATE_KEY. + */ + serviceProviderAddress: z + .string() + .refine((val) => isAddress(val), { + message: "serviceProviderAddress must be a valid blockchain address", + }) + .optional(), + }) + .optional(), }); +/** + * Parses and validates the environment variables using the defined schema. + * If validation fails, it throws an error detailing the issues. + */ export const config = ConfigSchema.parse(process.env); diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 44b9d64..0e1adcd 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -71,15 +71,23 @@ type RpcConfig = { type ProtocolRpcConfig = { l1: RpcConfig; l2: RpcConfig; + accessControl?: { + serviceProviderAddress: Address; + }; }; // TODO: add default caching strategy for RPC client export class ProtocolProvider implements IProtocolProvider { private l1ReadClient: PublicClient>; - private l2ReadClient: PublicClient>; - private l2WriteClient: WalletClient, Chain, Account>; + private readonly l2ReadClient: PublicClient>; + private readonly l2WriteClient: WalletClient< + FallbackTransport, + Chain, + Account + >; private readonly blockNumberService?: BlockNumberService; + private readonly serviceProviderAddress: Address; private oracleContract: GetContractReturnType< typeof oracleAbi, @@ -173,6 +181,12 @@ export class ProtocolProvider implements IProtocolProvider { wallet: this.l2WriteClient, }, }); + if (this.rpcConfig.accessControl?.serviceProviderAddress) { + this.serviceProviderAddress = this.rpcConfig.accessControl.serviceProviderAddress; + } else { + const account = privateKeyToAccount(privateKey); + this.serviceProviderAddress = account.address; + } } public write: IWriteProvider = { @@ -197,6 +211,13 @@ export class ProtocolProvider implements IProtocolProvider { getEscalation: this.getEscalation.bind(this), }; + /** + * @returns {Address} The address of the service provider. + */ + public getServiceProviderAddress(): Address { + return this.serviceProviderAddress; + } + private createReadClient( config: RpcConfig, chain: Chain, diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index eb2023a..9435996 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -51,8 +51,17 @@ vi.mock("viem", async () => { }; }); +vi.mock("viem/accounts", async () => { + const actual = await vi.importActual("viem/accounts"); + return { + ...actual, + privateKeyToAccount: vi.fn(), + privateKeyToAddress: vi.fn(), + }; +}); + describe("ProtocolProvider", () => { - const mockRpcConfig = { + const mockRpcConfigBase = { l1: { chainId: "eip155:1" as Caip2ChainId, urls: ["http://localhost:8545"], @@ -68,6 +77,20 @@ describe("ProtocolProvider", () => { transactionReceiptConfirmations: 1, }, }; + const mockServiceProviderAddress = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address; + const mockDerivedAddress = "0xDerivedAddressFromPrivateKey" as Address; + + const mockRpcConfigWithServiceProvider = { + ...mockRpcConfigBase, + accessControl: { + serviceProviderAddress: mockServiceProviderAddress, + }, + }; + + const mockRpcConfigWithoutServiceProvider = { + ...mockRpcConfigBase, + // accessControl is omitted to simulate derivation from PRIVATE_KEY + }; const mockContractAddress: ProtocolContractsAddresses = { oracle: "0x1234567890123456789012345678901234567890", @@ -82,6 +105,9 @@ describe("ProtocolProvider", () => { } as unknown as BlockNumberService; beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + (getContract as Mock).mockImplementation(({ address, abi }) => { if (abi === oracleAbi && address === mockContractAddress.oracle) { return { address }; @@ -153,6 +179,18 @@ describe("ProtocolProvider", () => { getContractEvents: vi.fn(), })); + (privateKeyToAccount as Mock).mockReturnValue({ + address: mockDerivedAddress, + publicKey: "0xPublicKey", + signMessage: vi.fn(), + signTransaction: vi.fn(), + signTypedData: vi.fn(), + source: "privateKey", + type: "local", + }); + + (privateKeyToAddress as Mock).mockReturnValue(mockDerivedAddress); + const mockAccount = privateKeyToAccount(mockedPrivateKey); (createWalletClient as Mock).mockReturnValue({ @@ -171,7 +209,7 @@ describe("ProtocolProvider", () => { describe("constructor", () => { it("creates a new ProtocolProvider instance successfully", () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigWithoutServiceProvider, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -181,7 +219,7 @@ describe("ProtocolProvider", () => { expect(createPublicClient).toHaveBeenCalledWith({ chain: arbitrum, transport: fallback( - mockRpcConfig.l2.urls.map((url) => + mockRpcConfigBase.l2.urls.map((url) => http(url, { timeout: protocolProvider["TIMEOUT"], retryDelay: protocolProvider["RETRY_INTERVAL"], @@ -193,7 +231,7 @@ describe("ProtocolProvider", () => { expect(createWalletClient).toHaveBeenCalledWith({ chain: arbitrum, transport: fallback( - mockRpcConfig.l2.urls.map((url) => + mockRpcConfigWithoutServiceProvider.l2.urls.map((url) => http(url, { timeout: protocolProvider["TIMEOUT"], retryDelay: protocolProvider["RETRY_INTERVAL"], @@ -228,7 +266,7 @@ describe("ProtocolProvider", () => { expect( () => new ProtocolProvider( - { ...mockRpcConfig, l1: { ...mockRpcConfig.l1, urls: [] } }, + { ...mockRpcConfigBase, l1: { ...mockRpcConfigBase.l1, urls: [] } }, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -241,7 +279,7 @@ describe("ProtocolProvider", () => { expect( () => new ProtocolProvider( - { ...mockRpcConfig, l2: { ...mockRpcConfig.l2, urls: [] } }, + { ...mockRpcConfigBase, l2: { ...mockRpcConfigBase.l2, urls: [] } }, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -268,7 +306,7 @@ describe("ProtocolProvider", () => { } as unknown as BlockNumberService; const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -295,13 +333,13 @@ describe("ProtocolProvider", () => { expect(mockBlockNumberService.getEpochBlockNumber).toHaveBeenCalledWith( mockEpochTimestamp, - mockRpcConfig.l2.chainId, + mockRpcConfigBase.l2.chainId, ); }); it("throws BlockNumberServiceRequiredError when blockNumberService is not provided", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -315,7 +353,7 @@ describe("ProtocolProvider", () => { it("throws when current epoch request fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -336,7 +374,7 @@ describe("ProtocolProvider", () => { it("throws when current epoch block request fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -364,7 +402,7 @@ describe("ProtocolProvider", () => { describe("proposeResponse", () => { it("successfully proposes a response", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -381,7 +419,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -402,7 +440,7 @@ describe("ProtocolProvider", () => { it("throws when transaction couldn't be confirmed", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -423,7 +461,7 @@ describe("ProtocolProvider", () => { it("throws ContractFunctionRevertedError when viem throws it", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -447,7 +485,7 @@ describe("ProtocolProvider", () => { it("throws WaitForTransactionReceiptTimeoutError when waitForTransactionReceipt times out", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -470,7 +508,7 @@ describe("ProtocolProvider", () => { describe("disputeResponse", () => { it("successfully disputes a response", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -492,7 +530,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -520,7 +558,7 @@ describe("ProtocolProvider", () => { describe("escalateDispute", () => { it("successfully escalates a dispute", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -542,7 +580,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -580,7 +618,7 @@ describe("ProtocolProvider", () => { describe("finalize", () => { it("successfully finalizes a request", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -597,7 +635,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -620,7 +658,7 @@ describe("ProtocolProvider", () => { describe("createRequest", () => { it("creates a request successfully", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -657,22 +695,20 @@ describe("ProtocolProvider", () => { describe("getAccountAddress", () => { it("returns the correct account address", () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), mockBlockNumberService, ); - - const expectedAddress = privateKeyToAccount(mockedPrivateKey).address; - expect(protocolProvider.getAccountAddress()).toBe(expectedAddress); + expect(protocolProvider.getAccountAddress()).toBe(mockDerivedAddress); }); }); describe("pledgeForDispute", () => { it("successfully pledges for a dispute", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -689,7 +725,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -712,7 +748,7 @@ describe("ProtocolProvider", () => { describe("pledgeAgainstDispute", () => { it("successfully pledges against a dispute", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -732,7 +768,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -758,7 +794,7 @@ describe("ProtocolProvider", () => { describe("settleDispute", () => { it("successfully settles a dispute", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -780,7 +816,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -808,7 +844,7 @@ describe("ProtocolProvider", () => { describe("approveModule", () => { it("successfully approves a module", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -837,7 +873,7 @@ describe("ProtocolProvider", () => { it("throws TransactionExecutionError when transaction fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -859,7 +895,7 @@ describe("ProtocolProvider", () => { describe("approvedModules", () => { it("successfully retrieves approved modules for a user", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -886,7 +922,7 @@ describe("ProtocolProvider", () => { it("uses private key account as default", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -911,7 +947,7 @@ describe("ProtocolProvider", () => { it("throws error when RPC client fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -934,7 +970,7 @@ describe("ProtocolProvider", () => { describe("getOracleEvents", () => { it("successfully fetches and parses Oracle events", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -1004,7 +1040,7 @@ describe("ProtocolProvider", () => { const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -1155,7 +1191,7 @@ describe("ProtocolProvider", () => { describe("getAvailableChains", () => { it("successfully retrieves available chains", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -1183,7 +1219,7 @@ describe("ProtocolProvider", () => { it("returns an empty array if no chains are allowed", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -1204,7 +1240,7 @@ describe("ProtocolProvider", () => { it("throws FetchAvailableChainsError when RPC call fails", async () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -1224,7 +1260,7 @@ describe("ProtocolProvider", () => { describe("getAccountingModuleAddress", () => { it("returns the correct accounting module address", () => { const protocolProvider = new ProtocolProvider( - mockRpcConfig, + mockRpcConfigBase, mockContractAddress, mockedPrivateKey, mockLogger(), @@ -1236,4 +1272,32 @@ describe("ProtocolProvider", () => { ); }); }); + + describe("Service Provider Address", () => { + it("uses the provided serviceProviderAddress from env", () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigWithServiceProvider, + mockContractAddress, + mockedPrivateKey, + mockLogger(), + mockBlockNumberService, + ); + + expect(protocolProvider.getServiceProviderAddress()).toBe(mockServiceProviderAddress); + }); + + it("derives serviceProviderAddress from PRIVATE_KEY when not provided", () => { + const derivedAddress = "0xDerivedAddressFromPrivateKey" as Address; + + const protocolProvider = new ProtocolProvider( + mockRpcConfigWithoutServiceProvider, + mockContractAddress, + mockedPrivateKey, + mockLogger(), + mockBlockNumberService, + ); + + expect(protocolProvider.getServiceProviderAddress()).toBe(derivedAddress); + }); + }); }); diff --git a/packages/blocknumber/src/services/blockNumberService.ts b/packages/blocknumber/src/services/blockNumberService.ts index 1d9baa2..617bfeb 100644 --- a/packages/blocknumber/src/services/blockNumberService.ts +++ b/packages/blocknumber/src/services/blockNumberService.ts @@ -8,9 +8,11 @@ import { import { createPublicClient, fallback, http } from "viem"; import { ChainWithoutProvider, EmptyRpcUrls, UnsupportedChain } from "../exceptions/index.js"; -import { BlockmetaClientConfig } from "../providers/blockmetaJsonBlockNumberProvider.js"; -import { BlockNumberProvider } from "../providers/blockNumberProvider.js"; -import { BlockNumberProviderFactory } from "../providers/blockNumberProviderFactory.js"; +import { + BlockmetaClientConfig, + BlockNumberProvider, + BlockNumberProviderFactory, +} from "../providers/index.js"; type RpcUrl = NonNullable[0]>; From e6accab572477308555c6c3f93306f06f6a5fd40 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:09:53 -0500 Subject: [PATCH 02/14] chore: cleanup types and docs --- apps/agent/src/config/index.ts | 4 +++- apps/agent/src/config/schemas.ts | 5 +++++ apps/scripts/utilities/approveAccountingModules.ts | 11 ++++++++++- .../tests/services/protocolProvider.spec.ts | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/agent/src/config/index.ts b/apps/agent/src/config/index.ts index 6a845e1..1d72e60 100644 --- a/apps/agent/src/config/index.ts +++ b/apps/agent/src/config/index.ts @@ -68,5 +68,7 @@ export const config = { }, processor: { ...configData.processor }, DISCORD_WEBHOOK: envData.DISCORD_WEBHOOK ?? "", - SERVICE_PROVIDER_ADDRESS: envData.SERVICE_PROVIDER_ADDRESS ?? "", + accessControl: envData.SERVICE_PROVIDER_ADDRESS + ? { serviceProviderAddress: envData.SERVICE_PROVIDER_ADDRESS } + : undefined, } as const; diff --git a/apps/agent/src/config/schemas.ts b/apps/agent/src/config/schemas.ts index 8e20d12..8a43359 100644 --- a/apps/agent/src/config/schemas.ts +++ b/apps/agent/src/config/schemas.ts @@ -52,6 +52,10 @@ const rpcConfigSchema = z.object({ retryInterval: z.number().int().positive(), }); +const accessControlSchema = z.object({ + serviceProviderAddress: addressSchema.optional(), +}); + const protocolProviderConfigSchema = z.object({ rpcsConfig: z.object({ l1: rpcConfigSchema, @@ -64,6 +68,7 @@ const protocolProviderConfigSchema = z.object({ bondEscalationModule: addressSchema, horizonAccountingExtension: addressSchema, }), + accessControl: accessControlSchema.optional(), }); const blockNumberServiceSchema = z.object({ diff --git a/apps/scripts/utilities/approveAccountingModules.ts b/apps/scripts/utilities/approveAccountingModules.ts index c0f42f6..e0e721e 100644 --- a/apps/scripts/utilities/approveAccountingModules.ts +++ b/apps/scripts/utilities/approveAccountingModules.ts @@ -2,7 +2,7 @@ import { ProtocolProvider } from "@ebo-agent/automated-dispute/src/index.js"; import { mockLogger } from "@ebo-agent/automated-dispute/tests/mocks/logger.mocks.js"; import { Caip2ChainId } from "@ebo-agent/shared"; import * as dotenv from "dotenv"; -import { Address, Hex, isHex } from "viem"; +import { Address, Hex, isAddress, isHex } from "viem"; import { z } from "zod"; dotenv.config(); @@ -70,6 +70,12 @@ const envSchema = z.object({ CONTRACTS_ADDRESSES: stringToJSONSchema.pipe(contractsAddressesSchema), BONDED_RESPONSE_MODULE_ADDRESS: z.string().min(1, "BONDED_RESPONSE_MODULE_ADDRESS is required"), BOND_ESCALATION_MODULE_ADDRESS: z.string().min(1, "BOND_ESCALATION_MODULE_ADDRESS is required"), + SERVICE_PROVIDER_ADDRESS: z + .string() + .refine((val) => isAddress(val), { + message: "SERVICE_PROVIDER_ADDRESS must be a valid blockchain address", + }) + .optional(), }); const envResult = envSchema.safeParse(process.env); @@ -99,6 +105,9 @@ const rpcConfig = { timeout: env.TIMEOUT, retryInterval: env.RETRY_INTERVAL, }, + accessControl: env.SERVICE_PROVIDER_ADDRESS + ? { serviceProviderAddress: env.SERVICE_PROVIDER_ADDRESS as Address } + : undefined, }; const contracts = env.CONTRACTS_ADDRESSES; diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index 9435996..1ec7d96 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -1286,7 +1286,7 @@ describe("ProtocolProvider", () => { expect(protocolProvider.getServiceProviderAddress()).toBe(mockServiceProviderAddress); }); - it("derives serviceProviderAddress from PRIVATE_KEY when not provided", () => { + it("derives serviceProviderAddress from PROTOCOL_PROVIDER_PRIVATE_KEY when not provided", () => { const derivedAddress = "0xDerivedAddressFromPrivateKey" as Address; const protocolProvider = new ProtocolProvider( From f764db0ed69908568a32d9568370e5653c765e4f Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:11:42 -0500 Subject: [PATCH 03/14] chore: cleanup docs --- packages/automated-dispute/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/automated-dispute/src/config.ts b/packages/automated-dispute/src/config.ts index 49d8da0..1f59148 100644 --- a/packages/automated-dispute/src/config.ts +++ b/packages/automated-dispute/src/config.ts @@ -7,7 +7,7 @@ const ConfigSchema = z.object({ .object({ /** * The service provider address used with access control modules. - * This is an optional field. If not provided, it will be derived from the PRIVATE_KEY. + * This is an optional field. If not provided, it will be derived from the PROTOCOL_PROVIDER_PRIVATE_KEY. */ serviceProviderAddress: z .string() From e5db40f4030d971fddb700adea60c51f6bdc3009 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:13:07 -0500 Subject: [PATCH 04/14] chore: cleanup docs --- .../automated-dispute/tests/services/protocolProvider.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index 1ec7d96..832626e 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -89,7 +89,7 @@ describe("ProtocolProvider", () => { const mockRpcConfigWithoutServiceProvider = { ...mockRpcConfigBase, - // accessControl is omitted to simulate derivation from PRIVATE_KEY + // accessControl is omitted to simulate derivation from PROTOCOL_PROVIDER_PRIVATE_KEY }; const mockContractAddress: ProtocolContractsAddresses = { From 0d7cd9d049b9271632a4f0ff024958e7ac36e322 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:42:42 -0500 Subject: [PATCH 05/14] fix: pr comments --- apps/agent/.env.example | 6 +-- apps/agent/README.md | 1 - apps/agent/config.example.yml | 2 + apps/agent/src/config/index.ts | 3 -- apps/agent/src/config/schemas.ts | 17 +++---- .../utilities/approveAccountingModules.ts | 3 -- packages/automated-dispute/src/config.ts | 15 ------ .../src/providers/protocolProvider.ts | 49 +++++++++---------- .../tests/services/protocolProvider.spec.ts | 12 ++--- 9 files changed, 37 insertions(+), 71 deletions(-) diff --git a/apps/agent/.env.example b/apps/agent/.env.example index fdf5d6a..d825fc7 100644 --- a/apps/agent/.env.example +++ b/apps/agent/.env.example @@ -17,8 +17,4 @@ BLOCK_NUMBER_BLOCKMETA_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNlIjoidH EBO_AGENT_CONFIG_FILE_PATH="./config.example.yml" # Discord webhook notifications -DISCORD_WEBHOOK=YOUR_DISCORD_WEBHOOK - -# Optional: Service Provider Address for Access Control Modules -# If not specified, it will be derived from PROTOCOL_PROVIDER_PRIVATE_KEY -SERVICE_PROVIDER_ADDRESS=0xYourServiceProviderAddressHere +DISCORD_WEBHOOK=YOUR_DISCORD_WEBHOOK \ No newline at end of file diff --git a/apps/agent/README.md b/apps/agent/README.md index 08e577a..53b33cc 100644 --- a/apps/agent/README.md +++ b/apps/agent/README.md @@ -38,7 +38,6 @@ cp .env.example .env | `BLOCK_NUMBER_BLOCKMETA_TOKEN` | Bearer token for the Blockmeta service (see notes below on how to obtain) | Yes | | `EBO_AGENT_CONFIG_FILE_PATH` | Path to the agent YAML configuration file | Yes | | `DISCORD_WEBHOOK` | Your Discord channel webhook for notifications [Learn how to create a Discord webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) | No | -| `SERVICE_PROVIDER_ADDRESS` | Service provider address to use with access control modules | No | **Notes:** diff --git a/apps/agent/config.example.yml b/apps/agent/config.example.yml index 60f0e14..e61103b 100644 --- a/apps/agent/config.example.yml +++ b/apps/agent/config.example.yml @@ -16,6 +16,8 @@ protocolProvider: eboRequestCreator: "0x1234567890123456789012345678901234567890" # EBO Request Creator contract bondEscalationModule: "0x1234567890123456789012345678901234567890" # Bond Escalation Module contract horizonAccountingExtension: "0x1234567890123456789012345678901234567890" # Accounting extension contract + accessControl: + serviceProviderAddress: "0x1234567890123456789012345678901234567890" # Service Provider Address blockNumberService: blockmetaConfig: diff --git a/apps/agent/src/config/index.ts b/apps/agent/src/config/index.ts index 1d72e60..96fea4d 100644 --- a/apps/agent/src/config/index.ts +++ b/apps/agent/src/config/index.ts @@ -68,7 +68,4 @@ export const config = { }, processor: { ...configData.processor }, DISCORD_WEBHOOK: envData.DISCORD_WEBHOOK ?? "", - accessControl: envData.SERVICE_PROVIDER_ADDRESS - ? { serviceProviderAddress: envData.SERVICE_PROVIDER_ADDRESS } - : undefined, } as const; diff --git a/apps/agent/src/config/schemas.ts b/apps/agent/src/config/schemas.ts index 8a43359..dc83d15 100644 --- a/apps/agent/src/config/schemas.ts +++ b/apps/agent/src/config/schemas.ts @@ -23,13 +23,6 @@ const rpcUrlsSchema = z .transform((str) => str.split(",")) .refine((arr) => arr.every((url) => z.string().url().safeParse(url).success)); -const serviceProviderAddressSchema = z - .string() - .refine((val) => isAddress(val), { - message: "serviceProviderAddress must be a valid blockchain address", - }) - .optional(); - export const envSchema = z.object({ PROTOCOL_PROVIDER_PRIVATE_KEY: z.string().refine((key) => isHex(key)), PROTOCOL_PROVIDER_L1_RPC_URLS: rpcUrlsSchema, @@ -38,7 +31,6 @@ export const envSchema = z.object({ BLOCK_NUMBER_BLOCKMETA_TOKEN: z.string(), EBO_AGENT_CONFIG_FILE_PATH: z.string(), DISCORD_WEBHOOK: z.string().optional(), - SERVICE_PROVIDER_ADDRESS: serviceProviderAddressSchema, }); const addressSchema = z.string().refine((address) => isAddress(address)); @@ -52,8 +44,13 @@ const rpcConfigSchema = z.object({ retryInterval: z.number().int().positive(), }); -const accessControlSchema = z.object({ - serviceProviderAddress: addressSchema.optional(), +export const accessControlSchema = z.object({ + serviceProviderAddress: z + .string() + .refine((val) => isAddress(val), { + message: "serviceProviderAddress must be a valid blockchain address", + }) + .optional(), }); const protocolProviderConfigSchema = z.object({ diff --git a/apps/scripts/utilities/approveAccountingModules.ts b/apps/scripts/utilities/approveAccountingModules.ts index e0e721e..d27fda2 100644 --- a/apps/scripts/utilities/approveAccountingModules.ts +++ b/apps/scripts/utilities/approveAccountingModules.ts @@ -105,9 +105,6 @@ const rpcConfig = { timeout: env.TIMEOUT, retryInterval: env.RETRY_INTERVAL, }, - accessControl: env.SERVICE_PROVIDER_ADDRESS - ? { serviceProviderAddress: env.SERVICE_PROVIDER_ADDRESS as Address } - : undefined, }; const contracts = env.CONTRACTS_ADDRESSES; diff --git a/packages/automated-dispute/src/config.ts b/packages/automated-dispute/src/config.ts index 1f59148..5150f94 100644 --- a/packages/automated-dispute/src/config.ts +++ b/packages/automated-dispute/src/config.ts @@ -1,22 +1,7 @@ -import { isAddress } from "viem"; import { z } from "zod"; const ConfigSchema = z.object({ DISCORD_WEBHOOK: z.string().url().optional(), - accessControl: z - .object({ - /** - * The service provider address used with access control modules. - * This is an optional field. If not provided, it will be derived from the PROTOCOL_PROVIDER_PRIVATE_KEY. - */ - serviceProviderAddress: z - .string() - .refine((val) => isAddress(val), { - message: "serviceProviderAddress must be a valid blockchain address", - }) - .optional(), - }) - .optional(), }); /** diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 0e1adcd..ba1b6ba 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -71,9 +71,10 @@ type RpcConfig = { type ProtocolRpcConfig = { l1: RpcConfig; l2: RpcConfig; - accessControl?: { - serviceProviderAddress: Address; - }; +}; + +type ProtocolProviderConfig = ProtocolRpcConfig & { + serviceProviderAddress?: Address; }; // TODO: add default caching strategy for RPC client @@ -121,26 +122,28 @@ export class ProtocolProvider implements IProtocolProvider { /** * Creates a new ProtocolProvider instance - * @param rpcConfig The configuration for RPC connections including URLs, timeout, retry interval, and transaction receipt confirmations + * @param config The configuration for the serviceProviderAddress and RPC connections, including URLs, timeout, retry interval, and transaction receipt confirmations * @param contracts The addresses of the protocol contracts that will be instantiated * @param privateKey The private key of the account that will be used to interact with the contracts * @param logger The logger instance * @param blockNumberService The service that will be used to fetch block numbers */ constructor( - private readonly rpcConfig: ProtocolRpcConfig, + private readonly config: ProtocolProviderConfig, contracts: ProtocolContractsAddresses, privateKey: Hex, private readonly logger: ILogger, blockNumberService?: BlockNumberService, ) { - const l1Chain = this.getViemChain(rpcConfig.l1.chainId); - const l2Chain = this.getViemChain(rpcConfig.l2.chainId); + const l1Chain = this.getViemChain(config.l1.chainId); + const l2Chain = this.getViemChain(config.l2.chainId); - this.l1ReadClient = this.createReadClient(rpcConfig.l1, l1Chain); - this.l2ReadClient = this.createReadClient(rpcConfig.l2, l2Chain); - this.l2WriteClient = this.createWriteClient(rpcConfig.l2, l2Chain, privateKey); + this.l1ReadClient = this.createReadClient(config.l1, l1Chain); + this.l2ReadClient = this.createReadClient(config.l2, l2Chain); + this.l2WriteClient = this.createWriteClient(config.l2, l2Chain, privateKey); this.blockNumberService = blockNumberService; + this.serviceProviderAddress = + config.serviceProviderAddress || privateKeyToAccount(privateKey).address; // Instantiate all the protocol contracts this.oracleContract = getContract({ @@ -181,12 +184,6 @@ export class ProtocolProvider implements IProtocolProvider { wallet: this.l2WriteClient, }, }); - if (this.rpcConfig.accessControl?.serviceProviderAddress) { - this.serviceProviderAddress = this.rpcConfig.accessControl.serviceProviderAddress; - } else { - const account = privateKeyToAccount(privateKey); - this.serviceProviderAddress = account.address; - } } public write: IWriteProvider = { @@ -349,7 +346,7 @@ export class ProtocolProvider implements IProtocolProvider { const startTimestamp = epochFirstBlock.timestamp as UnixTimestamp; - const l2ChainId = this.rpcConfig.l2.chainId; + const l2ChainId = this.config.l2.chainId; const l2FirstBlockNumber = await this.blockNumberService.getEpochBlockNumber( startTimestamp, l2ChainId, @@ -825,7 +822,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -873,7 +870,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(request); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -907,7 +904,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -943,7 +940,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -978,7 +975,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1025,7 +1022,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1076,7 +1073,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1132,7 +1129,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1172,7 +1169,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.rpcConfig.l2; + const { transactionReceiptConfirmations } = this.config.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index 832626e..d660f5b 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -82,9 +82,7 @@ describe("ProtocolProvider", () => { const mockRpcConfigWithServiceProvider = { ...mockRpcConfigBase, - accessControl: { - serviceProviderAddress: mockServiceProviderAddress, - }, + serviceProviderAddress: mockServiceProviderAddress, }; const mockRpcConfigWithoutServiceProvider = { @@ -1274,7 +1272,7 @@ describe("ProtocolProvider", () => { }); describe("Service Provider Address", () => { - it("uses the provided serviceProviderAddress from env", () => { + it("uses the provided serviceProviderAddress from config", () => { const protocolProvider = new ProtocolProvider( mockRpcConfigWithServiceProvider, mockContractAddress, @@ -1286,9 +1284,7 @@ describe("ProtocolProvider", () => { expect(protocolProvider.getServiceProviderAddress()).toBe(mockServiceProviderAddress); }); - it("derives serviceProviderAddress from PROTOCOL_PROVIDER_PRIVATE_KEY when not provided", () => { - const derivedAddress = "0xDerivedAddressFromPrivateKey" as Address; - + it("derives serviceProviderAddress from PROTOCOL_PROVIDER_PRIVATE_KEY when not provided in config", () => { const protocolProvider = new ProtocolProvider( mockRpcConfigWithoutServiceProvider, mockContractAddress, @@ -1297,7 +1293,7 @@ describe("ProtocolProvider", () => { mockBlockNumberService, ); - expect(protocolProvider.getServiceProviderAddress()).toBe(derivedAddress); + expect(protocolProvider.getServiceProviderAddress()).toBe(mockDerivedAddress); }); }); }); From 03dd45ff496e136685e18329297a41d8d0763854 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 00:22:47 -0500 Subject: [PATCH 06/14] feat: draft changes to interface and abis --- .../src/abis/eboRequestCreator.ts | 717 +------ packages/automated-dispute/src/abis/oracle.ts | 1872 ++++------------- .../src/interfaces/protocolProvider.ts | 36 +- .../automated-dispute/src/types/prophet.ts | 5 + 4 files changed, 590 insertions(+), 2040 deletions(-) diff --git a/packages/automated-dispute/src/abis/eboRequestCreator.ts b/packages/automated-dispute/src/abis/eboRequestCreator.ts index dcb4275..fd564c5 100644 --- a/packages/automated-dispute/src/abis/eboRequestCreator.ts +++ b/packages/automated-dispute/src/abis/eboRequestCreator.ts @@ -2,86 +2,26 @@ export const eboRequestCreatorAbi = [ { type: "constructor", inputs: [ - { - name: "_oracle", - type: "address", - internalType: "contract IOracle", - }, - { - name: "_epochManager", - type: "address", - internalType: "contract IEpochManager", - }, - { - name: "_arbitrable", - type: "address", - internalType: "contract IArbitrable", - }, + { name: "_oracle", type: "address", internalType: "contract IOracle" }, + { name: "_epochManager", type: "address", internalType: "contract IEpochManager" }, + { name: "_arbitrable", type: "address", internalType: "contract IArbitrable" }, { name: "_requestData", type: "tuple", internalType: "struct IOracle.Request", components: [ - { - name: "nonce", - type: "uint96", - internalType: "uint96", - }, - { - name: "requester", - type: "address", - internalType: "address", - }, - { - name: "requestModule", - type: "address", - internalType: "address", - }, - { - name: "responseModule", - type: "address", - internalType: "address", - }, - { - name: "disputeModule", - type: "address", - internalType: "address", - }, - { - name: "resolutionModule", - type: "address", - internalType: "address", - }, - { - name: "finalityModule", - type: "address", - internalType: "address", - }, - { - name: "requestModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "responseModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "disputeModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "resolutionModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "finalityModuleData", - type: "bytes", - internalType: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], }, ], @@ -91,51 +31,27 @@ export const eboRequestCreatorAbi = [ type: "function", name: "ARBITRABLE", inputs: [], - outputs: [ - { - name: "", - type: "address", - internalType: "contract IArbitrable", - }, - ], + outputs: [{ name: "", type: "address", internalType: "contract IArbitrable" }], stateMutability: "view", }, { type: "function", name: "ORACLE", inputs: [], - outputs: [ - { - name: "", - type: "address", - internalType: "contract IOracle", - }, - ], + outputs: [{ name: "", type: "address", internalType: "contract IOracle" }], stateMutability: "view", }, { type: "function", name: "START_EPOCH", inputs: [], - outputs: [ - { - name: "", - type: "uint256", - internalType: "uint256", - }, - ], + outputs: [{ name: "", type: "uint256", internalType: "uint256" }], stateMutability: "view", }, { type: "function", name: "addChain", - inputs: [ - { - name: "_chainId", - type: "string", - internalType: "string", - }, - ], + inputs: [{ name: "_chainId", type: "string", internalType: "string" }], outputs: [], stateMutability: "nonpayable", }, @@ -143,16 +59,8 @@ export const eboRequestCreatorAbi = [ type: "function", name: "createRequest", inputs: [ - { - name: "_epoch", - type: "uint256", - internalType: "uint256", - }, - { - name: "_chainId", - type: "string", - internalType: "string", - }, + { name: "_epoch", type: "uint256", internalType: "uint256" }, + { name: "_chainId", type: "string", internalType: "string" }, ], outputs: [], stateMutability: "nonpayable", @@ -161,26 +69,14 @@ export const eboRequestCreatorAbi = [ type: "function", name: "epochManager", inputs: [], - outputs: [ - { - name: "", - type: "address", - internalType: "contract IEpochManager", - }, - ], + outputs: [{ name: "", type: "address", internalType: "contract IEpochManager" }], stateMutability: "view", }, { type: "function", name: "getAllowedChainIds", inputs: [], - outputs: [ - { - name: "_chainIds", - type: "bytes32[]", - internalType: "bytes32[]", - }, - ], + outputs: [{ name: "_chainIds", type: "bytes32[]", internalType: "bytes32[]" }], stateMutability: "view", }, { @@ -193,66 +89,18 @@ export const eboRequestCreatorAbi = [ type: "tuple", internalType: "struct IOracle.Request", components: [ - { - name: "nonce", - type: "uint96", - internalType: "uint96", - }, - { - name: "requester", - type: "address", - internalType: "address", - }, - { - name: "requestModule", - type: "address", - internalType: "address", - }, - { - name: "responseModule", - type: "address", - internalType: "address", - }, - { - name: "disputeModule", - type: "address", - internalType: "address", - }, - { - name: "resolutionModule", - type: "address", - internalType: "address", - }, - { - name: "finalityModule", - type: "address", - internalType: "address", - }, - { - name: "requestModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "responseModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "disputeModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "resolutionModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "finalityModuleData", - type: "bytes", - internalType: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], }, ], @@ -261,13 +109,7 @@ export const eboRequestCreatorAbi = [ { type: "function", name: "removeChain", - inputs: [ - { - name: "_chainId", - type: "string", - internalType: "string", - }, - ], + inputs: [{ name: "_chainId", type: "string", internalType: "string" }], outputs: [], stateMutability: "nonpayable", }, @@ -276,66 +118,18 @@ export const eboRequestCreatorAbi = [ name: "requestData", inputs: [], outputs: [ - { - name: "nonce", - type: "uint96", - internalType: "uint96", - }, - { - name: "requester", - type: "address", - internalType: "address", - }, - { - name: "requestModule", - type: "address", - internalType: "address", - }, - { - name: "responseModule", - type: "address", - internalType: "address", - }, - { - name: "disputeModule", - type: "address", - internalType: "address", - }, - { - name: "resolutionModule", - type: "address", - internalType: "address", - }, - { - name: "finalityModule", - type: "address", - internalType: "address", - }, - { - name: "requestModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "responseModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "disputeModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "resolutionModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "finalityModuleData", - type: "bytes", - internalType: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], stateMutability: "view", }, @@ -343,35 +137,17 @@ export const eboRequestCreatorAbi = [ type: "function", name: "requestIdPerChainAndEpoch", inputs: [ - { - name: "_chainId", - type: "string", - internalType: "string", - }, - { - name: "_epoch", - type: "uint256", - internalType: "uint256", - }, - ], - outputs: [ - { - name: "_requestId", - type: "bytes32", - internalType: "bytes32", - }, + { name: "_chainId", type: "string", internalType: "string" }, + { name: "_epoch", type: "uint256", internalType: "uint256" }, ], + outputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], stateMutability: "view", }, { type: "function", name: "setDisputeModuleData", inputs: [ - { - name: "_disputeModule", - type: "address", - internalType: "address", - }, + { name: "_disputeModule", type: "address", internalType: "address" }, { name: "_disputeModuleData", type: "tuple", @@ -382,36 +158,12 @@ export const eboRequestCreatorAbi = [ type: "address", internalType: "contract IBondEscalationAccounting", }, - { - name: "bondToken", - type: "address", - internalType: "contract IERC20", - }, - { - name: "bondSize", - type: "uint256", - internalType: "uint256", - }, - { - name: "maxNumberOfEscalations", - type: "uint256", - internalType: "uint256", - }, - { - name: "bondEscalationDeadline", - type: "uint256", - internalType: "uint256", - }, - { - name: "tyingBuffer", - type: "uint256", - internalType: "uint256", - }, - { - name: "disputeWindow", - type: "uint256", - internalType: "uint256", - }, + { name: "bondToken", type: "address", internalType: "contract IERC20" }, + { name: "bondSize", type: "uint256", internalType: "uint256" }, + { name: "maxNumberOfEscalations", type: "uint256", internalType: "uint256" }, + { name: "bondEscalationDeadline", type: "uint256", internalType: "uint256" }, + { name: "tyingBuffer", type: "uint256", internalType: "uint256" }, + { name: "disputeWindow", type: "uint256", internalType: "uint256" }, ], }, ], @@ -422,11 +174,7 @@ export const eboRequestCreatorAbi = [ type: "function", name: "setEpochManager", inputs: [ - { - name: "_epochManager", - type: "address", - internalType: "contract IEpochManager", - }, + { name: "_epochManager", type: "address", internalType: "contract IEpochManager" }, ], outputs: [], stateMutability: "nonpayable", @@ -434,13 +182,7 @@ export const eboRequestCreatorAbi = [ { type: "function", name: "setFinalityModuleData", - inputs: [ - { - name: "_finalityModule", - type: "address", - internalType: "address", - }, - ], + inputs: [{ name: "_finalityModule", type: "address", internalType: "address" }], outputs: [], stateMutability: "nonpayable", }, @@ -448,36 +190,20 @@ export const eboRequestCreatorAbi = [ type: "function", name: "setRequestModuleData", inputs: [ - { - name: "_requestModule", - type: "address", - internalType: "address", - }, + { name: "_requestModule", type: "address", internalType: "address" }, { name: "_requestModuleData", type: "tuple", internalType: "struct IEBORequestModule.RequestParameters", components: [ - { - name: "epoch", - type: "uint256", - internalType: "uint256", - }, - { - name: "chainId", - type: "string", - internalType: "string", - }, + { name: "epoch", type: "uint256", internalType: "uint256" }, + { name: "chainId", type: "string", internalType: "string" }, { name: "accountingExtension", type: "address", internalType: "contract IAccountingExtension", }, - { - name: "paymentAmount", - type: "uint256", - internalType: "uint256", - }, + { name: "paymentAmount", type: "uint256", internalType: "uint256" }, ], }, ], @@ -488,22 +214,12 @@ export const eboRequestCreatorAbi = [ type: "function", name: "setResolutionModuleData", inputs: [ - { - name: "_resolutionModule", - type: "address", - internalType: "address", - }, + { name: "_resolutionModule", type: "address", internalType: "address" }, { name: "_resolutionModuleData", type: "tuple", internalType: "struct IArbitratorModule.RequestParameters", - components: [ - { - name: "arbitrator", - type: "address", - internalType: "address", - }, - ], + components: [{ name: "arbitrator", type: "address", internalType: "address" }], }, ], outputs: [], @@ -513,11 +229,7 @@ export const eboRequestCreatorAbi = [ type: "function", name: "setResponseModuleData", inputs: [ - { - name: "_responseModule", - type: "address", - internalType: "address", - }, + { name: "_responseModule", type: "address", internalType: "address" }, { name: "_responseModuleData", type: "tuple", @@ -528,26 +240,10 @@ export const eboRequestCreatorAbi = [ type: "address", internalType: "contract IAccountingExtension", }, - { - name: "bondToken", - type: "address", - internalType: "contract IERC20", - }, - { - name: "bondSize", - type: "uint256", - internalType: "uint256", - }, - { - name: "deadline", - type: "uint256", - internalType: "uint256", - }, - { - name: "disputeWindow", - type: "uint256", - internalType: "uint256", - }, + { name: "bondToken", type: "address", internalType: "contract IERC20" }, + { name: "bondSize", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + { name: "disputeWindow", type: "uint256", internalType: "uint256" }, ], }, ], @@ -557,39 +253,20 @@ export const eboRequestCreatorAbi = [ { type: "event", name: "ChainAdded", - inputs: [ - { - name: "_chainId", - type: "string", - indexed: true, - internalType: "string", - }, - ], + inputs: [{ name: "_chainId", type: "string", indexed: true, internalType: "string" }], anonymous: false, }, { type: "event", name: "ChainRemoved", - inputs: [ - { - name: "_chainId", - type: "string", - indexed: true, - internalType: "string", - }, - ], + inputs: [{ name: "_chainId", type: "string", indexed: true, internalType: "string" }], anonymous: false, }, { type: "event", name: "DisputeModuleDataSet", inputs: [ - { - name: "_disputeModule", - type: "address", - indexed: true, - internalType: "address", - }, + { name: "_disputeModule", type: "address", indexed: true, internalType: "address" }, { name: "_disputeModuleData", type: "tuple", @@ -601,36 +278,12 @@ export const eboRequestCreatorAbi = [ type: "address", internalType: "contract IBondEscalationAccounting", }, - { - name: "bondToken", - type: "address", - internalType: "contract IERC20", - }, - { - name: "bondSize", - type: "uint256", - internalType: "uint256", - }, - { - name: "maxNumberOfEscalations", - type: "uint256", - internalType: "uint256", - }, - { - name: "bondEscalationDeadline", - type: "uint256", - internalType: "uint256", - }, - { - name: "tyingBuffer", - type: "uint256", - internalType: "uint256", - }, - { - name: "disputeWindow", - type: "uint256", - internalType: "uint256", - }, + { name: "bondToken", type: "address", internalType: "contract IERC20" }, + { name: "bondSize", type: "uint256", internalType: "uint256" }, + { name: "maxNumberOfEscalations", type: "uint256", internalType: "uint256" }, + { name: "bondEscalationDeadline", type: "uint256", internalType: "uint256" }, + { name: "tyingBuffer", type: "uint256", internalType: "uint256" }, + { name: "disputeWindow", type: "uint256", internalType: "uint256" }, ], }, ], @@ -653,18 +306,8 @@ export const eboRequestCreatorAbi = [ type: "event", name: "FinalityModuleDataSet", inputs: [ - { - name: "_finalityModule", - type: "address", - indexed: true, - internalType: "address", - }, - { - name: "_finalityModuleData", - type: "bytes", - indexed: false, - internalType: "bytes", - }, + { name: "_finalityModule", type: "address", indexed: true, internalType: "address" }, + { name: "_finalityModuleData", type: "bytes", indexed: false, internalType: "bytes" }, ], anonymous: false, }, @@ -672,92 +315,29 @@ export const eboRequestCreatorAbi = [ type: "event", name: "RequestCreated", inputs: [ - { - name: "_requestId", - type: "bytes32", - indexed: true, - internalType: "bytes32", - }, + { name: "_requestId", type: "bytes32", indexed: true, internalType: "bytes32" }, { name: "_request", type: "tuple", indexed: false, internalType: "struct IOracle.Request", components: [ - { - name: "nonce", - type: "uint96", - internalType: "uint96", - }, - { - name: "requester", - type: "address", - internalType: "address", - }, - { - name: "requestModule", - type: "address", - internalType: "address", - }, - { - name: "responseModule", - type: "address", - internalType: "address", - }, - { - name: "disputeModule", - type: "address", - internalType: "address", - }, - { - name: "resolutionModule", - type: "address", - internalType: "address", - }, - { - name: "finalityModule", - type: "address", - internalType: "address", - }, - { - name: "requestModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "responseModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "disputeModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "resolutionModuleData", - type: "bytes", - internalType: "bytes", - }, - { - name: "finalityModuleData", - type: "bytes", - internalType: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], }, - { - name: "_epoch", - type: "uint256", - indexed: true, - internalType: "uint256", - }, - { - name: "_chainId", - type: "string", - indexed: true, - internalType: "string", - }, + { name: "_epoch", type: "uint256", indexed: true, internalType: "uint256" }, + { name: "_chainId", type: "string", indexed: true, internalType: "string" }, ], anonymous: false, }, @@ -765,38 +345,21 @@ export const eboRequestCreatorAbi = [ type: "event", name: "RequestModuleDataSet", inputs: [ - { - name: "_requestModule", - type: "address", - indexed: true, - internalType: "address", - }, + { name: "_requestModule", type: "address", indexed: true, internalType: "address" }, { name: "_requestModuleData", type: "tuple", indexed: false, internalType: "struct IEBORequestModule.RequestParameters", components: [ - { - name: "epoch", - type: "uint256", - internalType: "uint256", - }, - { - name: "chainId", - type: "string", - internalType: "string", - }, + { name: "epoch", type: "uint256", internalType: "uint256" }, + { name: "chainId", type: "string", internalType: "string" }, { name: "accountingExtension", type: "address", internalType: "contract IAccountingExtension", }, - { - name: "paymentAmount", - type: "uint256", - internalType: "uint256", - }, + { name: "paymentAmount", type: "uint256", internalType: "uint256" }, ], }, ], @@ -806,24 +369,13 @@ export const eboRequestCreatorAbi = [ type: "event", name: "ResolutionModuleDataSet", inputs: [ - { - name: "_resolutionModule", - type: "address", - indexed: true, - internalType: "address", - }, + { name: "_resolutionModule", type: "address", indexed: true, internalType: "address" }, { name: "_resolutionModuleData", type: "tuple", indexed: false, internalType: "struct IArbitratorModule.RequestParameters", - components: [ - { - name: "arbitrator", - type: "address", - internalType: "address", - }, - ], + components: [{ name: "arbitrator", type: "address", internalType: "address" }], }, ], anonymous: false, @@ -832,12 +384,7 @@ export const eboRequestCreatorAbi = [ type: "event", name: "ResponseModuleDataSet", inputs: [ - { - name: "_responseModule", - type: "address", - indexed: true, - internalType: "address", - }, + { name: "_responseModule", type: "address", indexed: true, internalType: "address" }, { name: "_responseModuleData", type: "tuple", @@ -849,54 +396,18 @@ export const eboRequestCreatorAbi = [ type: "address", internalType: "contract IAccountingExtension", }, - { - name: "bondToken", - type: "address", - internalType: "contract IERC20", - }, - { - name: "bondSize", - type: "uint256", - internalType: "uint256", - }, - { - name: "deadline", - type: "uint256", - internalType: "uint256", - }, - { - name: "disputeWindow", - type: "uint256", - internalType: "uint256", - }, + { name: "bondToken", type: "address", internalType: "contract IERC20" }, + { name: "bondSize", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + { name: "disputeWindow", type: "uint256", internalType: "uint256" }, ], }, ], anonymous: false, }, - { - type: "error", - name: "EBORequestCreator_ChainAlreadyAdded", - inputs: [], - }, - { - type: "error", - name: "EBORequestCreator_ChainNotAdded", - inputs: [], - }, - { - type: "error", - name: "EBORequestCreator_InvalidEpoch", - inputs: [], - }, - { - type: "error", - name: "EBORequestCreator_InvalidNonce", - inputs: [], - }, - { - type: "error", - name: "EBORequestCreator_RequestAlreadyCreated", - inputs: [], - }, -] as const; + { type: "error", name: "EBORequestCreator_ChainAlreadyAdded", inputs: [] }, + { type: "error", name: "EBORequestCreator_ChainNotAdded", inputs: [] }, + { type: "error", name: "EBORequestCreator_InvalidEpoch", inputs: [] }, + { type: "error", name: "EBORequestCreator_InvalidNonce", inputs: [] }, + { type: "error", name: "EBORequestCreator_RequestAlreadyCreated", inputs: [] }, +]; diff --git a/packages/automated-dispute/src/abis/oracle.ts b/packages/automated-dispute/src/abis/oracle.ts index 87f22ca..43e2927 100644 --- a/packages/automated-dispute/src/abis/oracle.ts +++ b/packages/automated-dispute/src/abis/oracle.ts @@ -1,1617 +1,623 @@ export const oracleAbi = [ { + type: "function", + name: "allowedModule", inputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - ], - name: "Oracle_AlreadyFinalized", - type: "error", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - ], - name: "Oracle_CannotEscalate", - type: "error", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - ], - name: "Oracle_CannotResolve", - type: "error", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_responseId", - type: "bytes32", - }, - ], - name: "Oracle_FinalizableResponseExists", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidDispute", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidDisputeBody", - type: "error", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - ], - name: "Oracle_InvalidDisputeId", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidDisputer", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidFinalizedResponse", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidProposer", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidRequest", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidRequestBody", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidResponse", - type: "error", - }, - { - inputs: [], - name: "Oracle_InvalidResponseBody", - type: "error", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - ], - name: "Oracle_NoResolutionModule", - type: "error", - }, - { - inputs: [ - { - internalType: "address", - name: "_caller", - type: "address", - }, - ], - name: "Oracle_NotDisputeOrResolutionModule", - type: "error", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_responseId", - type: "bytes32", - }, - ], - name: "Oracle_ResponseAlreadyDisputed", - type: "error", - }, - { - inputs: [], - name: "Oracle_ResponseAlreadyProposed", - type: "error", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "address", - name: "_caller", - type: "address", - }, - { - indexed: true, - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - { - components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - ], - indexed: false, - internalType: "struct IOracle.Dispute", - name: "_dispute", - type: "tuple", - }, - ], - name: "DisputeEscalated", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - { - components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - ], - indexed: false, - internalType: "struct IOracle.Dispute", - name: "_dispute", - type: "tuple", - }, - { - indexed: true, - internalType: "address", - name: "_caller", - type: "address", - }, - ], - name: "DisputeResolved", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - { - components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - ], - indexed: false, - internalType: "struct IOracle.Dispute", - name: "_dispute", - type: "tuple", - }, - { - indexed: false, - internalType: "enum IOracle.DisputeStatus", - name: "_status", - type: "uint8", - }, - ], - name: "DisputeStatusUpdated", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - { - indexed: true, - internalType: "bytes32", - name: "_responseId", - type: "bytes32", - }, - { - indexed: true, - internalType: "address", - name: "_caller", - type: "address", - }, + { name: "_requestId", type: "bytes32", internalType: "bytes32" }, + { name: "_module", type: "address", internalType: "address" }, ], - name: "OracleRequestFinalized", - type: "event", + outputs: [{ name: "_allowed", type: "bool", internalType: "bool" }], + stateMutability: "view", }, { - anonymous: false, + type: "function", + name: "createRequest", inputs: [ { - indexed: true, - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - indexed: false, - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { - indexed: false, - internalType: "bytes32", - name: "_ipfsHash", - type: "bytes32", - }, - ], - name: "RequestCreated", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "bytes32", - name: "_responseId", - type: "bytes32", - }, - { - indexed: true, - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - { - components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - ], - indexed: false, - internalType: "struct IOracle.Dispute", - name: "_dispute", - type: "tuple", - }, - ], - name: "ResponseDisputed", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - { - indexed: true, - internalType: "bytes32", - name: "_responseId", - type: "bytes32", - }, - { + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - indexed: false, - internalType: "struct IOracle.Response", - name: "_response", - type: "tuple", }, + { name: "_ipfsHash", type: "bytes32", internalType: "bytes32" }, ], - name: "ResponseProposed", - type: "event", + outputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], + stateMutability: "nonpayable", }, { - inputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - { - internalType: "address", - name: "_module", - type: "address", - }, - ], - name: "allowedModule", - outputs: [ - { - internalType: "bool", - name: "_isAllowed", - type: "bool", - }, - ], - stateMutability: "view", type: "function", - }, - { + name: "createRequests", inputs: [ { + name: "_requestsData", + type: "tuple[]", + internalType: "struct IOracle.Request[]", components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Request", - name: "_request", - type: "tuple", - }, - { - internalType: "bytes32", - name: "_ipfsHash", - type: "bytes32", - }, - ], - name: "createRequest", - outputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", }, + { name: "_ipfsHashes", type: "bytes32[]", internalType: "bytes32[]" }, ], + outputs: [{ name: "_batchRequestsIds", type: "bytes32[]", internalType: "bytes32[]" }], stateMutability: "nonpayable", - type: "function", }, { - inputs: [ - { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request[]", - name: "_requestsData", - type: "tuple[]", - }, - { - internalType: "bytes32[]", - name: "_ipfsHashes", - type: "bytes32[]", - }, - ], - name: "createRequests", - outputs: [ - { - internalType: "bytes32[]", - name: "_batchRequestsIds", - type: "bytes32[]", - }, - ], - stateMutability: "nonpayable", type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "_id", - type: "bytes32", - }, - ], name: "disputeCreatedAt", - outputs: [ - { - internalType: "uint256", - name: "_disputeCreatedAt", - type: "uint256", - }, - ], + inputs: [{ name: "_id", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_disputeCreatedAt", type: "uint256", internalType: "uint256" }], stateMutability: "view", - type: "function", }, { - inputs: [ - { - internalType: "bytes32", - name: "_responseId", - type: "bytes32", - }, - ], + type: "function", name: "disputeOf", - outputs: [ - { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - ], + inputs: [{ name: "_responseId", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], stateMutability: "view", - type: "function", }, { + type: "function", + name: "disputeResponse", inputs: [ { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Response", - name: "_response", - type: "tuple", }, { + name: "_response", + type: "tuple", + internalType: "struct IOracle.Response", components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Dispute", - name: "_dispute", - type: "tuple", }, - ], - name: "disputeResponse", - outputs: [ { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", + name: "_dispute", + type: "tuple", + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, ], + outputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], stateMutability: "nonpayable", - type: "function", }, { - inputs: [ - { - internalType: "bytes32", - name: "_disputeId", - type: "bytes32", - }, - ], + type: "function", name: "disputeStatus", - outputs: [ - { - internalType: "enum IOracle.DisputeStatus", - name: "_status", - type: "uint8", - }, - ], + inputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_status", type: "uint8", internalType: "enum IOracle.DisputeStatus" }], stateMutability: "view", - type: "function", }, { + type: "function", + name: "escalateDispute", inputs: [ { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Response", - name: "_response", - type: "tuple", }, { + name: "_response", + type: "tuple", + internalType: "struct IOracle.Response", components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Dispute", + }, + { name: "_dispute", type: "tuple", + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, ], - name: "escalateDispute", outputs: [], stateMutability: "nonpayable", - type: "function", }, { + type: "function", + name: "finalize", inputs: [ { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Response", + }, + { name: "_response", type: "tuple", + internalType: "struct IOracle.Response", + components: [ + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, + ], }, ], - name: "finalize", outputs: [], stateMutability: "nonpayable", - type: "function", }, - { - inputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - ], - name: "finalizedAt", - outputs: [ - { - internalType: "uint256", - name: "_finalizedAt", - type: "uint256", - }, - ], - stateMutability: "view", + { type: "function", + name: "finalizedAt", + inputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_finalizedAt", type: "uint256", internalType: "uint256" }], + stateMutability: "view", }, { - inputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - ], + type: "function", name: "finalizedResponseId", - outputs: [ - { - internalType: "bytes32", - name: "_finalizedResponseId", - type: "bytes32", - }, - ], + inputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_finalizedResponseId", type: "bytes32", internalType: "bytes32" }], stateMutability: "view", - type: "function", }, { - inputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - ], + type: "function", name: "getResponseIds", - outputs: [ - { - internalType: "bytes32[]", - name: "_ids", - type: "bytes32[]", - }, - ], + inputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_ids", type: "bytes32[]", internalType: "bytes32[]" }], stateMutability: "view", - type: "function", }, { - inputs: [ - { - internalType: "bytes32", - name: "_requestId", - type: "bytes32", - }, - { - internalType: "address", - name: "_user", - type: "address", - }, - ], + type: "function", name: "isParticipant", - outputs: [ - { - internalType: "bool", - name: "_isParticipant", - type: "bool", - }, + inputs: [ + { name: "_requestId", type: "bytes32", internalType: "bytes32" }, + { name: "_user", type: "address", internalType: "address" }, ], + outputs: [{ name: "_isParticipant", type: "bool", internalType: "bool" }], stateMutability: "view", - type: "function", }, { - inputs: [ - { - internalType: "uint256", - name: "_startFrom", - type: "uint256", - }, - { - internalType: "uint256", - name: "_batchSize", - type: "uint256", - }, - ], + type: "function", name: "listRequestIds", - outputs: [ - { - internalType: "bytes32[]", - name: "_list", - type: "bytes32[]", - }, + inputs: [ + { name: "_startFrom", type: "uint256", internalType: "uint256" }, + { name: "_batchSize", type: "uint256", internalType: "uint256" }, ], + outputs: [{ name: "_list", type: "bytes32[]", internalType: "bytes32[]" }], stateMutability: "view", + }, + { type: "function", + name: "nonceToRequestId", + inputs: [{ name: "_requestNumber", type: "uint256", internalType: "uint256" }], + outputs: [{ name: "_id", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", }, { + type: "function", + name: "proposeResponse", inputs: [ { - internalType: "uint256", - name: "_requestNumber", - type: "uint256", + name: "_request", + type: "tuple", + internalType: "struct IOracle.Request", + components: [ + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, + ], }, - ], - name: "nonceToRequestId", - outputs: [ { - internalType: "bytes32", - name: "_id", - type: "bytes32", + name: "_response", + type: "tuple", + internalType: "struct IOracle.Response", + components: [ + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, + ], }, ], - stateMutability: "view", + outputs: [{ name: "_responseId", type: "bytes32", internalType: "bytes32" }], + stateMutability: "nonpayable", + }, + { type: "function", + name: "requestCreatedAt", + inputs: [{ name: "_id", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_requestCreatedAt", type: "uint256", internalType: "uint256" }], + stateMutability: "view", }, { + type: "function", + name: "resolveDispute", inputs: [ { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Response", + }, + { name: "_response", type: "tuple", + internalType: "struct IOracle.Response", + components: [ + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, + ], }, - ], - name: "proposeResponse", - outputs: [ { - internalType: "bytes32", - name: "_responseId", - type: "bytes32", + name: "_dispute", + type: "tuple", + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, ], + outputs: [], stateMutability: "nonpayable", - type: "function", }, { - inputs: [ - { - internalType: "bytes32", - name: "_id", - type: "bytes32", - }, - ], - name: "requestCreatedAt", - outputs: [ - { - internalType: "uint256", - name: "_requestCreatedAt", - type: "uint256", - }, - ], + type: "function", + name: "responseCreatedAt", + inputs: [{ name: "_id", type: "bytes32", internalType: "bytes32" }], + outputs: [{ name: "_responseCreatedAt", type: "uint256", internalType: "uint256" }], stateMutability: "view", + }, + { type: "function", + name: "totalRequestCount", + inputs: [], + outputs: [{ name: "", type: "uint256", internalType: "uint256" }], + stateMutability: "view", }, { + type: "function", + name: "updateDisputeStatus", inputs: [ { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Response", - name: "_response", - type: "tuple", }, { + name: "_response", + type: "tuple", + internalType: "struct IOracle.Response", components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Dispute", + }, + { name: "_dispute", type: "tuple", + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, + { name: "_status", type: "uint8", internalType: "enum IOracle.DisputeStatus" }, ], - name: "resolveDispute", outputs: [], stateMutability: "nonpayable", - type: "function", }, { + type: "event", + name: "DisputeEscalated", inputs: [ + { name: "_caller", type: "address", indexed: true, internalType: "address" }, + { name: "_disputeId", type: "bytes32", indexed: true, internalType: "bytes32" }, { - internalType: "bytes32", - name: "_id", - type: "bytes32", + name: "_dispute", + type: "tuple", + indexed: false, + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, ], - name: "responseCreatedAt", - outputs: [ + anonymous: false, + }, + { + type: "event", + name: "DisputeResolved", + inputs: [ + { name: "_disputeId", type: "bytes32", indexed: true, internalType: "bytes32" }, { - internalType: "uint256", - name: "_responseCreatedAt", - type: "uint256", + name: "_dispute", + type: "tuple", + indexed: false, + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, + { name: "_caller", type: "address", indexed: true, internalType: "address" }, ], - stateMutability: "view", - type: "function", + anonymous: false, }, { - inputs: [], - name: "totalRequestCount", - outputs: [ + type: "event", + name: "DisputeStatusUpdated", + inputs: [ + { name: "_disputeId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { + name: "_dispute", + type: "tuple", + indexed: false, + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], + }, { - internalType: "uint256", - name: "", - type: "uint256", + name: "_status", + type: "uint8", + indexed: false, + internalType: "enum IOracle.DisputeStatus", }, ], - stateMutability: "view", - type: "function", + anonymous: false, + }, + { + type: "event", + name: "OracleRequestFinalized", + inputs: [ + { name: "_requestId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "_responseId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "_caller", type: "address", indexed: true, internalType: "address" }, + ], + anonymous: false, }, { + type: "event", + name: "RequestCreated", inputs: [ + { name: "_requestId", type: "bytes32", indexed: true, internalType: "bytes32" }, { - components: [ - { - internalType: "uint96", - name: "nonce", - type: "uint96", - }, - { - internalType: "address", - name: "requester", - type: "address", - }, - { - internalType: "address", - name: "requestModule", - type: "address", - }, - { - internalType: "address", - name: "responseModule", - type: "address", - }, - { - internalType: "address", - name: "disputeModule", - type: "address", - }, - { - internalType: "address", - name: "resolutionModule", - type: "address", - }, - { - internalType: "address", - name: "finalityModule", - type: "address", - }, - { - internalType: "bytes", - name: "requestModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "responseModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "disputeModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "resolutionModuleData", - type: "bytes", - }, - { - internalType: "bytes", - name: "finalityModuleData", - type: "bytes", - }, - ], - internalType: "struct IOracle.Request", name: "_request", type: "tuple", - }, - { + indexed: false, + internalType: "struct IOracle.Request", components: [ - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - { - internalType: "bytes", - name: "response", - type: "bytes", - }, + { name: "nonce", type: "uint96", internalType: "uint96" }, + { name: "requester", type: "address", internalType: "address" }, + { name: "requestModule", type: "address", internalType: "address" }, + { name: "responseModule", type: "address", internalType: "address" }, + { name: "disputeModule", type: "address", internalType: "address" }, + { name: "resolutionModule", type: "address", internalType: "address" }, + { name: "finalityModule", type: "address", internalType: "address" }, + { name: "requestModuleData", type: "bytes", internalType: "bytes" }, + { name: "responseModuleData", type: "bytes", internalType: "bytes" }, + { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, + { name: "resolutionModuleData", type: "bytes", internalType: "bytes" }, + { name: "finalityModuleData", type: "bytes", internalType: "bytes" }, ], - internalType: "struct IOracle.Response", - name: "_response", - type: "tuple", }, + { name: "_ipfsHash", type: "bytes32", indexed: false, internalType: "bytes32" }, + ], + anonymous: false, + }, + { + type: "event", + name: "ResponseDisputed", + inputs: [ + { name: "_responseId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "_disputeId", type: "bytes32", indexed: true, internalType: "bytes32" }, { - components: [ - { - internalType: "address", - name: "disputer", - type: "address", - }, - { - internalType: "address", - name: "proposer", - type: "address", - }, - { - internalType: "bytes32", - name: "responseId", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "requestId", - type: "bytes32", - }, - ], - internalType: "struct IOracle.Dispute", name: "_dispute", type: "tuple", + indexed: false, + internalType: "struct IOracle.Dispute", + components: [ + { name: "disputer", type: "address", internalType: "address" }, + { name: "proposer", type: "address", internalType: "address" }, + { name: "responseId", type: "bytes32", internalType: "bytes32" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + ], }, + ], + anonymous: false, + }, + { + type: "event", + name: "ResponseProposed", + inputs: [ + { name: "_requestId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "_responseId", type: "bytes32", indexed: true, internalType: "bytes32" }, { - internalType: "enum IOracle.DisputeStatus", - name: "_status", - type: "uint8", + name: "_response", + type: "tuple", + indexed: false, + internalType: "struct IOracle.Response", + components: [ + { name: "proposer", type: "address", internalType: "address" }, + { name: "requestId", type: "bytes32", internalType: "bytes32" }, + { name: "response", type: "bytes", internalType: "bytes" }, + ], }, ], - name: "updateDisputeStatus", - outputs: [], - stateMutability: "nonpayable", - type: "function", + anonymous: false, + }, + { + type: "error", + name: "Oracle_AlreadyFinalized", + inputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], + }, + { + type: "error", + name: "Oracle_CannotEscalate", + inputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], + }, + { + type: "error", + name: "Oracle_CannotResolve", + inputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], + }, + { + type: "error", + name: "Oracle_FinalizableResponseExists", + inputs: [{ name: "_responseId", type: "bytes32", internalType: "bytes32" }], + }, + { type: "error", name: "Oracle_InvalidDispute", inputs: [] }, + { type: "error", name: "Oracle_InvalidDisputeBody", inputs: [] }, + { + type: "error", + name: "Oracle_InvalidDisputeId", + inputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], + }, + { type: "error", name: "Oracle_InvalidDisputer", inputs: [] }, + { type: "error", name: "Oracle_InvalidFinalizedResponse", inputs: [] }, + { type: "error", name: "Oracle_InvalidProposer", inputs: [] }, + { type: "error", name: "Oracle_InvalidRequest", inputs: [] }, + { type: "error", name: "Oracle_InvalidRequestBody", inputs: [] }, + { type: "error", name: "Oracle_InvalidResponse", inputs: [] }, + { type: "error", name: "Oracle_InvalidResponseBody", inputs: [] }, + { + type: "error", + name: "Oracle_NoResolutionModule", + inputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], + }, + { + type: "error", + name: "Oracle_NotDisputeOrResolutionModule", + inputs: [{ name: "_caller", type: "address", internalType: "address" }], + }, + { + type: "error", + name: "Oracle_ResponseAlreadyDisputed", + inputs: [{ name: "_responseId", type: "bytes32", internalType: "bytes32" }], }, -] as const; + { type: "error", name: "Oracle_ResponseAlreadyProposed", inputs: [] }, +]; diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts index 80f58e4..7a31c5b 100644 --- a/packages/automated-dispute/src/interfaces/protocolProvider.ts +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -2,6 +2,7 @@ import { Caip2ChainId } from "@ebo-agent/shared"; import { Address, Block } from "viem"; import type { + AccessControl, BondEscalation, Dispute, EboEvent, @@ -13,7 +14,14 @@ import type { } from "../types/index.js"; import { ProtocolContractsNames } from "../constants.js"; +/** + * Represents the names of protocol contracts. + */ export type ProtocolContract = (typeof ProtocolContractsNames)[number]; + +/** + * Maps protocol contract names to their respective addresses. + */ export type ProtocolContractsAddresses = Record; /** @@ -83,46 +91,54 @@ export interface IWriteProvider { * * @param epoch The epoch for which the request is being created. * @param chains An array of chain identifiers where the request should be created. + * @param accessControl The access control parameters for the request. * @throws Will throw an error if the chains array is empty or if the transaction fails. * @returns A promise that resolves when the request is successfully created. */ - createRequest(epoch: bigint, chains: Caip2ChainId): Promise; + createRequest(epoch: bigint, chains: Caip2ChainId, accessControl: AccessControl): Promise; /** * Proposes a response to a request. * * @param request The request data. * @param response The response data. + * @param accessControl The access control parameters for proposing the response. * @returns A promise that resolves when the response is proposed. */ proposeResponse( request: Request["prophetData"], response: Response["prophetData"], + accessControl: AccessControl, ): Promise; /** * Disputes a proposed response. * - * @param request The request data. + * @param request The request data. * @param response The response data. * @param dispute The dispute data. + * @param accessControl The access control parameters for disputing the response. * @returns A promise that resolves when the response is disputed. */ disputeResponse( request: Request["prophetData"], response: Response["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise; + /** * Pledges support for a dispute. * * @param request The request data for the dispute. * @param dispute The dispute data. + * @param accessControl The access control parameters for pledging support. * @returns A promise that resolves when the pledge is made. */ pledgeForDispute( request: Request["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise; /** @@ -130,11 +146,13 @@ export interface IWriteProvider { * * @param request The request data for the dispute. * @param dispute The dispute data. + * @param accessControl The access control parameters for pledging against. * @returns A promise that resolves when the pledge is made. */ pledgeAgainstDispute( request: Request["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise; /** @@ -143,12 +161,14 @@ export interface IWriteProvider { * @param request The request data. * @param response The response data. * @param dispute The dispute data. + * @param accessControl The access control parameters for settling the dispute. * @returns A promise that resolves when the dispute is settled. */ settleDispute( request: Request["prophetData"], response: Response["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise; /** @@ -157,12 +177,14 @@ export interface IWriteProvider { * @param request The request data. * @param response The response data. * @param dispute The dispute data. + * @param accessControl The access control parameters for escalating the dispute. * @returns A promise that resolves when the dispute is escalated. */ escalateDispute( request: Request["prophetData"], response: Response["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise; /** @@ -170,17 +192,23 @@ export interface IWriteProvider { * * @param request The request data. * @param response The response data. + * @param accessControl The access control parameters for finalizing the request. * @returns A promise that resolves when the request is finalized. */ - finalize(request: Request["prophetData"], response: Response["prophetData"]): Promise; + finalize( + request: Request["prophetData"], + response: Response["prophetData"], + accessControl: AccessControl, + ): Promise; /** * Approves a module in the accounting extension contract. * * @param module The address of the module to approve. + * @param accessControl The access control parameters for approving the module. * @returns A promise that resolves when the module is approved. */ - approveModule(module: Address): Promise; + approveModule(module: Address, accessControl: AccessControl): Promise; } /** diff --git a/packages/automated-dispute/src/types/prophet.ts b/packages/automated-dispute/src/types/prophet.ts index bc1e4d6..ac6b583 100644 --- a/packages/automated-dispute/src/types/prophet.ts +++ b/packages/automated-dispute/src/types/prophet.ts @@ -7,6 +7,11 @@ export type DisputeId = Branded; export type RequestStatus = "Active" | "Finalized"; +export interface AccessControl { + user: Address; + data: Hex; +} + export interface Request { id: RequestId; status: RequestStatus; From 54fbd3a8bdd903de1811733838b8d8b1e0490e94 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:56:46 -0500 Subject: [PATCH 07/14] fix: pr comments --- apps/agent/src/index.ts | 1 + .../utilities/approveAccountingModules.ts | 8 +++- .../src/providers/protocolProvider.ts | 38 +++++++++--------- .../tests/mocks/eboActor.mocks.ts | 31 ++++++++------- .../tests/mocks/eboProcessor.mocks.ts | 4 +- .../tests/services/eboActor/fixtures.ts | 2 + .../tests/services/protocolProvider.spec.ts | 39 ++++++++++++++++++- 7 files changed, 88 insertions(+), 35 deletions(-) diff --git a/apps/agent/src/index.ts b/apps/agent/src/index.ts index 9124bc6..1a63c24 100644 --- a/apps/agent/src/index.ts +++ b/apps/agent/src/index.ts @@ -42,6 +42,7 @@ const main = async (): Promise => { config.protocolProvider.rpcsConfig, config.protocolProvider.contracts, config.protocolProvider.privateKey, + config.protocolProvider.accessControl?.serviceProviderAddress, logger, blockNumberService, ); diff --git a/apps/scripts/utilities/approveAccountingModules.ts b/apps/scripts/utilities/approveAccountingModules.ts index d27fda2..1f306c1 100644 --- a/apps/scripts/utilities/approveAccountingModules.ts +++ b/apps/scripts/utilities/approveAccountingModules.ts @@ -109,7 +109,13 @@ const rpcConfig = { const contracts = env.CONTRACTS_ADDRESSES; -const provider = new ProtocolProvider(rpcConfig, contracts, env.PRIVATE_KEY as Hex, mockLogger()); +const provider = new ProtocolProvider( + rpcConfig, + contracts, + env.PRIVATE_KEY as Hex, + env.SERVICE_PROVIDER_ADDRESS, + mockLogger(), +); /** * Approves the necessary modules by calling approveModule on ProtocolProvider. diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index ba1b6ba..92a9d51 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -122,28 +122,30 @@ export class ProtocolProvider implements IProtocolProvider { /** * Creates a new ProtocolProvider instance - * @param config The configuration for the serviceProviderAddress and RPC connections, including URLs, timeout, retry interval, and transaction receipt confirmations + * @param rpcConfig The configuration for the serviceProviderAddress and RPC connections, including URLs, timeout, retry interval, and transaction receipt confirmations * @param contracts The addresses of the protocol contracts that will be instantiated * @param privateKey The private key of the account that will be used to interact with the contracts + * @param serviceProviderAddress The address of the service provider * @param logger The logger instance * @param blockNumberService The service that will be used to fetch block numbers */ constructor( - private readonly config: ProtocolProviderConfig, + private readonly rpcConfig: ProtocolProviderConfig, contracts: ProtocolContractsAddresses, privateKey: Hex, + serviceProviderAddress: Address | undefined, private readonly logger: ILogger, blockNumberService?: BlockNumberService, ) { - const l1Chain = this.getViemChain(config.l1.chainId); - const l2Chain = this.getViemChain(config.l2.chainId); + const l1Chain = this.getViemChain(rpcConfig.l1.chainId); + const l2Chain = this.getViemChain(rpcConfig.l2.chainId); - this.l1ReadClient = this.createReadClient(config.l1, l1Chain); - this.l2ReadClient = this.createReadClient(config.l2, l2Chain); - this.l2WriteClient = this.createWriteClient(config.l2, l2Chain, privateKey); + this.l1ReadClient = this.createReadClient(rpcConfig.l1, l1Chain); + this.l2ReadClient = this.createReadClient(rpcConfig.l2, l2Chain); + this.l2WriteClient = this.createWriteClient(rpcConfig.l2, l2Chain, privateKey); this.blockNumberService = blockNumberService; this.serviceProviderAddress = - config.serviceProviderAddress || privateKeyToAccount(privateKey).address; + serviceProviderAddress || privateKeyToAccount(privateKey).address; // Instantiate all the protocol contracts this.oracleContract = getContract({ @@ -346,7 +348,7 @@ export class ProtocolProvider implements IProtocolProvider { const startTimestamp = epochFirstBlock.timestamp as UnixTimestamp; - const l2ChainId = this.config.l2.chainId; + const l2ChainId = this.rpcConfig.l2.chainId; const l2FirstBlockNumber = await this.blockNumberService.getEpochBlockNumber( startTimestamp, l2ChainId, @@ -822,7 +824,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -870,7 +872,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(request); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -904,7 +906,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -940,7 +942,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -975,7 +977,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1022,7 +1024,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1073,7 +1075,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1129,7 +1131,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, @@ -1169,7 +1171,7 @@ export class ProtocolProvider implements IProtocolProvider { const hash = await this.l2WriteClient.writeContract(simulatedRequest); - const { transactionReceiptConfirmations } = this.config.l2; + const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ hash, confirmations: transactionReceiptConfirmations, diff --git a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts index 262d946..5c64294 100644 --- a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts @@ -18,6 +18,7 @@ import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, DEFAULT_MOCKED_REQUEST_CREATED_DATA, mockedPrivateKey, + mockServiceProviderAddress, } from "../services/eboActor/fixtures.js"; /** @@ -50,6 +51,7 @@ export function buildEboActor(request: Request, logger: ILogger) { }, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, + mockServiceProviderAddress, logger, ); @@ -76,6 +78,20 @@ export function buildEboActor(request: Request, logger: ILogger) { [chainId, ["http://localhost:8539"]], ]); + const notificationService: NotificationService = { + send: vi.fn().mockResolvedValue(undefined), + sendOrThrow: vi.fn().mockResolvedValue(undefined), + createErrorMessage: vi + .fn() + .mockImplementation((defaultMessage: string, context?: unknown, err?: unknown) => { + return { + title: defaultMessage, + description: err instanceof Error ? err.message : undefined, + }; + }), + sendError: vi.fn().mockResolvedValue(undefined), + }; + const blockNumberService = new BlockNumberService( blockNumberRpcUrls, { @@ -88,6 +104,7 @@ export function buildEboActor(request: Request, logger: ILogger) { }, }, logger, + notificationService, ); vi.spyOn(blockNumberService, "getEpochBlockNumber").mockResolvedValue(BigInt(12345)); @@ -96,20 +113,6 @@ export function buildEboActor(request: Request, logger: ILogger) { const eventProcessingMutex = new Mutex(); - const notificationService: NotificationService = { - send: vi.fn().mockResolvedValue(undefined), - sendOrThrow: vi.fn().mockResolvedValue(undefined), - createErrorMessage: vi - .fn() - .mockImplementation((defaultMessage: string, context?: unknown, err?: unknown) => { - return { - title: defaultMessage, - description: err instanceof Error ? err.message : undefined, - }; - }), - sendError: vi.fn().mockResolvedValue(undefined), - }; - const actor = new EboActor( { id, epoch, chainId }, protocolProvider, diff --git a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts index eb2b183..47fba6c 100644 --- a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts @@ -8,6 +8,7 @@ import { AccountingModules } from "../../src/types/prophet.js"; import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, + mockServiceProviderAddress, } from "../services/eboActor/fixtures.js"; export function buildEboProcessor( @@ -16,7 +17,7 @@ export function buildEboProcessor( responseModule: "0x02", escalationModule: "0x03", }, - notifier?: NotificationService, + notifier: NotificationService, ) { const blockNumberRpcUrls = new Map([ ["eip155:1" as Caip2ChainId, ["http://localhost:8539"]], @@ -56,6 +57,7 @@ export function buildEboProcessor( }, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, + mockServiceProviderAddress, logger, blockNumberService, ); diff --git a/packages/automated-dispute/tests/services/eboActor/fixtures.ts b/packages/automated-dispute/tests/services/eboActor/fixtures.ts index 1e203a9..be3ea45 100644 --- a/packages/automated-dispute/tests/services/eboActor/fixtures.ts +++ b/packages/automated-dispute/tests/services/eboActor/fixtures.ts @@ -15,6 +15,8 @@ import { export const mockedPrivateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; +export const mockServiceProviderAddress = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address; + export const DEFAULT_MOCKED_PROTOCOL_CONTRACTS: ProtocolContractsAddresses = { oracle: "0x1234560000000000000000000000000000000000" as Address, epochManager: "0x6543210000000000000000000000000000000000" as Address, diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index d660f5b..b3e4f0d 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -37,6 +37,7 @@ import { DEFAULT_MOCKED_REQUEST_CREATED_DATA, DEFAULT_MOCKED_RESPONSE_DATA, mockedPrivateKey, + mockServiceProviderAddress, } from "./eboActor/fixtures.js"; vi.mock("viem", async () => { @@ -77,7 +78,6 @@ describe("ProtocolProvider", () => { transactionReceiptConfirmations: 1, }, }; - const mockServiceProviderAddress = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address; const mockDerivedAddress = "0xDerivedAddressFromPrivateKey" as Address; const mockRpcConfigWithServiceProvider = { @@ -267,6 +267,7 @@ describe("ProtocolProvider", () => { { ...mockRpcConfigBase, l1: { ...mockRpcConfigBase.l1, urls: [] } }, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ), @@ -280,6 +281,7 @@ describe("ProtocolProvider", () => { { ...mockRpcConfigBase, l2: { ...mockRpcConfigBase.l2, urls: [] } }, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ), @@ -307,6 +309,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -354,6 +357,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -375,6 +379,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -403,6 +408,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -420,6 +426,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -441,6 +448,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -462,6 +470,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -486,6 +495,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -509,6 +519,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -531,6 +542,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -559,6 +571,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -581,6 +594,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -619,6 +633,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -636,6 +651,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -659,6 +675,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -696,6 +713,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -709,6 +727,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -726,6 +745,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -749,6 +769,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -769,6 +790,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -795,6 +817,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -817,6 +840,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -845,6 +869,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -874,6 +899,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -896,6 +922,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -923,6 +950,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -948,6 +976,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -971,6 +1000,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1041,6 +1071,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1192,6 +1223,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1220,6 +1252,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1241,6 +1274,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1261,6 +1295,7 @@ describe("ProtocolProvider", () => { mockRpcConfigBase, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1277,6 +1312,7 @@ describe("ProtocolProvider", () => { mockRpcConfigWithServiceProvider, mockContractAddress, mockedPrivateKey, + mockServiceProviderAddress, mockLogger(), mockBlockNumberService, ); @@ -1289,6 +1325,7 @@ describe("ProtocolProvider", () => { mockRpcConfigWithoutServiceProvider, mockContractAddress, mockedPrivateKey, + undefined, mockLogger(), mockBlockNumberService, ); From 32c8008a43f0f30e623e65510ae288b9945b2fad Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:09:38 -0500 Subject: [PATCH 08/14] fix: as const --- packages/automated-dispute/src/abis/eboRequestCreator.ts | 2 +- packages/automated-dispute/src/abis/oracle.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/automated-dispute/src/abis/eboRequestCreator.ts b/packages/automated-dispute/src/abis/eboRequestCreator.ts index fd564c5..13709d1 100644 --- a/packages/automated-dispute/src/abis/eboRequestCreator.ts +++ b/packages/automated-dispute/src/abis/eboRequestCreator.ts @@ -410,4 +410,4 @@ export const eboRequestCreatorAbi = [ { type: "error", name: "EBORequestCreator_InvalidEpoch", inputs: [] }, { type: "error", name: "EBORequestCreator_InvalidNonce", inputs: [] }, { type: "error", name: "EBORequestCreator_RequestAlreadyCreated", inputs: [] }, -]; +] as const; diff --git a/packages/automated-dispute/src/abis/oracle.ts b/packages/automated-dispute/src/abis/oracle.ts index 43e2927..2029e12 100644 --- a/packages/automated-dispute/src/abis/oracle.ts +++ b/packages/automated-dispute/src/abis/oracle.ts @@ -620,4 +620,4 @@ export const oracleAbi = [ inputs: [{ name: "_responseId", type: "bytes32", internalType: "bytes32" }], }, { type: "error", name: "Oracle_ResponseAlreadyProposed", inputs: [] }, -]; +] as const; From 7c3a506187792f5f14e4cdd230c56a62e1ec05f9 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:02:01 -0500 Subject: [PATCH 09/14] feat: update rpc abis and behavior --- .../src/abis/eboRequestCreator.ts | 4 + packages/automated-dispute/src/abis/oracle.ts | 107 +++++++++++++++++- .../src/interfaces/protocolProvider.ts | 14 +-- .../src/providers/protocolProvider.ts | 69 ++++++----- .../src/services/eboActor.ts | 33 +++++- .../src/services/eboProcessor.ts | 3 +- .../automated-dispute/src/types/events.ts | 1 - .../automated-dispute/src/types/prophet.ts | 5 +- .../tests/services/eboActor.spec.ts | 4 + .../eboActor/onDisputeStatusUpdated.spec.ts | 10 +- .../eboActor/onLastBlockupdated.spec.ts | 4 + .../eboActor/onRequestCreated.spec.ts | 4 + 12 files changed, 204 insertions(+), 54 deletions(-) diff --git a/packages/automated-dispute/src/abis/eboRequestCreator.ts b/packages/automated-dispute/src/abis/eboRequestCreator.ts index 13709d1..0145453 100644 --- a/packages/automated-dispute/src/abis/eboRequestCreator.ts +++ b/packages/automated-dispute/src/abis/eboRequestCreator.ts @@ -17,6 +17,7 @@ export const eboRequestCreatorAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -96,6 +97,7 @@ export const eboRequestCreatorAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -125,6 +127,7 @@ export const eboRequestCreatorAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -329,6 +332,7 @@ export const eboRequestCreatorAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, diff --git a/packages/automated-dispute/src/abis/oracle.ts b/packages/automated-dispute/src/abis/oracle.ts index 2029e12..06d6658 100644 --- a/packages/automated-dispute/src/abis/oracle.ts +++ b/packages/automated-dispute/src/abis/oracle.ts @@ -25,6 +25,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -33,6 +34,15 @@ export const oracleAbi = [ ], }, { name: "_ipfsHash", type: "bytes32", internalType: "bytes32" }, + { + name: "_accessControl", + type: "tuple", + internalType: "struct IAccessController.AccessControl", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], stateMutability: "nonpayable", @@ -53,6 +63,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -61,6 +72,15 @@ export const oracleAbi = [ ], }, { name: "_ipfsHashes", type: "bytes32[]", internalType: "bytes32[]" }, + { + name: "_accessControl", + type: "tuple[]", + internalType: "struct IAccessController.AccessControl[]", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [{ name: "_batchRequestsIds", type: "bytes32[]", internalType: "bytes32[]" }], stateMutability: "nonpayable", @@ -95,6 +115,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -123,6 +144,15 @@ export const oracleAbi = [ { name: "requestId", type: "bytes32", internalType: "bytes32" }, ], }, + { + name: "_accessControl", + type: "tuple", + internalType: "struct IAccessController.AccessControl", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [{ name: "_disputeId", type: "bytes32", internalType: "bytes32" }], stateMutability: "nonpayable", @@ -150,6 +180,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -178,6 +209,15 @@ export const oracleAbi = [ { name: "requestId", type: "bytes32", internalType: "bytes32" }, ], }, + { + name: "_accessControl", + type: "tuple", + internalType: "struct IAccessController.AccessControl", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [], stateMutability: "nonpayable", @@ -198,6 +238,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -215,6 +256,15 @@ export const oracleAbi = [ { name: "response", type: "bytes", internalType: "bytes" }, ], }, + { + name: "_accessControl", + type: "tuple", + internalType: "struct IAccessController.AccessControl", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [], stateMutability: "nonpayable", @@ -240,6 +290,16 @@ export const oracleAbi = [ outputs: [{ name: "_ids", type: "bytes32[]", internalType: "bytes32[]" }], stateMutability: "view", }, + { + type: "function", + name: "isAccessModuleApproved", + inputs: [ + { name: "_user", type: "address", internalType: "address" }, + { name: "_accessModule", type: "address", internalType: "address" }, + ], + outputs: [{ name: "_approved", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, { type: "function", name: "isParticipant", @@ -283,6 +343,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -300,6 +361,15 @@ export const oracleAbi = [ { name: "response", type: "bytes", internalType: "bytes" }, ], }, + { + name: "_accessControl", + type: "tuple", + internalType: "struct IAccessController.AccessControl", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [{ name: "_responseId", type: "bytes32", internalType: "bytes32" }], stateMutability: "nonpayable", @@ -327,6 +397,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -355,6 +426,15 @@ export const oracleAbi = [ { name: "requestId", type: "bytes32", internalType: "bytes32" }, ], }, + { + name: "_accessControl", + type: "tuple", + internalType: "struct IAccessController.AccessControl", + components: [ + { name: "user", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + }, ], outputs: [], stateMutability: "nonpayable", @@ -366,6 +446,16 @@ export const oracleAbi = [ outputs: [{ name: "_responseCreatedAt", type: "uint256", internalType: "uint256" }], stateMutability: "view", }, + { + type: "function", + name: "setAccessModule", + inputs: [ + { name: "_accessModule", type: "address", internalType: "address" }, + { name: "_approved", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, { type: "function", name: "totalRequestCount", @@ -389,6 +479,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -422,6 +513,16 @@ export const oracleAbi = [ outputs: [], stateMutability: "nonpayable", }, + { + type: "event", + name: "AccessModuleSet", + inputs: [ + { name: "_user", type: "address", indexed: true, internalType: "address" }, + { name: "_accessModule", type: "address", indexed: true, internalType: "address" }, + { name: "_approved", type: "bool", indexed: false, internalType: "bool" }, + ], + anonymous: false, + }, { type: "event", name: "DisputeEscalated", @@ -460,7 +561,6 @@ export const oracleAbi = [ { name: "requestId", type: "bytes32", internalType: "bytes32" }, ], }, - { name: "_caller", type: "address", indexed: true, internalType: "address" }, ], anonymous: false, }, @@ -496,7 +596,6 @@ export const oracleAbi = [ inputs: [ { name: "_requestId", type: "bytes32", indexed: true, internalType: "bytes32" }, { name: "_responseId", type: "bytes32", indexed: true, internalType: "bytes32" }, - { name: "_caller", type: "address", indexed: true, internalType: "address" }, ], anonymous: false, }, @@ -518,6 +617,7 @@ export const oracleAbi = [ { name: "disputeModule", type: "address", internalType: "address" }, { name: "resolutionModule", type: "address", internalType: "address" }, { name: "finalityModule", type: "address", internalType: "address" }, + { name: "accessModule", type: "address", internalType: "address" }, { name: "requestModuleData", type: "bytes", internalType: "bytes" }, { name: "responseModuleData", type: "bytes", internalType: "bytes" }, { name: "disputeModuleData", type: "bytes", internalType: "bytes" }, @@ -570,6 +670,9 @@ export const oracleAbi = [ ], anonymous: false, }, + { type: "error", name: "AccessController_NoAccess", inputs: [] }, + { type: "error", name: "OracleAccessController_AccessModuleAlreadySet", inputs: [] }, + { type: "error", name: "OracleAccessController_AccessModuleNotApproved", inputs: [] }, { type: "error", name: "Oracle_AlreadyFinalized", diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts index 7a31c5b..496e4be 100644 --- a/packages/automated-dispute/src/interfaces/protocolProvider.ts +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -90,12 +90,11 @@ export interface IWriteProvider { * Creates a request on the EBO Request Creator contract. * * @param epoch The epoch for which the request is being created. - * @param chains An array of chain identifiers where the request should be created. - * @param accessControl The access control parameters for the request. + * @param chain A chain identifier where the request should be created. * @throws Will throw an error if the chains array is empty or if the transaction fails. * @returns A promise that resolves when the request is successfully created. */ - createRequest(epoch: bigint, chains: Caip2ChainId, accessControl: AccessControl): Promise; + createRequest(epoch: bigint, chain: Caip2ChainId): Promise; /** * Proposes a response to a request. @@ -132,13 +131,11 @@ export interface IWriteProvider { * * @param request The request data for the dispute. * @param dispute The dispute data. - * @param accessControl The access control parameters for pledging support. * @returns A promise that resolves when the pledge is made. */ pledgeForDispute( request: Request["prophetData"], dispute: Dispute["prophetData"], - accessControl: AccessControl, ): Promise; /** @@ -146,13 +143,11 @@ export interface IWriteProvider { * * @param request The request data for the dispute. * @param dispute The dispute data. - * @param accessControl The access control parameters for pledging against. * @returns A promise that resolves when the pledge is made. */ pledgeAgainstDispute( request: Request["prophetData"], dispute: Dispute["prophetData"], - accessControl: AccessControl, ): Promise; /** @@ -161,14 +156,12 @@ export interface IWriteProvider { * @param request The request data. * @param response The response data. * @param dispute The dispute data. - * @param accessControl The access control parameters for settling the dispute. * @returns A promise that resolves when the dispute is settled. */ settleDispute( request: Request["prophetData"], response: Response["prophetData"], dispute: Dispute["prophetData"], - accessControl: AccessControl, ): Promise; /** @@ -205,10 +198,9 @@ export interface IWriteProvider { * Approves a module in the accounting extension contract. * * @param module The address of the module to approve. - * @param accessControl The access control parameters for approving the module. * @returns A promise that resolves when the module is approved. */ - approveModule(module: Address, accessControl: AccessControl): Promise; + approveModule(module: Address): Promise; } /** diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 92a9d51..466364f 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -59,6 +59,7 @@ import { ProtocolContractsAddresses, } from "../interfaces/index.js"; import { ProphetCodec } from "../services/index.js"; +import { AccessControl } from "../types/index.js"; type RpcConfig = { chainId: Caip2ChainId; @@ -147,18 +148,18 @@ export class ProtocolProvider implements IProtocolProvider { this.serviceProviderAddress = serviceProviderAddress || privateKeyToAccount(privateKey).address; - // Instantiate all the protocol contracts + // Instantiate all the protocol contracts with updated types this.oracleContract = getContract({ address: contracts.oracle, abi: oracleAbi, client: this.l2WriteClient, - }); + }) as GetContractReturnType; this.epochManagerContract = getContract({ address: contracts.epochManager, abi: epochManagerAbi, client: this.l2ReadClient, - }); + }) as GetContractReturnType; this.eboRequestCreatorContract = getContract({ address: contracts.eboRequestCreator, @@ -167,7 +168,11 @@ export class ProtocolProvider implements IProtocolProvider { public: this.l2ReadClient, wallet: this.l2WriteClient, }, - }); + }) as GetContractReturnType< + typeof eboRequestCreatorAbi, + typeof this.l2WriteClient, + Address + >; this.bondEscalationContract = getContract({ address: contracts.bondEscalationModule, @@ -176,7 +181,11 @@ export class ProtocolProvider implements IProtocolProvider { public: this.l2ReadClient, wallet: this.l2WriteClient, }, - }); + }) as GetContractReturnType< + typeof bondEscalationModuleAbi, + typeof this.l2WriteClient, + Address + >; this.horizonAccountingExtensionContract = getContract({ address: contracts.horizonAccountingExtension, @@ -185,7 +194,11 @@ export class ProtocolProvider implements IProtocolProvider { public: this.l2ReadClient, wallet: this.l2WriteClient, }, - }); + }) as GetContractReturnType< + typeof horizonAccountingExtensionAbi, + typeof this.l2WriteClient, + Address + >; } public write: IWriteProvider = { @@ -255,16 +268,10 @@ export class ProtocolProvider implements IProtocolProvider { /** * Creates an HTTP transport with logging capabilities for requests and responses. * - * This function sets up a custom HTTP transport that logs the details of each - * HTTP request and response, including the URL, method, headers, and body. - * The logging captures and logs the cloned `Request` and `Response` objects, - * ensuring the original objects remain unaltered. - * * @param {string} url - The base URL for the HTTP transport. * @param {number} timeout - The timeout duration in milliseconds for each HTTP request. * @param {number} retryInterval - The delay in milliseconds before retrying a failed request. * @returns {HttpTransport} The configured HTTP transport with logging enabled. - * */ private createHttpTransportWithLogging( url: string, @@ -369,10 +376,6 @@ export class ProtocolProvider implements IProtocolProvider { /** * Fetches the timestamp for a given event by using its block hash. * - * Note: We use block hash instead of block number due to an Anvil bug where events are generated with L1 block numbers - * instead of L2 block numbers when forking Arbitrum. By using the block hash, we can get the correct timestamp - * even if the block number is incorrect. - * * @param event - The event for which to fetch the timestamp. * @returns The timestamp of the block in which the event was included. */ @@ -617,7 +620,7 @@ export class ProtocolProvider implements IProtocolProvider { const eventsWithTimestamps = await Promise.all( events.map(async (event) => { - const { _requestId, _responseId, _caller } = event.args; + const { _requestId, _responseId } = event.args; const timestamp = await this.getEventTimestamp(event); @@ -632,7 +635,6 @@ export class ProtocolProvider implements IProtocolProvider { metadata: { requestId: HexUtils.normalize(_requestId) as RequestId, responseId: HexUtils.normalize(_responseId) as ResponseId, - caller: _caller as Address, blockNumber: event.blockNumber, }, } as EboEvent<"OracleRequestFinalized">; @@ -649,7 +651,7 @@ export class ProtocolProvider implements IProtocolProvider { * @param {bigint} toBlock - The ending block number to fetch events up to. * @returns {Promise[]>} A promise that resolves to an array of Oracle events. */ - async getOracleEvents(fromBlock: bigint, toBlock: bigint) { + async getOracleEvents(fromBlock: bigint, toBlock: bigint): Promise[]> { const [ requestCreatedEvents, responseProposedEvents, @@ -744,7 +746,7 @@ export class ProtocolProvider implements IProtocolProvider { * @returns A promise that resolves to an array of EboEvents sorted by block number and log index. * @throws {Error} If the block range is invalid or if there's an error fetching events. */ - async getEvents(fromBlock: bigint, toBlock: bigint) { + async getEvents(fromBlock: bigint, toBlock: bigint): Promise[]> { if (fromBlock > toBlock) { throw new InvalidBlockRangeError(fromBlock, toBlock); } @@ -761,7 +763,7 @@ export class ProtocolProvider implements IProtocolProvider { * @param {EboEvent[][]} streams - A collection of EboEvent[] arrays. * @returns {EboEvent[]} The merged and sorted event array. */ - private mergeEventStreams(...streams: EboEvent[][]) { + private mergeEventStreams(...streams: EboEvent[][]): EboEvent[] { return streams .reduce((acc, curr) => acc.concat(curr), []) .sort((a, b) => { @@ -838,10 +840,10 @@ export class ProtocolProvider implements IProtocolProvider { /** * Gets the list of approved modules' addresses for a given user. * - * @param {Address} user - The address of the user. If not specified, it fallbacks to the L2 account address. + * @param {Address} user - The address of the user. * @returns {Promise} A promise that resolves with an array of approved modules for the user. */ - async getApprovedModules(user?: Address): Promise { + async getApprovedModules(user: Address): Promise { const bondAddress = user ?? this.getAccountAddress(); const modules = await this.horizonAccountingExtensionContract.read.approvedModules([ @@ -862,7 +864,7 @@ 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.l2ReadClient.simulateContract({ + const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.eboRequestCreatorContract.address, abi: eboRequestCreatorAbi, functionName: "createRequest", @@ -870,7 +872,7 @@ export class ProtocolProvider implements IProtocolProvider { account: this.l2WriteClient.account, }); - const hash = await this.l2WriteClient.writeContract(request); + const hash = await this.l2WriteClient.writeContract(simulatedRequest); const { transactionReceiptConfirmations } = this.rpcConfig.l2; const receipt = await this.l2ReadClient.waitForTransactionReceipt({ @@ -888,6 +890,7 @@ export class ProtocolProvider implements IProtocolProvider { * * @param {Request["prophetData"]} request - The request data. * @param {Response["prophetData"]} response - The response data to propose. + * @param {AccessControl} accessControl - The access control parameters for proposing the response. * @throws {TransactionExecutionError} Throws if the transaction fails during execution. * @throws {ContractFunctionRevertedError} Throws if the contract function reverts. * @returns {Promise} @@ -895,12 +898,13 @@ export class ProtocolProvider implements IProtocolProvider { async proposeResponse( request: Request["prophetData"], response: Response["prophetData"], + accessControl: AccessControl, ): Promise { const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "proposeResponse", - args: [request, response], + args: [request, response, accessControl], account: this.l2WriteClient.account, }); @@ -923,6 +927,7 @@ export class ProtocolProvider implements IProtocolProvider { * @param {Request["prophetData"]} request - The request data. * @param {Response["prophetData"]} response - The response data to dispute. * @param {Dispute["prophetData"]} dispute - The dispute data. + * @param {AccessControl} accessControl - The access control parameters for disputing the response. * @throws {TransactionExecutionError} Throws if the transaction fails during execution. * @throws {ContractFunctionRevertedError} Throws if the contract function reverts. * @returns {Promise} @@ -931,12 +936,13 @@ export class ProtocolProvider implements IProtocolProvider { request: Request["prophetData"], response: Response["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise { const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "disputeResponse", - args: [request, response, dispute], + args: [request, response, dispute, accessControl], account: this.l2WriteClient.account, }); @@ -1112,6 +1118,7 @@ export class ProtocolProvider implements IProtocolProvider { * @param {Request["prophetData"]} request - The request data. * @param {Response["prophetData"]} response - The response data. * @param {Dispute["prophetData"]} dispute - The dispute data. + * @param {AccessControl} accessControl - The access control parameters for escalating the dispute. * @throws {TransactionExecutionError} Throws if the transaction fails during execution. * @throws {ContractFunctionRevertedError} Throws if the contract function reverts. * @returns {Promise} @@ -1120,12 +1127,13 @@ export class ProtocolProvider implements IProtocolProvider { request: Request["prophetData"], response: Response["prophetData"], dispute: Dispute["prophetData"], + accessControl: AccessControl, ): Promise { const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "escalateDispute", - args: [request, response, dispute], + args: [request, response, dispute, accessControl], account: this.l2WriteClient.account, }); @@ -1152,20 +1160,21 @@ export class ProtocolProvider implements IProtocolProvider { * * @param {Request["prophetData"]} request - The request data. * @param {Response["prophetData"]} response - The response data to finalize. + * @param {AccessControl} accessControl - The access control parameters for finalizing the request. * @throws {TransactionExecutionError} Throws if the transaction fails during execution. * @throws {ContractFunctionRevertedError} Throws if the contract function reverts. * @returns {Promise} */ - async finalize( request: Request["prophetData"], response: Response["prophetData"], + accessControl: AccessControl, ): Promise { const { request: simulatedRequest } = await this.l2ReadClient.simulateContract({ address: this.oracleContract.address, abi: oracleAbi, functionName: "finalize", - args: [request, response], + args: [request, response, accessControl], account: this.l2WriteClient.account, }); diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index bd4da40..43e4f98 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -48,7 +48,7 @@ import { ProphetCodec, UpdateDisputeStatus, } from "../services/index.js"; -import { ActorRequest } from "../types/index.js"; +import { AccessControl, ActorRequest } from "../types/index.js"; /** * Compare function to sort events chronologically in ascending order by block number @@ -112,6 +112,17 @@ export class EboActor { this.errorHandler = new ErrorHandler(this.notificationService, this.logger); } + /** + * Get the default access control object. + * @returns AccessControl object + */ + private getDefaultAccessControl(): AccessControl { + return { + user: this.protocolProvider.getAccountAddress(), + data: "0x", + }; + } + /** * Enqueue events to be processed by the actor. * @@ -362,7 +373,11 @@ export class EboActor { }; try { - await this.protocolProvider.finalize(request.prophetData, nullResponseProphetData); + await this.protocolProvider.finalize( + request.prophetData, + nullResponseProphetData, + this.getDefaultAccessControl(), + ); this.logger.info(`Request ${request.id} finalized with no response.`, { requestId: request.id, }); @@ -505,7 +520,11 @@ export class EboActor { }); try { - await this.protocolProvider.finalize(request.prophetData, response.prophetData); + await this.protocolProvider.finalize( + request.prophetData, + response.prophetData, + this.getDefaultAccessControl(), + ); } catch (err) { if (err instanceof CustomContractError) { this.logger.warn(`Finalizing request reverted: ${err.name}`, { @@ -584,6 +603,7 @@ export class EboActor { request.prophetData, response.prophetData, dispute.prophetData, + this.getDefaultAccessControl(), ); this.logger.info(`Dispute ${dispute.id} escalated.`, { disputeId: dispute.id }); @@ -810,7 +830,11 @@ export class EboActor { }; try { - await this.protocolProvider.proposeResponse(request.prophetData, response); + await this.protocolProvider.proposeResponse( + request.prophetData, + response, + this.getDefaultAccessControl(), + ); this.logger.info(`Block ${responseBody.block} proposed`, { blockNumber: responseBody.block, @@ -886,6 +910,7 @@ export class EboActor { request.prophetData, proposedResponse.prophetData, dispute, + this.getDefaultAccessControl(), ); } catch (err) { if (err instanceof ContractFunctionRevertedError) { diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 966aac6..0fe3160 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -84,8 +84,9 @@ export class EboProcessor { * @throws {PendingModulesApproval} when there is at least one module pending approval */ private async checkAllModulesApproved() { + const userAddress: Address = this.protocolProvider.getAccountAddress(); const approvedModules: readonly Address[] = - await this.protocolProvider.getApprovedModules(); + await this.protocolProvider.getApprovedModules(userAddress); const summary: Record<"approved" | "notApproved", Partial> = { approved: {}, diff --git a/packages/automated-dispute/src/types/events.ts b/packages/automated-dispute/src/types/events.ts index b1327e8..dda6964 100644 --- a/packages/automated-dispute/src/types/events.ts +++ b/packages/automated-dispute/src/types/events.ts @@ -44,7 +44,6 @@ export interface DisputeEscalated { export interface OracleRequestFinalized { requestId: RequestId; responseId: ResponseId; - caller: Address; } export type EboEventData = E extends "RequestCreated" diff --git a/packages/automated-dispute/src/types/prophet.ts b/packages/automated-dispute/src/types/prophet.ts index ac6b583..54578cb 100644 --- a/packages/automated-dispute/src/types/prophet.ts +++ b/packages/automated-dispute/src/types/prophet.ts @@ -7,10 +7,10 @@ export type DisputeId = Branded; export type RequestStatus = "Active" | "Finalized"; -export interface AccessControl { +export type AccessControl = { user: Address; data: Hex; -} +}; export interface Request { id: RequestId; @@ -63,6 +63,7 @@ export interface Request { disputeModuleData: Hex; resolutionModuleData: Hex; finalityModuleData: Hex; + accessModule: Hex; }>; } diff --git a/packages/automated-dispute/tests/services/eboActor.spec.ts b/packages/automated-dispute/tests/services/eboActor.spec.ts index 91b4b8e..0f873fb 100644 --- a/packages/automated-dispute/tests/services/eboActor.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor.spec.ts @@ -540,6 +540,10 @@ describe("EboActor", () => { request.prophetData, response.prophetData, dispute.prophetData, + expect.objectContaining({ + user: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + data: "0x", + }), ); expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} escalated.`, { diff --git a/packages/automated-dispute/tests/services/eboActor/onDisputeStatusUpdated.spec.ts b/packages/automated-dispute/tests/services/eboActor/onDisputeStatusUpdated.spec.ts index 5084629..0c18b66 100644 --- a/packages/automated-dispute/tests/services/eboActor/onDisputeStatusUpdated.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor/onDisputeStatusUpdated.spec.ts @@ -1,10 +1,10 @@ import { ILogger, UnixTimestamp } from "@ebo-agent/shared"; import { describe, expect, it, vi } from "vitest"; -import { ProphetCodec } from "../../../src/services/prophetCodec.ts"; +import { ProphetCodec } from "../../../src/services/prophetCodec.js"; import { DisputeId, EboEvent } from "../../../src/types/index.js"; -import mocks from "../../mocks/index.ts"; -import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./fixtures.ts"; +import mocks from "../../mocks/index.js"; +import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./fixtures.js"; const logger: ILogger = { info: vi.fn(), @@ -103,6 +103,10 @@ describe("onDisputeStatusUpdated", () => { block: 2n, }), }), + expect.objectContaining({ + user: proposerAddress, + data: "0x", + }), ); }); diff --git a/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts b/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts index 20af9fc..206cad7 100644 --- a/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts @@ -249,6 +249,10 @@ describe("EboActor", () => { expect(mockFinalize).toHaveBeenCalledWith( request.prophetData, firstResponse.prophetData, + expect.objectContaining({ + user: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + data: "0x", + }), ); }); diff --git a/packages/automated-dispute/tests/services/eboActor/onRequestCreated.spec.ts b/packages/automated-dispute/tests/services/eboActor/onRequestCreated.spec.ts index 058af38..0232546 100644 --- a/packages/automated-dispute/tests/services/eboActor/onRequestCreated.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor/onRequestCreated.spec.ts @@ -91,6 +91,10 @@ describe("EboActor", () => { block: indexedEpochBlockNumber, }), }), + expect.objectContaining({ + user: proposerAddress, + data: "0x", + }), ); }); From 180ef4d97d526df3adc21954b0edb8eb38391b14 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:07:59 -0500 Subject: [PATCH 10/14] chore: cleanup --- .../src/providers/protocolProvider.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 466364f..252ca74 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -148,7 +148,7 @@ export class ProtocolProvider implements IProtocolProvider { this.serviceProviderAddress = serviceProviderAddress || privateKeyToAccount(privateKey).address; - // Instantiate all the protocol contracts with updated types + // Instantiate all the protocol contracts this.oracleContract = getContract({ address: contracts.oracle, abi: oracleAbi, @@ -268,6 +268,12 @@ export class ProtocolProvider implements IProtocolProvider { /** * Creates an HTTP transport with logging capabilities for requests and responses. * + * This function sets up a custom HTTP transport that logs the details of each + * HTTP request and response, including the URL, method, headers, and body. + * The logging captures and logs the cloned `Request` and `Response` objects, + * ensuring the original objects remain unaltered. + * + * * @param {string} url - The base URL for the HTTP transport. * @param {number} timeout - The timeout duration in milliseconds for each HTTP request. * @param {number} retryInterval - The delay in milliseconds before retrying a failed request. @@ -376,6 +382,10 @@ export class ProtocolProvider implements IProtocolProvider { /** * Fetches the timestamp for a given event by using its block hash. * + * Note: We use block hash instead of block number due to an Anvil bug where events are generated with L1 block numbers + * instead of L2 block numbers when forking Arbitrum. By using the block hash, we can get the correct timestamp + * even if the block number is incorrect. + * * @param event - The event for which to fetch the timestamp. * @returns The timestamp of the block in which the event was included. */ @@ -840,7 +850,7 @@ export class ProtocolProvider implements IProtocolProvider { /** * Gets the list of approved modules' addresses for a given user. * - * @param {Address} user - The address of the user. + * @param {Address} user - The address of the user. If not specified, it fallbacks to the L2 account address. * @returns {Promise} A promise that resolves with an array of approved modules for the user. */ async getApprovedModules(user: Address): Promise { From 46d0522ca548e2423e84a5e300654cebe2f6c3b0 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:53:27 -0500 Subject: [PATCH 11/14] feat: validate horizon auth --- .../src/abis/horizonStaking.ts | 93 +++++++++++++++++++ packages/automated-dispute/src/abis/index.ts | 1 + packages/automated-dispute/src/constants.ts | 1 + .../src/interfaces/protocolProvider.ts | 9 ++ .../src/providers/protocolProvider.ts | 35 +++++++ .../src/services/eboProcessor.ts | 16 ++++ 6 files changed, 155 insertions(+) create mode 100644 packages/automated-dispute/src/abis/horizonStaking.ts diff --git a/packages/automated-dispute/src/abis/horizonStaking.ts b/packages/automated-dispute/src/abis/horizonStaking.ts new file mode 100644 index 0000000..7ac1c75 --- /dev/null +++ b/packages/automated-dispute/src/abis/horizonStaking.ts @@ -0,0 +1,93 @@ +export const horizonStakingAbi = [ + { + type: "function", + name: "addToProvision", + inputs: [ + { name: "serviceProvider", type: "address", internalType: "address" }, + { name: "verifier", type: "address", internalType: "address" }, + { name: "tokens", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getProvision", + inputs: [ + { name: "serviceProvider", type: "address", internalType: "address" }, + { name: "verifier", type: "address", internalType: "address" }, + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct IHorizonStaking.Provision", + components: [ + { name: "tokens", type: "uint256", internalType: "uint256" }, + { name: "tokensThawing", type: "uint256", internalType: "uint256" }, + { name: "sharesThawing", type: "uint256", internalType: "uint256" }, + { name: "maxVerifierCut", type: "uint32", internalType: "uint32" }, + { name: "thawingPeriod", type: "uint64", internalType: "uint64" }, + { name: "createdAt", type: "uint64", internalType: "uint64" }, + { name: "maxVerifierCutPending", type: "uint32", internalType: "uint32" }, + { name: "thawingPeriodPending", type: "uint64", internalType: "uint64" }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isAuthorized", + inputs: [ + { name: "serviceProvider", type: "address", internalType: "address" }, + { name: "verifier", type: "address", internalType: "address" }, + { name: "operator", type: "address", internalType: "address" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "provision", + inputs: [ + { name: "serviceProvider", type: "address", internalType: "address" }, + { name: "verifier", type: "address", internalType: "address" }, + { name: "tokens", type: "uint256", internalType: "uint256" }, + { name: "maxVerifierCut", type: "uint32", internalType: "uint32" }, + { name: "thawingPeriod", type: "uint64", internalType: "uint64" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "slash", + inputs: [ + { name: "serviceProvider", type: "address", internalType: "address" }, + { name: "tokens", type: "uint256", internalType: "uint256" }, + { name: "tokensVerifier", type: "uint256", internalType: "uint256" }, + { name: "verifierDestination", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "stake", + inputs: [{ name: "tokens", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "thaw", + inputs: [ + { name: "serviceProvider", type: "address", internalType: "address" }, + { name: "verifier", type: "address", internalType: "address" }, + { name: "tokens", type: "uint256", internalType: "uint256" }, + ], + outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], + stateMutability: "nonpayable", + }, +]; diff --git a/packages/automated-dispute/src/abis/index.ts b/packages/automated-dispute/src/abis/index.ts index f5c80b6..37c7761 100644 --- a/packages/automated-dispute/src/abis/index.ts +++ b/packages/automated-dispute/src/abis/index.ts @@ -3,3 +3,4 @@ export * from "./epochManager.js"; export * from "./eboRequestCreator.js"; export * from "./bondEscalationModule.js"; export * from "./horizonAccountingExtension.js"; +export * from "./horizonStaking.js"; diff --git a/packages/automated-dispute/src/constants.ts b/packages/automated-dispute/src/constants.ts index e2290c5..7e90005 100644 --- a/packages/automated-dispute/src/constants.ts +++ b/packages/automated-dispute/src/constants.ts @@ -4,4 +4,5 @@ export const ProtocolContractsNames = [ "eboRequestCreator", "bondEscalationModule", "horizonAccountingExtension", + "horizonStaking", ] as const; diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts index 496e4be..a0f5b2b 100644 --- a/packages/automated-dispute/src/interfaces/protocolProvider.ts +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -80,6 +80,15 @@ export interface IReadProvider { * @returns A Promise that resolves to the BondEscalation data. */ getEscalation(requestId: RequestId): Promise; + + /** + * @notice Check if an operator is authorized for the caller on a specific verifier/data service. + * @param serviceProvider The service provider on behalf of whom they're claiming to act + * @param verifier The verifier/data service on which they're claiming to act + * @param operator The address to check for auth + * @return Whether the operator is authorized or not + */ + isAuthorized(serviceProvider: Address, verifier: Address, operator: Address): Promise; } /** diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 252ca74..a70b0b5 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -41,6 +41,7 @@ import { eboRequestCreatorAbi, epochManagerAbi, horizonAccountingExtensionAbi, + horizonStakingAbi, oracleAbi, } from "../abis/index.js"; import { @@ -121,6 +122,12 @@ export class ProtocolProvider implements IProtocolProvider { Address >; + private horizonStakingContract: GetContractReturnType< + typeof horizonStakingAbi, + typeof this.l2ReadClient, + Address + >; + /** * Creates a new ProtocolProvider instance * @param rpcConfig The configuration for the serviceProviderAddress and RPC connections, including URLs, timeout, retry interval, and transaction receipt confirmations @@ -199,6 +206,12 @@ export class ProtocolProvider implements IProtocolProvider { typeof this.l2WriteClient, Address >; + + this.horizonStakingContract = getContract({ + address: contracts.horizonStaking, + abi: horizonStakingAbi, + client: this.l2ReadClient, + }) as GetContractReturnType; } public write: IWriteProvider = { @@ -221,6 +234,7 @@ export class ProtocolProvider implements IProtocolProvider { getAccountingModuleAddress: this.getAccountingModuleAddress.bind(this), getApprovedModules: this.getApprovedModules.bind(this), getEscalation: this.getEscalation.bind(this), + isAuthorized: this.isAuthorized.bind(this), }; /** @@ -1219,4 +1233,25 @@ export class ProtocolProvider implements IProtocolProvider { return bondEscalation; } + + /** + * Checks if an operator is authorized for a given service provider and verifier. + * + * @param serviceProvider - The service provider address. + * @param verifier - The verifier address. + * @param operator - The operator address. + * @returns A promise that resolves to a boolean indicating authorization status. + */ + async isAuthorized( + serviceProvider: Address, + verifier: Address, + operator: Address, + ): Promise { + const authorized = (await this.horizonStakingContract.read.isAuthorized( + serviceProvider, + verifier, + operator, + )) as boolean; + return authorized; + } } diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 0fe3160..4c980f2 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -61,6 +61,22 @@ export class EboProcessor { await this.checkAllModulesApproved(); + const serviceProvider = this.protocolProvider.getServiceProviderAddress(); + const verifier = this.protocolProvider.getAccountingModuleAddress(); + const operator = this.protocolProvider.getAccountAddress(); + + const isAuth = await this.protocolProvider.isAuthorized( + serviceProvider, + verifier, + operator, + ); + + if (!isAuth) { + const errorMessage = `Authorization required: Operator ${operator} is not authorized for service provider ${serviceProvider}.`; + this.logger.error(errorMessage); + process.exit(1); + } + await this.sync(); // Bootstrapping this.eventsInterval = setInterval(async () => { From 0d48ffef87a06d8377154d02e7c73094a76e5a0c Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:21:43 -0500 Subject: [PATCH 12/14] fix: tests and refactoring --- apps/agent/config.example.yml | 1 + apps/agent/config.tenderly.yml | 1 + apps/agent/src/config/schemas.ts | 1 + apps/scripts/.env.example | 2 +- apps/scripts/test/setup.ts | 1 + .../utilities/approveAccountingModules.ts | 1 + .../src/abis/horizonStaking.ts | 2 +- .../src/providers/protocolProvider.ts | 4 +- .../tests/services/protocolProvider.spec.ts | 72 +++++++++++++++++++ 9 files changed, 81 insertions(+), 4 deletions(-) diff --git a/apps/agent/config.example.yml b/apps/agent/config.example.yml index e61103b..a52c357 100644 --- a/apps/agent/config.example.yml +++ b/apps/agent/config.example.yml @@ -16,6 +16,7 @@ protocolProvider: eboRequestCreator: "0x1234567890123456789012345678901234567890" # EBO Request Creator contract bondEscalationModule: "0x1234567890123456789012345678901234567890" # Bond Escalation Module contract horizonAccountingExtension: "0x1234567890123456789012345678901234567890" # Accounting extension contract + horizonStaking: "0x1234567890123456789012345678901234567890" # Horizon Staking contract accessControl: serviceProviderAddress: "0x1234567890123456789012345678901234567890" # Service Provider Address diff --git a/apps/agent/config.tenderly.yml b/apps/agent/config.tenderly.yml index d8a082f..849bd0a 100644 --- a/apps/agent/config.tenderly.yml +++ b/apps/agent/config.tenderly.yml @@ -16,6 +16,7 @@ protocolProvider: eboRequestCreator: "0xa13318684281a820304C164427396385C306d870" bondEscalationModule: "0x52d7728fE87826FfF51b21b303e2FF7cB04F6Aec" horizonAccountingExtension: "0xbDAB27D1903da4e18B0D1BE873E18924514E52eC" + horizonStaking: "0x1234567890123456789012345678901234567890" # TODO: Update address blockNumberService: blockmetaConfig: diff --git a/apps/agent/src/config/schemas.ts b/apps/agent/src/config/schemas.ts index dc83d15..1360bb7 100644 --- a/apps/agent/src/config/schemas.ts +++ b/apps/agent/src/config/schemas.ts @@ -64,6 +64,7 @@ const protocolProviderConfigSchema = z.object({ eboRequestCreator: addressSchema, bondEscalationModule: addressSchema, horizonAccountingExtension: addressSchema, + horizonStaking: addressSchema, }), accessControl: accessControlSchema.optional(), }); diff --git a/apps/scripts/.env.example b/apps/scripts/.env.example index aa918b0..ee38ee1 100644 --- a/apps/scripts/.env.example +++ b/apps/scripts/.env.example @@ -4,6 +4,6 @@ RPC_URLS_L2=["https://l2.rpc.url"] TRANSACTION_RECEIPT_CONFIRMATIONS=1 TIMEOUT=30000 RETRY_INTERVAL=1000 -CONTRACTS_ADDRESSES={"l1ChainId":"eip155:1","l2ChainId":"eip155:42161","oracle":"0x...","epochManager":"0x...","bondEscalationModule":"0x...","horizonAccountingExtension":"0x..."} +CONTRACTS_ADDRESSES={"l1ChainId":"eip155:1","l2ChainId":"eip155:42161","oracle":"0x...","epochManager":"0x...","bondEscalationModule":"0x...","horizonAccountingExtension":"0x...","horizonStaking":"0x..."} BONDED_RESPONSE_MODULE_ADDRESS=0x... BOND_ESCALATION_MODULE_ADDRESS=0x... \ No newline at end of file diff --git a/apps/scripts/test/setup.ts b/apps/scripts/test/setup.ts index a9a243c..0996b35 100644 --- a/apps/scripts/test/setup.ts +++ b/apps/scripts/test/setup.ts @@ -16,6 +16,7 @@ vi.stubEnv( epochManager: "0x0000000000000000000000000000000000000002", bondEscalationModule: "0x0000000000000000000000000000000000000003", horizonAccountingExtension: "0x0000000000000000000000000000000000000004", + horizonStaking: "0x0000000000000000000000000000000000000005", }), ); vi.stubEnv("BONDED_RESPONSE_MODULE_ADDRESS", "0xBondedResponseModule"); diff --git a/apps/scripts/utilities/approveAccountingModules.ts b/apps/scripts/utilities/approveAccountingModules.ts index 1f306c1..43d5ea8 100644 --- a/apps/scripts/utilities/approveAccountingModules.ts +++ b/apps/scripts/utilities/approveAccountingModules.ts @@ -42,6 +42,7 @@ const contractsAddressesSchema = z.object({ horizonAccountingExtension: hexSchema .optional() .default("0x0000000000000000000000000000000000000000"), + horizonStaking: hexSchema, // Default to a zero address because it's not required for the script but required for ProtocolProvider eboRequestCreator: hexSchema.optional().default("0x0000000000000000000000000000000000000000"), }); diff --git a/packages/automated-dispute/src/abis/horizonStaking.ts b/packages/automated-dispute/src/abis/horizonStaking.ts index 7ac1c75..47e3b03 100644 --- a/packages/automated-dispute/src/abis/horizonStaking.ts +++ b/packages/automated-dispute/src/abis/horizonStaking.ts @@ -90,4 +90,4 @@ export const horizonStakingAbi = [ outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], stateMutability: "nonpayable", }, -]; +] as const; diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index a70b0b5..d1d2e34 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -1247,11 +1247,11 @@ export class ProtocolProvider implements IProtocolProvider { verifier: Address, operator: Address, ): Promise { - const authorized = (await this.horizonStakingContract.read.isAuthorized( + const authorized = await this.horizonStakingContract.read.isAuthorized([ serviceProvider, verifier, operator, - )) as boolean; + ]); return authorized; } } diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index b3e4f0d..c5412e8 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -20,6 +20,7 @@ import { eboRequestCreatorAbi, epochManagerAbi, horizonAccountingExtensionAbi, + horizonStakingAbi, oracleAbi, } from "../../src/abis/index.js"; import { @@ -96,6 +97,7 @@ describe("ProtocolProvider", () => { eboRequestCreator: "0x1234567890123456789012345678901234567890", bondEscalationModule: "0x1234567890123456789012345678901234567890", horizonAccountingExtension: "0x1234567890123456789012345678901234567890", + horizonStaking: "0x1234567890123456789012345678901234567890", }; const mockBlockNumberService = { @@ -160,6 +162,14 @@ describe("ProtocolProvider", () => { }, }; } + if (abi === horizonStakingAbi && address === mockContractAddress.horizonStaking) { + return { + address, + read: { + isAuthorized: vi.fn(), + }, + }; + } throw new Error("Invalid contract address or ABI"); }); @@ -1333,4 +1343,66 @@ describe("ProtocolProvider", () => { expect(protocolProvider.getServiceProviderAddress()).toBe(mockDerivedAddress); }); }); + + describe("isAuthorized", () => { + it("returns true when operator is authorized", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + + ( + protocolProvider["horizonStakingContract"].read.isAuthorized as Mock + ).mockResolvedValue(true); + + const serviceProvider = "0xServiceProvider" as Address; + const verifier = "0xVerifier" as Address; + const operator = "0xOperator" as Address; + + const result = await protocolProvider.read.isAuthorized( + serviceProvider, + verifier, + operator, + ); + + expect(result).toBe(true); + expect( + protocolProvider["horizonStakingContract"].read.isAuthorized, + ).toHaveBeenCalledWith([serviceProvider, verifier, operator]); + }); + + it("returns false when operator is not authorized", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + + ( + protocolProvider["horizonStakingContract"].read.isAuthorized as Mock + ).mockResolvedValue(false); + + const serviceProvider = "0xServiceProvider" as Address; + const verifier = "0xVerifier" as Address; + const operator = "0xOperator" as Address; + + const result = await protocolProvider.read.isAuthorized( + serviceProvider, + verifier, + operator, + ); + + expect(result).toBe(false); + expect( + protocolProvider["horizonStakingContract"].read.isAuthorized, + ).toHaveBeenCalledWith([serviceProvider, verifier, operator]); + }); + }); }); From 436c87390f809b464dc2f353994143f29a5a6b06 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:21:29 -0500 Subject: [PATCH 13/14] fix: tests and pr comments --- apps/agent/config.tenderly.yml | 2 +- apps/agent/src/config/schemas.ts | 2 +- .../utilities/approveAccountingModules.ts | 20 +- .../src/providers/protocolProvider.ts | 37 +- .../tests/mocks/eboActor.mocks.ts | 2 +- .../tests/mocks/eboProcessor.mocks.ts | 9 +- .../tests/services/eboActor/fixtures.ts | 4 +- .../tests/services/eboProcessor.spec.ts | 1522 ++++++++--------- 8 files changed, 722 insertions(+), 876 deletions(-) diff --git a/apps/agent/config.tenderly.yml b/apps/agent/config.tenderly.yml index 849bd0a..666b7e5 100644 --- a/apps/agent/config.tenderly.yml +++ b/apps/agent/config.tenderly.yml @@ -16,7 +16,7 @@ protocolProvider: eboRequestCreator: "0xa13318684281a820304C164427396385C306d870" bondEscalationModule: "0x52d7728fE87826FfF51b21b303e2FF7cB04F6Aec" horizonAccountingExtension: "0xbDAB27D1903da4e18B0D1BE873E18924514E52eC" - horizonStaking: "0x1234567890123456789012345678901234567890" # TODO: Update address + horizonStaking: "0x3F53F9f9a5d7F36dCC869f8D2F227499c411c0cf" blockNumberService: blockmetaConfig: diff --git a/apps/agent/src/config/schemas.ts b/apps/agent/src/config/schemas.ts index 1360bb7..84e9430 100644 --- a/apps/agent/src/config/schemas.ts +++ b/apps/agent/src/config/schemas.ts @@ -33,7 +33,7 @@ export const envSchema = z.object({ DISCORD_WEBHOOK: z.string().optional(), }); -const addressSchema = z.string().refine((address) => isAddress(address)); +export const addressSchema = z.string().refine((address) => isAddress(address)); const rpcConfigSchema = z.object({ chainId: z diff --git a/apps/scripts/utilities/approveAccountingModules.ts b/apps/scripts/utilities/approveAccountingModules.ts index 43d5ea8..88efa45 100644 --- a/apps/scripts/utilities/approveAccountingModules.ts +++ b/apps/scripts/utilities/approveAccountingModules.ts @@ -20,11 +20,9 @@ const stringToJSONSchema = z.string().transform((str, ctx): Record isHex(val), { - message: "Must be a valid Hex string", -}); +const addressSchema = z.string().refine((address) => isAddress(address)); /** * Defines the schema for CONTRACTS_ADDRESSES based on expected structure. @@ -36,15 +34,17 @@ const contractsAddressesSchema = z.object({ l2ChainId: z.string().refine((val): val is Caip2ChainId => val.includes(":"), { message: "l2ChainId must be in the format 'namespace:chainId' (e.g., 'eip155:42161')", }), - oracle: hexSchema, - epochManager: hexSchema, - bondEscalationModule: hexSchema, - horizonAccountingExtension: hexSchema + oracle: addressSchema, + epochManager: addressSchema, + bondEscalationModule: addressSchema, + horizonAccountingExtension: addressSchema .optional() .default("0x0000000000000000000000000000000000000000"), - horizonStaking: hexSchema, + horizonStaking: addressSchema, // Default to a zero address because it's not required for the script but required for ProtocolProvider - eboRequestCreator: hexSchema.optional().default("0x0000000000000000000000000000000000000000"), + eboRequestCreator: addressSchema + .optional() + .default("0x0000000000000000000000000000000000000000"), }); /** diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index d1d2e34..ad75ab5 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -160,13 +160,13 @@ export class ProtocolProvider implements IProtocolProvider { address: contracts.oracle, abi: oracleAbi, client: this.l2WriteClient, - }) as GetContractReturnType; + }); this.epochManagerContract = getContract({ address: contracts.epochManager, abi: epochManagerAbi, client: this.l2ReadClient, - }) as GetContractReturnType; + }); this.eboRequestCreatorContract = getContract({ address: contracts.eboRequestCreator, @@ -175,11 +175,7 @@ export class ProtocolProvider implements IProtocolProvider { public: this.l2ReadClient, wallet: this.l2WriteClient, }, - }) as GetContractReturnType< - typeof eboRequestCreatorAbi, - typeof this.l2WriteClient, - Address - >; + }); this.bondEscalationContract = getContract({ address: contracts.bondEscalationModule, @@ -188,11 +184,7 @@ export class ProtocolProvider implements IProtocolProvider { public: this.l2ReadClient, wallet: this.l2WriteClient, }, - }) as GetContractReturnType< - typeof bondEscalationModuleAbi, - typeof this.l2WriteClient, - Address - >; + }); this.horizonAccountingExtensionContract = getContract({ address: contracts.horizonAccountingExtension, @@ -201,17 +193,13 @@ export class ProtocolProvider implements IProtocolProvider { public: this.l2ReadClient, wallet: this.l2WriteClient, }, - }) as GetContractReturnType< - typeof horizonAccountingExtensionAbi, - typeof this.l2WriteClient, - Address - >; + }); this.horizonStakingContract = getContract({ address: contracts.horizonStaking, abi: horizonStakingAbi, client: this.l2ReadClient, - }) as GetContractReturnType; + }); } public write: IWriteProvider = { @@ -870,11 +858,7 @@ export class ProtocolProvider implements IProtocolProvider { async getApprovedModules(user: Address): Promise { const bondAddress = user ?? this.getAccountAddress(); - const modules = await this.horizonAccountingExtensionContract.read.approvedModules([ - bondAddress, - ]); - - return modules; + return await this.horizonAccountingExtensionContract.read.approvedModules([bondAddress]); } // TODO: waiting for ChainId to be merged for _chains parameter @@ -1224,14 +1208,12 @@ export class ProtocolProvider implements IProtocolProvider { async getEscalation(requestId: RequestId): Promise { const result = await this.bondEscalationContract.read.getEscalation([requestId]); - const bondEscalation: BondEscalation = { + return { disputeId: result.disputeId, status: ProphetCodec.decodeBondEscalationStatus(result.status), amountOfPledgesForDispute: result.amountOfPledgesForDispute, amountOfPledgesAgainstDispute: result.amountOfPledgesAgainstDispute, }; - - return bondEscalation; } /** @@ -1247,11 +1229,10 @@ export class ProtocolProvider implements IProtocolProvider { verifier: Address, operator: Address, ): Promise { - const authorized = await this.horizonStakingContract.read.isAuthorized([ + return await this.horizonStakingContract.read.isAuthorized([ serviceProvider, verifier, operator, ]); - return authorized; } } diff --git a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts index d9b6e55..fee5771 100644 --- a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts @@ -4,7 +4,7 @@ import { Mutex } from "async-mutex"; import { Block, pad } from "viem"; import { vi } from "vitest"; -import { ProtocolProvider } from "../../src/providers/index.js"; +import { ProtocolProvider } from "../../src/index.js"; import { EboActor, EboMemoryRegistry, ProphetCodec } from "../../src/services/index.js"; import { Dispute, diff --git a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts index 47fba6c..a2577cc 100644 --- a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts @@ -2,9 +2,12 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Caip2ChainId, ILogger, NotificationService } from "@ebo-agent/shared"; import { vi } from "vitest"; -import { ProtocolProvider } from "../../src/providers/index.js"; -import { EboActorsManager, EboProcessor } from "../../src/services/index.js"; -import { AccountingModules } from "../../src/types/prophet.js"; +import { + AccountingModules, + EboActorsManager, + EboProcessor, + ProtocolProvider, +} from "../../src/index.js"; import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, diff --git a/packages/automated-dispute/tests/services/eboActor/fixtures.ts b/packages/automated-dispute/tests/services/eboActor/fixtures.ts index be3ea45..7874afb 100644 --- a/packages/automated-dispute/tests/services/eboActor/fixtures.ts +++ b/packages/automated-dispute/tests/services/eboActor/fixtures.ts @@ -1,8 +1,8 @@ import { Caip2ChainId, UnixTimestamp } from "@ebo-agent/shared"; import { Address, Hex, pad } from "viem"; +import { ProphetCodec } from "../../../src/index.js"; import { ProtocolContractsAddresses } from "../../../src/interfaces/index.js"; -import { ProphetCodec } from "../../../src/services/prophetCodec.js"; import { Dispute, DisputeId, @@ -23,6 +23,7 @@ export const DEFAULT_MOCKED_PROTOCOL_CONTRACTS: ProtocolContractsAddresses = { eboRequestCreator: "0x9999990000000000000000000000000000000000" as Address, bondEscalationModule: "0x1a2b3c0000000000000000000000000000000000" as Address, horizonAccountingExtension: "0x9999990000000000000000000000000000000000" as Address, + horizonStaking: "0x9999990000000000000000000000000000000000" as Address, }; export const DEFAULT_MOCKED_RESPONSE_DATA: Response = { @@ -101,6 +102,7 @@ export const DEFAULT_MOCKED_REQUEST_CREATED_DATA: Request = { ), finalityModuleData: "0x1311111111111111111111111111111111111111" as Hex, resolutionModuleData: "0x1511111111111111111111111111111111111111" as Hex, + accessModule: "0x1611111111111111111111111111111111111111" as Hex, }, }; diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index 98c8801..e0406a1 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -1,4 +1,6 @@ +import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Caip2ChainId, NotificationService, UnixTimestamp } from "@ebo-agent/shared"; +// NOTE: Must import Caip2Utils from here to ensure mock applies correctly import { Caip2Utils } from "@ebo-agent/shared/src/index.js"; import { Block, Hex } from "viem"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; @@ -8,7 +10,7 @@ import { PendingModulesApproval, ProcessorAlreadyStarted, } from "../../src/exceptions/index.js"; -import { ProphetCodec } from "../../src/index.js"; +import { EboActorsManager, EboProcessor, ProphetCodec, ProtocolProvider } from "../../src/index.js"; import { AccountingModules, EboEvent, @@ -28,945 +30,803 @@ const allModulesApproved = Object.values(accountingModules); describe("EboProcessor", () => { let notifier: NotificationService; + let processor: EboProcessor; + let protocolProvider: ProtocolProvider; + let actorsManager: EboActorsManager; + let blockNumberService: BlockNumberService; + + beforeEach(() => { + vi.useFakeTimers(); + notifier = { + send: vi.fn().mockResolvedValue(undefined), + sendOrThrow: vi.fn().mockResolvedValue(undefined), + sendError: vi.fn().mockResolvedValue(undefined), + createErrorMessage: vi.fn((defaultMessage, context) => { + return { + title: defaultMessage, + description: JSON.stringify(context, null, 2), + }; + }), + }; + + const built = mocks.buildEboProcessor(logger, accountingModules, notifier); + processor = built.processor; + protocolProvider = built.protocolProvider; + actorsManager = built.actorsManager; + blockNumberService = built.blockNumberService; + + vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(true); + + vi.spyOn(protocolProvider, "getServiceProviderAddress").mockReturnValue( + "0x0000000000000000000000000000000000000001", + ); + vi.spyOn(protocolProvider, "getAccountingModuleAddress").mockReturnValue( + "0x0000000000000000000000000000000000000002", + ); + vi.spyOn(protocolProvider, "getAccountAddress").mockReturnValue( + "0x0000000000000000000000000000000000000003", + ); + }); - describe("start", () => { - const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; - - beforeEach(() => { - vi.useFakeTimers(); - notifier = { - send: vi.fn().mockResolvedValue(undefined), - sendOrThrow: vi.fn().mockResolvedValue(undefined), - sendError: vi.fn().mockResolvedValue(undefined), - createErrorMessage: vi.fn((defaultMessage, context) => { - return { - title: defaultMessage, - description: JSON.stringify(context, null, 2), - }; - }), - }; - }); + afterEach(() => { + vi.useRealTimers(); + vi.clearAllTimers(); + vi.resetAllMocks(); + }); - afterEach(() => { - vi.useRealTimers(); - vi.resetAllMocks(); - }); + it("throws if at least one module is pending approval", async () => { + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue([]); - it("throws if at least one module is pending approval", async () => { - const { processor, protocolProvider } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); + const result = processor.start(); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue([]); + await expect(result).rejects.toThrow(PendingModulesApproval); + }); - const result = processor.start(); + it("bootstraps actors with onchain active requests when starting", async () => { + const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); + + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; + + const lastFinalizedBlock = { + number: currentEpoch.firstBlockNumber + 10n, + } as unknown as Block; + + const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + ); + + const requestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 1n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + metadata: { + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + request: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: encodedRequestModuleData, + }, + ipfsHash: "0x01" as Hex, + }, + }; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(lastFinalizedBlock); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue(["eip155:1"]); + + const mockCreateActor = vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + const mockCreateRequest = vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + + await processor.start(msBetweenChecks); + + const { chainId, epoch } = + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData; + + expect(mockCreateActor).toHaveBeenCalledWith( + expect.objectContaining({ + id: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + chainId: chainId, + epoch: epoch, + }), + protocolProvider, + blockNumberService, + logger, + notifier, + ); + expect(mockCreateRequest).not.toHaveBeenCalled(); + }); - await expect(result).rejects.toThrow(PendingModulesApproval); - }); + it("does not create actors to handle unsupported chains", async () => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + const unsupportedChains: Caip2ChainId[] = ["eip155:9999"]; - it("bootstraps actors with onchain active requests when starting", async () => { - const { processor, actorsManager, protocolProvider, blockNumberService } = - mocks.buildEboProcessor(logger, accountingModules, notifier); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + ...supportedChains, + ...unsupportedChains, + ]); - const currentEpoch: Epoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const allSupportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return allSupportedChains.includes(chainId); + }); - const lastFinalizedBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; + const unsupportedRequest: Request = { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, + id: "0xunsupported" as RequestId, + decodedData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, + requestModuleData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + chainId: "eip155:9999" as Caip2ChainId, + }, + }, + prophetData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ + epoch: 1n, + chainId: "eip155:9999" as Caip2ChainId, + accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, + paymentAmount: 1000n, + }), + }, + }; + + const unsupportedRequestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 2n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: unsupportedRequest.id, + metadata: { + requestId: unsupportedRequest.id, + request: { + ...unsupportedRequest.prophetData, + requestModuleData: unsupportedRequest.prophetData.requestModuleData, + }, + ipfsHash: "0x02" as Hex, + }, + }; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue({ + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue({ + number: 10n, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + } as unknown as Block); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([unsupportedRequestCreatedEvent]); - const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( - request.decodedData.requestModuleData, - ); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - const requestCreatedEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - blockNumber: 1n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request.id, - metadata: { - requestId: request.id, - request: { - ...request.prophetData, - requestModuleData: encodedRequestModuleData, - }, - ipfsHash: "0x01" as Hex, - }, - }; + const mockCreateActor = vi.spyOn(actorsManager, "createActor"); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( - lastFinalizedBlock, - ); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + const mockLoggerWarn = vi.spyOn(logger, "warn"); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue(["eip155:1"]); + const mockNotifierSendError = vi.spyOn(notifier, "sendError"); - const { actor } = mocks.buildEboActor(request, logger); + await processor.start(msBetweenChecks); - const mockCreateActor = vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); - const mockCreateRequest = vi - .spyOn(protocolProvider, "createRequest") - .mockResolvedValue(); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - await processor.start(msBetweenChecks); + expect(mockCreateActor).not.toHaveBeenCalledWith( + expect.objectContaining({ + id: unsupportedRequest.id, + chainId: "eip155:9999" as Caip2ChainId, + epoch: 1n, + }), + ); - const { chainId, epoch } = request.decodedData.requestModuleData; + expect(mockLoggerWarn).toHaveBeenCalledWith( + `Chain eip155:9999 not supported by the agent. Skipping...`, + ); - expect(mockCreateActor).toBeCalledWith( - expect.objectContaining({ - id: request.id, - chainId: chainId, - epoch: epoch, - }), - protocolProvider, - blockNumberService, - logger, - notifier, - ); - expect(mockCreateRequest).not.toHaveBeenCalled(); + expect(mockNotifierSendError).toHaveBeenCalledWith( + `Chain eip155:9999 not supported by the agent. Skipping...`, + { chainId: "eip155:9999", requestId: "0xunsupported" }, + new Error("Unsupported chain"), + ); + }); + + it("throws if called more than once", async () => { + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; + + const lastFinalizedBlock = { + number: 1n, + } as unknown as Block; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(lastFinalizedBlock); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); + + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return supportedChains.includes(chainId); }); - it("does not create actors to handle unsupported chains", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161"]), + ); - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - const unsupportedChains: Caip2ChainId[] = ["eip155:9999"]; + await processor.start(msBetweenChecks); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - ...supportedChains, - ...unsupportedChains, - ]); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const allSupportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return allSupportedChains.includes(chainId); - }); + await expect(processor.start(msBetweenChecks)).rejects.toThrow(ProcessorAlreadyStarted); + }); - const unsupportedRequest: Request = { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, - id: "0xunsupported" as RequestId, - decodedData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, - requestModuleData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, - chainId: "eip155:9999" as Caip2ChainId, - }, - }, - prophetData: { + it("fetches events since epoch start when starting", async () => { + const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); + + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; + + const currentBlock: Block = { + number: currentEpoch.firstBlockNumber + 10n, + } as unknown as Block; + + const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + ); + + const requestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 1n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + metadata: { + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + request: { ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ - epoch: 1n, - chainId: "eip155:9999" as Caip2ChainId, - accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, - paymentAmount: 1000n, - }), - }, - }; - - const unsupportedRequestCreatedEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - blockNumber: 2n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: unsupportedRequest.id, - metadata: { - requestId: unsupportedRequest.id, - request: { - ...unsupportedRequest.prophetData, - requestModuleData: unsupportedRequest.prophetData.requestModuleData, - }, - ipfsHash: "0x02" as Hex, + requestModuleData: encodedRequestModuleData, }, - }; + ipfsHash: "0x01" as Hex, + }, + }; - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue({ - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue({ - number: 10n, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - } as unknown as Block); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([ - unsupportedRequestCreatedEvent, - ]); + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); - const mockCreateActor = vi.spyOn(actorsManager, "createActor"); + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return supportedChains.includes(chainId); + }); - const mockLoggerWarn = vi.spyOn(logger, "warn"); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161"]), + ); - const mockNotifierSendError = vi.spyOn(notifier, "sendError"); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); + vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - await processor.start(msBetweenChecks); + const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); + mockGetEvents.mockResolvedValue([requestCreatedEvent]); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + await processor.start(msBetweenChecks); - expect(mockCreateActor).not.toHaveBeenCalledWith( - expect.objectContaining({ - id: unsupportedRequest.id, - chainId: "eip155:9999" as Caip2ChainId, - epoch: 1n, - }), - ); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - expect(mockLoggerWarn).toHaveBeenCalledWith( - `Chain eip155:9999 not supported by the agent. Skipping...`, - ); + expect(mockGetEvents).toHaveBeenCalledWith( + currentEpoch.firstBlockNumber, + currentBlock.number, + ); + }); - expect(mockNotifierSendError).toHaveBeenCalledWith( - `Chain eip155:9999 not supported by the agent. Skipping...`, - { chainId: "eip155:9999", requestId: "0xunsupported" }, - new Error("Unsupported chain"), - ); + it("drops past events and keeps operating", async () => { + const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); + + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; + + const currentBlock: Block = { + number: currentEpoch.firstBlockNumber + 10n, + } as unknown as Block; + + const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + ); + + const requestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 1n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + metadata: { + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + request: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: encodedRequestModuleData, + }, + ipfsHash: "0x01" as Hex, + }, + }; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); + + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); + + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return supportedChains.includes(chainId); }); - it("throws if called more than once", async () => { - const { processor, protocolProvider } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - - const currentEpoch: Epoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const lastFinalizedBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( - lastFinalizedBlock, - ); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); - - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); - }); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161"]), + ); + + vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); + vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor, "canBeTerminated").mockResolvedValue(false); + vi.spyOn(actor, "enqueue").mockImplementation(() => { + throw new PastEventEnqueueError(requestCreatedEvent, requestCreatedEvent); + }); - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); + const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); + mockGetEvents.mockResolvedValue([requestCreatedEvent]); - await processor.start(msBetweenChecks); + await processor.start(msBetweenChecks); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - await expect(processor.start(msBetweenChecks)).rejects.toThrow(ProcessorAlreadyStarted); - }); + expect(logger.warn).toHaveBeenCalledWith( + expect.stringContaining("Dropping already enqueued event"), + ); + }); - it("fetches events since epoch start when starting", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - const { actor } = mocks.buildEboActor(request, logger); - - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const currentBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; - - const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( - request.decodedData.requestModuleData, - ); - - const requestCreatedEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - blockNumber: 1n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request.id, - metadata: { - requestId: request.id, - request: { - ...request.prophetData, - requestModuleData: encodedRequestModuleData, - }, - ipfsHash: "0x01" as Hex, - }, - }; + it("fetches non-consumed events if event fetching fails", async () => { + const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); + const mockLastCheckedBlock = 5n; + processor["lastCheckedBlock"] = mockLastCheckedBlock; - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); + const currentEpoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); - }); + const currentBlock = { + number: currentEpoch.firstBlockNumber + 10n, + } as unknown as Block; - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor, "canBeTerminated").mockResolvedValue(false); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); + mockGetEvents.mockRejectedValueOnce(new Error("Fetch failed")).mockResolvedValueOnce([]); - const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); - mockGetEvents.mockResolvedValue([requestCreatedEvent]); + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return supportedChains.includes(chainId); + }); - await processor.start(msBetweenChecks); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161"]), + ); + + const mockSendError = vi.spyOn(notifier, "sendError").mockResolvedValue(undefined); + + await processor.start(msBetweenChecks); + + await vi.advanceTimersByTimeAsync(msBetweenChecks); + + expect(mockGetEvents).toHaveBeenCalledTimes(2); + expect(mockGetEvents).toHaveBeenNthCalledWith( + 1, + mockLastCheckedBlock + 1n, + currentBlock.number, + ); + expect(mockGetEvents).toHaveBeenNthCalledWith( + 2, + mockLastCheckedBlock + 1n, + currentBlock.number, + ); + + expect(mockSendError).toHaveBeenCalledWith( + "Error during synchronization", + {}, + expect.any(Error), + ); + }); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + it("enqueues and process every new event into the actor", async () => { + const actorRequest = DEFAULT_MOCKED_REQUEST_CREATED_DATA as Request; + mocks.buildEboActor(actorRequest, logger); + + const currentEpoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; + + const currentBlock = { + number: currentEpoch.firstBlockNumber + 10n, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + } as unknown as Block; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + + const request1: Request = { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, + id: "0x01" as RequestId, + decodedData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, + requestModuleData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + chainId: "eip155:1" as Caip2ChainId, + }, + }, + prophetData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ + epoch: 1n, + chainId: "eip155:1" as Caip2ChainId, + accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, + paymentAmount: 1000n, + }), + }, + }; + + const request2: Request = { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, + id: "0x02" as RequestId, + decodedData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, + requestModuleData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + chainId: "eip155:1" as Caip2ChainId, + }, + }, + prophetData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ + epoch: 1n, + chainId: "eip155:1" as Caip2ChainId, + accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, + paymentAmount: 1000n, + }), + }, + }; + const response = mocks.buildResponse(request1); - expect(mockGetEvents).toHaveBeenCalledWith( - currentEpoch.firstBlockNumber, - currentBlock.number, - ); - }); + const decodedRequestModuleData = + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData; + const encodedRequestModuleData = + ProphetCodec.encodeRequestRequestModuleData(decodedRequestModuleData); - it("drops past events and keeps operating", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - const { actor } = mocks.buildEboActor(request, logger); - - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const currentBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; - - const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( - request.decodedData.requestModuleData, - ); - - const requestCreatedEvent: EboEvent<"RequestCreated"> = { + const eventStream: EboEvent[] = [ + { name: "RequestCreated", - blockNumber: 1n, + blockNumber: 6n, logIndex: 1, timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request.id, + requestId: request1.id, metadata: { - requestId: request.id, + requestId: request1.id, request: { - ...request.prophetData, + ...request1.prophetData, requestModuleData: encodedRequestModuleData, }, ipfsHash: "0x01" as Hex, }, - }; - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); - - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); - - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); - }); - - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); - - vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); - vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); - vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "canBeTerminated").mockResolvedValue(false); - vi.spyOn(actor, "enqueue").mockImplementation(() => { - throw new PastEventEnqueueError(requestCreatedEvent, requestCreatedEvent); - }); - - const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); - mockGetEvents.mockResolvedValue([requestCreatedEvent]); - - await processor.start(msBetweenChecks); - - await vi.advanceTimersByTimeAsync(msBetweenChecks); - - expect(logger.warn).toHaveBeenCalledWith( - expect.stringContaining("Dropping already enqueued event"), - ); - }); - - it("keeps the last block checked unaltered when something fails during sync", async () => { - const initialCurrentBlock = 1n; - - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - const { actor } = mocks.buildEboActor(request, logger); - - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const mockProtocolProviderGetEvents = vi - .spyOn(protocolProvider, "getEvents") - .mockImplementationOnce(() => { - throw new Error(); - }) - .mockResolvedValueOnce([]); - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - - vi.spyOn(protocolProvider, "getLastFinalizedBlock") - .mockResolvedValueOnce({ number: initialCurrentBlock + 10n } as unknown as Block< - bigint, - false, - "finalized" - >) - .mockResolvedValueOnce({ number: initialCurrentBlock + 20n } as unknown as Block< - bigint, - false, - "finalized" - >); - - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); - vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "canBeTerminated").mockResolvedValue(false); - - await processor.start(msBetweenChecks); - - expect(mockProtocolProviderGetEvents).toHaveBeenNthCalledWith( - 1, - currentEpoch.firstBlockNumber, - initialCurrentBlock + 10n, - ); - - expect(mockProtocolProviderGetEvents).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith(expect.stringMatching("Sync failed")); - - await vi.advanceTimersByTimeAsync(msBetweenChecks); - - expect(mockProtocolProviderGetEvents).toHaveBeenNthCalledWith( - 2, - currentEpoch.firstBlockNumber, - initialCurrentBlock + 20n, - ); + }, + { + name: "ResponseProposed", + blockNumber: 7n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 2, 0, 0, 0, 0)) as UnixTimestamp, + requestId: request2.id, + metadata: { + requestId: request2.id, + responseId: response.id, + response: response.prophetData, + }, + }, + ]; + + vi.spyOn(protocolProvider, "getEvents") + .mockResolvedValueOnce(eventStream) + .mockResolvedValueOnce([]); + + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + "eip155:137", + ]); + + const actor1 = mocks.buildEboActor(request1, logger).actor; + const actor2 = mocks.buildEboActor(request2, logger).actor; + + vi.spyOn(actorsManager, "createActor") + .mockResolvedValueOnce(actor1) + .mockResolvedValueOnce(actor2); + + vi.spyOn(actorsManager, "getActor").mockImplementation((requestId: RequestId) => { + switch (requestId) { + case request1.id: + return actor1; + case request2.id: + return actor2; + default: + return undefined; + } }); - it("fetches non-consumed events if event fetching fails", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - const { actor } = mocks.buildEboActor(request, logger); - - const mockLastCheckedBlock = 5n; - processor["lastCheckedBlock"] = mockLastCheckedBlock; - - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const currentBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); - vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "canBeTerminated").mockResolvedValue(false); - - const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); - mockGetEvents - .mockImplementationOnce(() => { - throw new Error("Fetch failed"); - }) - .mockResolvedValueOnce([]); - - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); - }); + const mockEnqueue1 = vi.spyOn(actor1, "enqueue").mockImplementation(() => {}); + const mockEnqueue2 = vi.spyOn(actor2, "enqueue").mockImplementation(() => {}); + vi.spyOn(actor1, "processEvents").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor2, "processEvents").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor1, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor2, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); - - await processor.start(msBetweenChecks); - - await vi.advanceTimersByTimeAsync(msBetweenChecks); - - expect(mockGetEvents).toHaveBeenCalledTimes(2); - expect(mockGetEvents).toHaveBeenNthCalledWith( - 1, - mockLastCheckedBlock + 1n, - currentBlock.number, - ); - expect(mockGetEvents).toHaveBeenNthCalledWith( - 2, - mockLastCheckedBlock + 1n, - currentBlock.number, - ); + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161", "eip155:137"]; + return supportedChains.includes(chainId); }); - it("enqueues and process every new event into the actor", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - const actorRequest = DEFAULT_MOCKED_REQUEST_CREATED_DATA as Request; - const { actor } = mocks.buildEboActor(actorRequest, logger); - - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const currentBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - - const response = mocks.buildResponse(request); - - const decodedRequestModuleData = request.decodedData.requestModuleData; - const encodedRequestModuleData = - ProphetCodec.encodeRequestRequestModuleData(decodedRequestModuleData); - - const eventStream: EboEvent[] = [ - { - name: "RequestCreated", - blockNumber: 6n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request.id, - metadata: { - requestId: request.id, - request: { - ...request.prophetData, - requestModuleData: encodedRequestModuleData, - }, - ipfsHash: "0x01" as Hex, - }, - }, - { - name: "ResponseProposed", - blockNumber: 7n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 2, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request.id, - metadata: { - requestId: request.id, - responseId: response.id, - response: response.prophetData, - }, - }, - ]; - - vi.spyOn(protocolProvider, "getEvents") - .mockResolvedValueOnce(eventStream) - .mockResolvedValueOnce([]); - - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); - - vi.spyOn(actorsManager, "createActor").mockResolvedValue(actor); - vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); - vi.spyOn(actor, "enqueue").mockImplementation(() => {}); - vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); - }); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161", "eip155:137"]), + ); - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); + await processor.start(msBetweenChecks); - await processor.start(msBetweenChecks); + await vi.advanceTimersByTimeAsync(msBetweenChecks); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + expect(mockEnqueue1).toHaveBeenCalledWith(eventStream[0]); + expect(mockEnqueue2).toHaveBeenCalledWith(eventStream[1]); - expect(actor.enqueue).toHaveBeenCalledTimes(eventStream.length); - expect(actor.enqueue).toHaveBeenNthCalledWith(1, eventStream[0]); - expect(actor.enqueue).toHaveBeenNthCalledWith(2, eventStream[1]); + expect(actor1.processEvents).toHaveBeenCalledTimes(1); + expect(actor2.processEvents).toHaveBeenCalledTimes(1); + }); - expect(actor.processEvents).toHaveBeenCalledTimes(1); - expect(actor.onLastBlockUpdated).toHaveBeenCalledTimes(1); - }); + it("does not create a new request if a corresponding actor already exist", async () => { + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; - it("enqueues events into corresponding actors", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const currentBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - - const request1: Request = { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, - id: "0x01" as RequestId, - decodedData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, - requestModuleData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, - chainId: "eip155:1" as Caip2ChainId, - }, - }, - prophetData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ - epoch: 1n, - chainId: "eip155:1" as Caip2ChainId, - accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, - paymentAmount: 1000n, - }), - }, - }; - const response1 = mocks.buildResponse(request1); - - const request2: Request = { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, - id: "0x02" as RequestId, - decodedData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, - requestModuleData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, - chainId: "eip155:137" as Caip2ChainId, - }, - }, - prophetData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ - epoch: 1n, - chainId: "eip155:137" as Caip2ChainId, - accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, - paymentAmount: 1000n, - }), - }, - }; - const response2 = mocks.buildResponse(request2); - - const eventStream: EboEvent[] = [ - { - name: "ResponseProposed", - blockNumber: 7n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request1.id, - metadata: { - requestId: request1.id, - responseId: response1.id, - response: response1.prophetData, - }, - }, - { - name: "ResponseProposed", - blockNumber: 7n, - logIndex: 2, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: request2.id, - metadata: { - requestId: request2.id, - responseId: response2.id, - response: response2.prophetData, - }, - }, - ]; - - vi.spyOn(protocolProvider, "getEvents") - .mockResolvedValueOnce(eventStream) - .mockResolvedValueOnce([]); - - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - "eip155:137", - ]); - - const actor1 = mocks.buildEboActor(request1, logger).actor; - const actor2 = mocks.buildEboActor(request2, logger).actor; - - vi.spyOn(actorsManager, "createActor") - .mockResolvedValueOnce(actor1) - .mockResolvedValueOnce(actor2); - vi.spyOn(actorsManager, "getActor").mockImplementation((requestId: RequestId) => { - switch (requestId) { - case request1.id: - return actor1; - case request2.id: - return actor2; - default: - return undefined; - } - }); + const lastFinalizedBlock = { + number: 1n, + } as unknown as Block; - vi.spyOn(actor1, "enqueue").mockImplementation(() => {}); - vi.spyOn(actor2, "enqueue").mockImplementation(() => {}); - vi.spyOn(actor1, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor2, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor1, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor2, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(lastFinalizedBlock); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161", "eip155:137"]; - return supportedChains.includes(chainId); - }); + vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ + { id: "0x01" as RequestId, chainId: "eip155:1", epoch: currentEpoch.number }, + { id: "0x02" as RequestId, chainId: "eip155:42161", epoch: currentEpoch.number }, + ]); - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161", "eip155:137"]), - ); + processor["handledChainIdsPerEpoch"].set(1n, new Set(["eip155:1", "eip155:42161"])); - await processor.start(msBetweenChecks); + const mockProtocolProviderCreateRequest = vi + .spyOn(protocolProvider, "createRequest") + .mockImplementation(() => Promise.resolve()); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + await processor.start(msBetweenChecks); - expect(actor1.enqueue).toHaveBeenCalledWith(eventStream[0]); - expect(actor2.enqueue).toHaveBeenCalledWith(eventStream[1]); + expect(mockProtocolProviderCreateRequest).not.toHaveBeenCalled(); + }); - expect(actor1.processEvents).toHaveBeenCalledTimes(1); - expect(actor2.processEvents).toHaveBeenCalledTimes(1); + it("adds handled chain IDs when actors are created", async () => { + const { processor, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + notifier, + ); + + const requestId = "0x01" as RequestId; + const epoch = 1n; + const chainId = "eip155:1" as Caip2ChainId; + + const firstEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + requestId, + blockNumber: 1n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + metadata: { + requestId: "0x01" as RequestId, + request: DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + ipfsHash: "0x01" as Hex, + }, + }; + + vi.spyOn(actorsManager, "getActor").mockReturnValue(undefined); + vi.spyOn(actorsManager, "createActor").mockImplementation(() => { + return mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger).actor; }); - it("does not create a new request if a corresponding actor already exist", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); - - const currentEpoch: Epoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; - - const lastFinalizedBlock = { - number: 1n, - } as unknown as Block; - - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( - lastFinalizedBlock, - ); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); - - vi.spyOn(actorsManager, "getActorsRequests").mockReturnValue([ - { id: "0x01" as RequestId, chainId: "eip155:1", epoch: currentEpoch.number }, - { id: "0x02" as RequestId, chainId: "eip155:42161", epoch: currentEpoch.number }, - ]); - - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); - - const mockProtocolProviderCreateRequest = vi - .spyOn(protocolProvider, "createRequest") - .mockImplementation(() => Promise.resolve()); - - await processor.start(); - - expect(mockProtocolProviderCreateRequest).not.toHaveBeenCalled(); - }); + await processor["getOrCreateActor"](requestId, firstEvent); - it("adds handled chain IDs when actors are created", async () => { - const { processor, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); + const handledChainIds = processor["getHandledChainIds"](epoch); - const requestId = "0x01" as RequestId; - const epoch = 1n; - const chainId = "eip155:1" as Caip2ChainId; + expect(handledChainIds).toBeDefined(); + expect(handledChainIds!.has(chainId)).toBe(true); + }); - const firstEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - requestId, - blockNumber: 1n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - metadata: { - requestId: "0x01" as RequestId, - request: DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - ipfsHash: "0x01" as Hex, - }, - }; + it("retains handled chain IDs after actors are terminated", async () => { + const { processor, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + notifier, + ); - vi.spyOn(actorsManager, "getActor").mockReturnValue(undefined); - vi.spyOn(actorsManager, "createActor").mockImplementation(() => { - const mockRequest: Request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; - return mocks.buildEboActor(mockRequest, logger).actor; - }); + const requestId = "0x01" as RequestId; + const epoch = 1n; + const chainId = "eip155:1" as Caip2ChainId; - await processor["getOrCreateActor"](requestId, firstEvent); + const actor = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger).actor; - const handledChainIds = processor["getHandledChainIds"](epoch); + vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); + vi.spyOn(actorsManager, "deleteActor").mockReturnValue(true); - expect(handledChainIds).toBeDefined(); - expect(handledChainIds!.has(chainId)).toBe(true); - }); + processor["addHandledChainId"](epoch, chainId); - it("retains handled chain IDs after actors are terminated", async () => { - const { processor, actorsManager } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); + await processor["terminateActor"](requestId); - const requestId = "0x01" as RequestId; - const epoch = 1n; - const chainId = "eip155:1" as Caip2ChainId; + const handledChainIds = processor["getHandledChainIds"](epoch); - const mockRequest = DEFAULT_MOCKED_REQUEST_CREATED_DATA; + expect(handledChainIds).toBeDefined(); + expect(handledChainIds!.has(chainId)).toBe(true); + }); - const actor = mocks.buildEboActor(mockRequest, logger).actor; + it("cleans up epochs less than currentEpoch - 1", () => { + const { processor } = mocks.buildEboProcessor(logger, accountingModules, notifier); - vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); - vi.spyOn(actorsManager, "deleteActor").mockReturnValue(true); + processor["handledChainIdsPerEpoch"].set(1n, new Set(["eip155:1"])); + processor["handledChainIdsPerEpoch"].set(2n, new Set(["eip155:1"])); + processor["handledChainIdsPerEpoch"].set(3n, new Set(["eip155:1"])); - processor["addHandledChainId"](epoch, chainId); + processor["cleanupOldEpochs"](3n); - await processor["terminateActor"](requestId); + expect(processor["handledChainIdsPerEpoch"].has(1n)).toBe(false); + expect(processor["handledChainIdsPerEpoch"].has(2n)).toBe(true); + expect(processor["handledChainIdsPerEpoch"].has(3n)).toBe(true); + }); - const handledChainIds = processor["getHandledChainIds"](epoch); + it("does not create requests for already handled chain IDs", async () => { + const { processor, protocolProvider } = mocks.buildEboProcessor( + logger, + accountingModules, + notifier, + ); - expect(handledChainIds).toBeDefined(); - expect(handledChainIds!.has(chainId)).toBe(true); - }); + const currentEpoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; - it("cleans up epochs less than currentEpoch - 1", () => { - const { processor } = mocks.buildEboProcessor(logger, accountingModules, notifier); + const availableChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - processor["handledChainIdsPerEpoch"].set(1n, new Set(["eip155:1"])); - processor["handledChainIdsPerEpoch"].set(2n, new Set(["eip155:1"])); - processor["handledChainIdsPerEpoch"].set(3n, new Set(["eip155:1"])); + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue(availableChains); - processor["cleanupOldEpochs"](3n); + processor["handledChainIdsPerEpoch"].set(1n, new Set(["eip155:1"])); - expect(processor["handledChainIdsPerEpoch"].has(1n)).toBe(false); - expect(processor["handledChainIdsPerEpoch"].has(2n)).toBe(true); - expect(processor["handledChainIdsPerEpoch"].has(3n)).toBe(true); - }); + const createRequestSpy = vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - it("does not create requests for already handled chain IDs", async () => { - const { processor, protocolProvider } = mocks.buildEboProcessor( - logger, - accountingModules, - notifier, - ); + await processor["createMissingRequests"](currentEpoch.number); + + expect(createRequestSpy).toHaveBeenCalledTimes(1); + expect(createRequestSpy).toHaveBeenCalledWith(currentEpoch.number, "eip155:42161"); + }); - const currentEpoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; + it("logs error and exits the process if not authorized", async () => { + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(false); + const mockLoggerError = vi.spyOn(logger, "error").mockImplementation(() => {}); + const mockProcessExit = vi + .spyOn(process, "exit") + .mockImplementation((code?: string | number | null | undefined) => { + throw new Error(`process.exit: ${code}`); + }); + const mockSetInterval = vi.spyOn(global, "setInterval"); + + await expect(processor.start(msBetweenChecks)).rejects.toThrow("process.exit: 1"); + expect(mockLoggerError).toHaveBeenCalledWith( + "Authorization required: Operator 0x0000000000000000000000000000000000000003 is not authorized for service provider 0x0000000000000000000000000000000000000001.", + ); + expect(mockProcessExit).toHaveBeenCalledWith(1); + expect(mockSetInterval).not.toHaveBeenCalled(); + }); - const availableChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + it("proceeds normally if authorized", async () => { + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(true); + const mockSync = vi.spyOn(processor as any, "sync").mockResolvedValue(undefined); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue(availableChains); + const mockSetInterval = vi.spyOn(global, "setInterval").mockImplementation(() => { + return 1 as unknown as NodeJS.Timeout; + }); - processor["handledChainIdsPerEpoch"].set(1n, new Set(["eip155:1"])); + vi.spyOn(global, "clearInterval").mockImplementation(() => {}); - const createRequestSpy = vi - .spyOn(protocolProvider, "createRequest") - .mockResolvedValue(); + await processor.start(msBetweenChecks); - await processor["createMissingRequests"](currentEpoch.number); + if (processor["eventsInterval"]) { + clearInterval(processor["eventsInterval"]); + processor["eventsInterval"] = undefined; + } - expect(createRequestSpy).toHaveBeenCalledTimes(1); - expect(createRequestSpy).toHaveBeenCalledWith(currentEpoch.number, "eip155:42161"); - }); + expect(protocolProvider.isAuthorized).toHaveBeenCalledWith( + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + ); + expect(mockSync).toHaveBeenCalled(); + expect(mockSetInterval).toHaveBeenCalledWith(expect.any(Function), msBetweenChecks); }); }); From 255da56369c4050f4d224fe20e23d352d79f53f1 Mon Sep 17 00:00:00 2001 From: jahabeebs <47253537+jahabeebs@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:35:40 -0500 Subject: [PATCH 14/14] fix: pr comments --- .../tests/services/eboProcessor.spec.ts | 538 +++++++++--------- 1 file changed, 274 insertions(+), 264 deletions(-) diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index e0406a1..a94d507 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -74,278 +74,334 @@ describe("EboProcessor", () => { vi.resetAllMocks(); }); - it("throws if at least one module is pending approval", async () => { - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue([]); + describe("start", () => { + it("bootstraps actors with onchain active requests when starting", async () => { + const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); - const result = processor.start(); + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; - await expect(result).rejects.toThrow(PendingModulesApproval); - }); + const lastFinalizedBlock = { + number: currentEpoch.firstBlockNumber + 10n, + } as unknown as Block; - it("bootstraps actors with onchain active requests when starting", async () => { - const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); + const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + ); - const currentEpoch: Epoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; + const requestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 1n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + metadata: { + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + request: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: encodedRequestModuleData, + }, + ipfsHash: "0x01" as Hex, + }, + }; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( + lastFinalizedBlock, + ); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue(["eip155:1"]); + + const mockCreateActor = vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + const mockCreateRequest = vi + .spyOn(protocolProvider, "createRequest") + .mockResolvedValue(); + + await processor.start(msBetweenChecks); + + const { chainId, epoch } = + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData; + + expect(mockCreateActor).toHaveBeenCalledWith( + expect.objectContaining({ + id: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + chainId: chainId, + epoch: epoch, + }), + protocolProvider, + blockNumberService, + logger, + notifier, + ); + expect(mockCreateRequest).not.toHaveBeenCalled(); + }); - const lastFinalizedBlock = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; + it("does not create actors to handle unsupported chains", async () => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + const unsupportedChains: Caip2ChainId[] = ["eip155:9999"]; - const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( - DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, - ); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + ...supportedChains, + ...unsupportedChains, + ]); - const requestCreatedEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - blockNumber: 1n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, - metadata: { - requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, - request: { + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const allSupportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return allSupportedChains.includes(chainId); + }); + + const unsupportedRequest: Request = { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, + id: "0xunsupported" as RequestId, + decodedData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, + requestModuleData: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + chainId: "eip155:9999" as Caip2ChainId, + }, + }, + prophetData: { ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - requestModuleData: encodedRequestModuleData, + requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ + epoch: 1n, + chainId: "eip155:9999" as Caip2ChainId, + accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, + paymentAmount: 1000n, + }), }, - ipfsHash: "0x01" as Hex, - }, - }; + }; - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(lastFinalizedBlock); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue(["eip155:1"]); + const unsupportedRequestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 2n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: unsupportedRequest.id, + metadata: { + requestId: unsupportedRequest.id, + request: { + ...unsupportedRequest.prophetData, + requestModuleData: unsupportedRequest.prophetData.requestModuleData, + }, + ipfsHash: "0x02" as Hex, + }, + }; - const mockCreateActor = vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); - const mockCreateRequest = vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue({ + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue({ + number: 10n, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + } as unknown as Block); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([ + unsupportedRequestCreatedEvent, + ]); - await processor.start(msBetweenChecks); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - const { chainId, epoch } = - DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData; + const mockCreateActor = vi.spyOn(actorsManager, "createActor"); - expect(mockCreateActor).toHaveBeenCalledWith( - expect.objectContaining({ - id: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, - chainId: chainId, - epoch: epoch, - }), - protocolProvider, - blockNumberService, - logger, - notifier, - ); - expect(mockCreateRequest).not.toHaveBeenCalled(); - }); + const mockLoggerWarn = vi.spyOn(logger, "warn"); - it("does not create actors to handle unsupported chains", async () => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - const unsupportedChains: Caip2ChainId[] = ["eip155:9999"]; + const mockNotifierSendError = vi.spyOn(notifier, "sendError"); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - ...supportedChains, - ...unsupportedChains, - ]); + await processor.start(msBetweenChecks); - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const allSupportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return allSupportedChains.includes(chainId); - }); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - const unsupportedRequest: Request = { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA, - id: "0xunsupported" as RequestId, - decodedData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData, - requestModuleData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + expect(mockCreateActor).not.toHaveBeenCalledWith( + expect.objectContaining({ + id: unsupportedRequest.id, chainId: "eip155:9999" as Caip2ChainId, - }, - }, - prophetData: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - requestModuleData: ProphetCodec.encodeRequestRequestModuleData({ epoch: 1n, - chainId: "eip155:9999" as Caip2ChainId, - accountingExtension: "0x0000000000000000000000000000000000000000" as Hex, - paymentAmount: 1000n, }), - }, - }; + ); - const unsupportedRequestCreatedEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - blockNumber: 2n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: unsupportedRequest.id, - metadata: { - requestId: unsupportedRequest.id, - request: { - ...unsupportedRequest.prophetData, - requestModuleData: unsupportedRequest.prophetData.requestModuleData, - }, - ipfsHash: "0x02" as Hex, - }, - }; + expect(mockLoggerWarn).toHaveBeenCalledWith( + `Chain eip155:9999 not supported by the agent. Skipping...`, + ); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue({ - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + expect(mockNotifierSendError).toHaveBeenCalledWith( + `Chain eip155:9999 not supported by the agent. Skipping...`, + { chainId: "eip155:9999", requestId: "0xunsupported" }, + new Error("Unsupported chain"), + ); }); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue({ - number: 10n, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - } as unknown as Block); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([unsupportedRequestCreatedEvent]); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + it("throws if called more than once", async () => { + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; + + const lastFinalizedBlock = { + number: 1n, + } as unknown as Block; + + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( + lastFinalizedBlock, + ); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); + + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return supportedChains.includes(chainId); + }); - const mockCreateActor = vi.spyOn(actorsManager, "createActor"); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161"]), + ); - const mockLoggerWarn = vi.spyOn(logger, "warn"); + await processor.start(msBetweenChecks); - const mockNotifierSendError = vi.spyOn(notifier, "sendError"); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - await processor.start(msBetweenChecks); + await expect(processor.start(msBetweenChecks)).rejects.toThrow(ProcessorAlreadyStarted); + }); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + it("fetches events since epoch start when starting", async () => { + const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); - expect(mockCreateActor).not.toHaveBeenCalledWith( - expect.objectContaining({ - id: unsupportedRequest.id, - chainId: "eip155:9999" as Caip2ChainId, - epoch: 1n, - }), - ); + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + }; - expect(mockLoggerWarn).toHaveBeenCalledWith( - `Chain eip155:9999 not supported by the agent. Skipping...`, - ); + const currentBlock: Block = { + number: currentEpoch.firstBlockNumber + 10n, + } as unknown as Block; - expect(mockNotifierSendError).toHaveBeenCalledWith( - `Chain eip155:9999 not supported by the agent. Skipping...`, - { chainId: "eip155:9999", requestId: "0xunsupported" }, - new Error("Unsupported chain"), - ); - }); + const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( + DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, + ); - it("throws if called more than once", async () => { - const currentEpoch: Epoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; + const requestCreatedEvent: EboEvent<"RequestCreated"> = { + name: "RequestCreated", + blockNumber: 1n, + logIndex: 1, + timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + metadata: { + requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, + request: { + ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, + requestModuleData: encodedRequestModuleData, + }, + ipfsHash: "0x01" as Hex, + }, + }; - const lastFinalizedBlock = { - number: 1n, - } as unknown as Block; + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); + vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); + vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(lastFinalizedBlock); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ + "eip155:1", + "eip155:42161", + ]); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); + vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { + const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; + return supportedChains.includes(chainId); + }); - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); - }); + processor["handledChainIdsPerEpoch"].set( + currentEpoch.number, + new Set(["eip155:1", "eip155:42161"]), + ); - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); + vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); + vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); + vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); + vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); + vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - await processor.start(msBetweenChecks); + const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); + mockGetEvents.mockResolvedValue([requestCreatedEvent]); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + await processor.start(msBetweenChecks); - await expect(processor.start(msBetweenChecks)).rejects.toThrow(ProcessorAlreadyStarted); - }); + await vi.advanceTimersByTimeAsync(msBetweenChecks); - it("fetches events since epoch start when starting", async () => { - const { actor } = mocks.buildEboActor(DEFAULT_MOCKED_REQUEST_CREATED_DATA, logger); + expect(mockGetEvents).toHaveBeenCalledWith( + currentEpoch.firstBlockNumber, + currentBlock.number, + ); + }); - const currentEpoch: Epoch = { - number: 1n, - firstBlockNumber: 1n, - startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - }; + it("logs error and exits the process if not authorized", async () => { + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(false); + const mockLoggerError = vi.spyOn(logger, "error").mockImplementation(() => {}); + const mockProcessExit = vi + .spyOn(process, "exit") + .mockImplementation((code?: string | number | null | undefined) => { + throw new Error(`process.exit: ${code}`); + }); + const mockSetInterval = vi.spyOn(global, "setInterval"); + + await expect(processor.start(msBetweenChecks)).rejects.toThrow("process.exit: 1"); + expect(mockLoggerError).toHaveBeenCalledWith( + "Authorization required: Operator 0x0000000000000000000000000000000000000003 is not authorized for service provider 0x0000000000000000000000000000000000000001.", + ); + expect(mockProcessExit).toHaveBeenCalledWith(1); + expect(mockSetInterval).not.toHaveBeenCalled(); + }); - const currentBlock: Block = { - number: currentEpoch.firstBlockNumber + 10n, - } as unknown as Block; + it("proceeds normally if authorized", async () => { + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); + vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(true); + const mockSync = vi.spyOn(processor as any, "sync").mockResolvedValue(undefined); - const encodedRequestModuleData = ProphetCodec.encodeRequestRequestModuleData( - DEFAULT_MOCKED_REQUEST_CREATED_DATA.decodedData.requestModuleData, - ); + const mockSetInterval = vi.spyOn(global, "setInterval").mockImplementation(() => { + return 1 as unknown as NodeJS.Timeout; + }); - const requestCreatedEvent: EboEvent<"RequestCreated"> = { - name: "RequestCreated", - blockNumber: 1n, - logIndex: 1, - timestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp, - requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, - metadata: { - requestId: DEFAULT_MOCKED_REQUEST_CREATED_DATA.id, - request: { - ...DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData, - requestModuleData: encodedRequestModuleData, - }, - ipfsHash: "0x01" as Hex, - }, - }; + vi.spyOn(global, "clearInterval").mockImplementation(() => {}); - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); - vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); - vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); + await processor.start(msBetweenChecks); - vi.spyOn(protocolProvider, "getAvailableChains").mockResolvedValue([ - "eip155:1", - "eip155:42161", - ]); + if (processor["eventsInterval"]) { + clearInterval(processor["eventsInterval"]); + processor["eventsInterval"] = undefined; + } - vi.spyOn(Caip2Utils, "isSupported").mockImplementation((chainId: Caip2ChainId) => { - const supportedChains: Caip2ChainId[] = ["eip155:1", "eip155:42161"]; - return supportedChains.includes(chainId); + expect(protocolProvider.isAuthorized).toHaveBeenCalledWith( + "0x0000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000003", + ); + expect(mockSync).toHaveBeenCalled(); + expect(mockSetInterval).toHaveBeenCalledWith(expect.any(Function), msBetweenChecks); }); + }); - processor["handledChainIdsPerEpoch"].set( - currentEpoch.number, - new Set(["eip155:1", "eip155:42161"]), - ); - - vi.spyOn(protocolProvider, "createRequest").mockResolvedValue(); - vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); - vi.spyOn(actorsManager, "getActor").mockReturnValue(actor); - vi.spyOn(actor, "processEvents").mockImplementation(() => Promise.resolve()); - vi.spyOn(actor, "onLastBlockUpdated").mockImplementation(() => Promise.resolve()); - - const mockGetEvents = vi.spyOn(protocolProvider, "getEvents"); - mockGetEvents.mockResolvedValue([requestCreatedEvent]); - - await processor.start(msBetweenChecks); + it("throws if at least one module is pending approval", async () => { + vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue([]); - await vi.advanceTimersByTimeAsync(msBetweenChecks); + const result = processor.start(); - expect(mockGetEvents).toHaveBeenCalledWith( - currentEpoch.firstBlockNumber, - currentBlock.number, - ); + await expect(result).rejects.toThrow(PendingModulesApproval); }); it("drops past events and keeps operating", async () => { @@ -783,50 +839,4 @@ describe("EboProcessor", () => { expect(createRequestSpy).toHaveBeenCalledTimes(1); expect(createRequestSpy).toHaveBeenCalledWith(currentEpoch.number, "eip155:42161"); }); - - it("logs error and exits the process if not authorized", async () => { - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(false); - const mockLoggerError = vi.spyOn(logger, "error").mockImplementation(() => {}); - const mockProcessExit = vi - .spyOn(process, "exit") - .mockImplementation((code?: string | number | null | undefined) => { - throw new Error(`process.exit: ${code}`); - }); - const mockSetInterval = vi.spyOn(global, "setInterval"); - - await expect(processor.start(msBetweenChecks)).rejects.toThrow("process.exit: 1"); - expect(mockLoggerError).toHaveBeenCalledWith( - "Authorization required: Operator 0x0000000000000000000000000000000000000003 is not authorized for service provider 0x0000000000000000000000000000000000000001.", - ); - expect(mockProcessExit).toHaveBeenCalledWith(1); - expect(mockSetInterval).not.toHaveBeenCalled(); - }); - - it("proceeds normally if authorized", async () => { - vi.spyOn(protocolProvider, "getApprovedModules").mockResolvedValue(allModulesApproved); - vi.spyOn(protocolProvider, "isAuthorized").mockResolvedValue(true); - const mockSync = vi.spyOn(processor as any, "sync").mockResolvedValue(undefined); - - const mockSetInterval = vi.spyOn(global, "setInterval").mockImplementation(() => { - return 1 as unknown as NodeJS.Timeout; - }); - - vi.spyOn(global, "clearInterval").mockImplementation(() => {}); - - await processor.start(msBetweenChecks); - - if (processor["eventsInterval"]) { - clearInterval(processor["eventsInterval"]); - processor["eventsInterval"] = undefined; - } - - expect(protocolProvider.isAuthorized).toHaveBeenCalledWith( - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003", - ); - expect(mockSync).toHaveBeenCalled(); - expect(mockSetInterval).toHaveBeenCalledWith(expect.any(Function), msBetweenChecks); - }); });