diff --git a/apps/agent/src/index.ts b/apps/agent/src/index.ts index d5b9778..c0bf3b6 100644 --- a/apps/agent/src/index.ts +++ b/apps/agent/src/index.ts @@ -1,6 +1,7 @@ import { inspect } from "util"; import { EboActorsManager, EboProcessor } from "@ebo-agent/automated-dispute"; import { ProtocolProvider } from "@ebo-agent/automated-dispute/dist/providers/protocolProvider.js"; +import { AccountingModules } from "@ebo-agent/automated-dispute/dist/types/prophet.js"; import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Logger } from "@ebo-agent/shared"; @@ -29,6 +30,11 @@ const config = { }, processor: { msBetweenChecks: 1, + accountingModules: { + requestModule: "0x01", + responseModule: "0x02", + escalationModule: "0x03", + } as AccountingModules, }, }; @@ -49,7 +55,13 @@ const main = async (): Promise => { const actorsManager = new EboActorsManager(); - const processor = new EboProcessor(protocolProvider, blockNumberService, actorsManager, logger); + const processor = new EboProcessor( + config.processor.accountingModules, + protocolProvider, + blockNumberService, + actorsManager, + logger, + ); await processor.start(config.processor.msBetweenChecks); }; diff --git a/packages/automated-dispute/src/exceptions/eboProcessor/index.ts b/packages/automated-dispute/src/exceptions/eboProcessor/index.ts index ee485fe..bd6ba41 100644 --- a/packages/automated-dispute/src/exceptions/eboProcessor/index.ts +++ b/packages/automated-dispute/src/exceptions/eboProcessor/index.ts @@ -1 +1,2 @@ +export * from "./pendingModulesApproval.exception.js"; export * from "./processorAlreadyStarted.exception.js"; diff --git a/packages/automated-dispute/src/exceptions/eboProcessor/pendingModulesApproval.exception.ts b/packages/automated-dispute/src/exceptions/eboProcessor/pendingModulesApproval.exception.ts new file mode 100644 index 0000000..79f70a0 --- /dev/null +++ b/packages/automated-dispute/src/exceptions/eboProcessor/pendingModulesApproval.exception.ts @@ -0,0 +1,21 @@ +import { AccountingModules } from "../../types/index.js"; + +export class PendingModulesApproval extends Error { + constructor( + public readonly approvedModules: Partial, + public readonly pendingModules: Partial, + ) { + const approvedModulesStr = Object.entries(approvedModules) + .map(([key, value]) => `(${key}: ${value})`) + .join(", "); + + const pendingModulesStr = Object.entries(pendingModules) + .map(([key, value]) => `(${key}: ${value})`) + .join(", "); + + super( + `Modules approved: ${approvedModulesStr}\n` + + `Modules pending approval: ${pendingModulesStr}`, + ); + } +} diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts index 421c797..7943fc7 100644 --- a/packages/automated-dispute/src/interfaces/protocolProvider.ts +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -47,6 +47,20 @@ export interface IReadProvider { * @returns A promise that resolves with an array of chain IDs. */ getAvailableChains(): Promise; + + /** + * Gets the address of the accounting module. + * + * @returns An address that points to the deployed accounting module. + */ + getAccountingModuleAddress(): Address; + + /** + * Gets the list of approved modules' addresses based on the wallet's account address. + * + * @returns A promise that resolves with an array of approved modules. + */ + getAccountingApprovedModules(): Promise; } /** @@ -148,6 +162,13 @@ export interface IWriteProvider { * @returns A promise that resolves when the request is finalized. */ finalize(request: Request["prophetData"], response: Response["prophetData"]): Promise; + + /** + * Approves modules needed by the accounting contract. + * + * @param modules an array of addresses for the modules to be approved + */ + approveAccountingModules(modules: Address[]): Promise; } /** diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index dbb9d7b..29307cc 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -125,6 +125,7 @@ export class ProtocolProvider implements IProtocolProvider { settleDispute: this.settleDispute.bind(this), escalateDispute: this.escalateDispute.bind(this), finalize: this.finalize.bind(this), + approveAccountingModules: this.approveAccountingModules.bind(this), }; public read: IReadProvider = { @@ -133,6 +134,8 @@ export class ProtocolProvider implements IProtocolProvider { getEvents: this.getEvents.bind(this), hasStakedAssets: this.hasStakedAssets.bind(this), getAvailableChains: this.getAvailableChains.bind(this), + getAccountingModuleAddress: this.getAccountingModuleAddress.bind(this), + getAccountingApprovedModules: this.getAccountingApprovedModules.bind(this), }; /** @@ -257,6 +260,20 @@ export class ProtocolProvider implements IProtocolProvider { return ["eip155:1", "eip155:42161"]; } + getAccountingModuleAddress(): Address { + // TODO: implement actual method + return "0x01"; + } + + async getAccountingApprovedModules(): Promise { + // TODO: implement actual method + return []; + } + + async approveAccountingModules(_modules: Address[]): Promise { + // TODO: implement actual method + } + // TODO: waiting for ChainId to be merged for _chains parameter /** * Creates a request on the EBO Request Creator contract by simulating the transaction diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 7950502..99c8cbc 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -3,10 +3,21 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; import { Address, ILogger } from "@ebo-agent/shared"; -import { ProcessorAlreadyStarted } from "../exceptions/index.js"; +import { PendingModulesApproval, ProcessorAlreadyStarted } from "../exceptions/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; -import { alreadyDeletedActorWarning, droppingUnhandledEventsWarning } from "../templates/index.js"; -import { ActorRequest, EboEvent, EboEventName, Epoch, RequestId } from "../types/index.js"; +import { + alreadyDeletedActorWarning, + droppingUnhandledEventsWarning, + pendingApprovedModulesError, +} from "../templates/index.js"; +import { + AccountingModules, + ActorRequest, + EboEvent, + EboEventName, + Epoch, + RequestId, +} from "../types/index.js"; import { EboActorsManager } from "./eboActorsManager.js"; const DEFAULT_MS_BETWEEN_CHECKS = 10 * 60 * 1000; // 10 minutes @@ -18,6 +29,7 @@ export class EboProcessor { private lastCheckedBlock?: bigint; constructor( + private readonly accountingModules: AccountingModules, private readonly protocolProvider: ProtocolProvider, private readonly blockNumberService: BlockNumberService, private readonly actorsManager: EboActorsManager, @@ -32,6 +44,8 @@ export class EboProcessor { public async start(msBetweenChecks: number = DEFAULT_MS_BETWEEN_CHECKS) { if (this.eventsInterval) throw new ProcessorAlreadyStarted(); + await this.checkAllModulesApproved(); + await this.sync(); // Bootstrapping this.eventsInterval = setInterval(async () => { @@ -47,6 +61,42 @@ export class EboProcessor { }, msBetweenChecks); } + /** + * Check if all the modules have been granted approval within the accounting module. + * + * @throws {PendingModulesApproval} when there is at least one module pending approval + */ + private async checkAllModulesApproved() { + const approvedModules: Address[] = + await this.protocolProvider.getAccountingApprovedModules(); + + const summary: Record<"approved" | "notApproved", Partial> = { + approved: {}, + notApproved: {}, + }; + + for (const [moduleName, moduleAddress] of Object.entries(this.accountingModules)) { + const isApproved = approvedModules.includes(moduleAddress); + const key = (isApproved ? "approved" : "notApproved") as keyof typeof summary; + + summary[key] = { ...summary[key], [moduleName]: moduleAddress }; + } + + if (Object.keys(summary.notApproved).length > 0) { + const accountingModuleAddress = this.protocolProvider.getAccountingModuleAddress(); + + this.logger.error( + pendingApprovedModulesError( + accountingModuleAddress, + summary["approved"], + summary["notApproved"], + ), + ); + + throw new PendingModulesApproval(summary["approved"], summary["notApproved"]); + } + } + /** Sync new blocks and their events with their corresponding actors. */ private async sync() { try { diff --git a/packages/automated-dispute/src/templates/index.ts b/packages/automated-dispute/src/templates/index.ts index b3e63b2..1a3bdd5 100644 --- a/packages/automated-dispute/src/templates/index.ts +++ b/packages/automated-dispute/src/templates/index.ts @@ -1,4 +1,6 @@ -import { RequestId } from "../types/prophet.js"; +import { Address } from "viem"; + +import { AccountingModules, RequestId } from "../types/prophet.js"; export const alreadyDeletedActorWarning = (requestId: RequestId) => ` Actor handling request ${requestId} was already deleted. @@ -11,3 +13,32 @@ Dropping events for request ${requestId} because no actor is handling it and the The request likely started before the current epoch's first block, which will not be handled by the agent. `; + +export const pendingApprovedModulesError = ( + horizonAddress: Address, + approvedModules: Partial, + notApprovedModules: Partial, +) => { + const approvedModulesList = Object.entries(approvedModules).map( + ([key, value]) => `* ${key} at ${value}\n`, + ); + const notApprovedModulesList = Object.entries(notApprovedModules).map( + ([key, value]) => `* ${key} at ${value}\n`, + ); + + return ` +The EBO agent cannot proceed until certain actions are resolved by the operator. + +The following modules already have approvals from HorizonAccountingExtension at ${horizonAddress}: +${approvedModulesList} + +The following modules need approval from HorizonAccountingExtension at ${horizonAddress}: +${notApprovedModulesList} + +To grant the necessary approvals, please run the script located at: + +apps/scripts/approveAccountingModules.ts + +Once approvals are completed, restart the EBO agent to continue. +`; +}; diff --git a/packages/automated-dispute/src/types/prophet.ts b/packages/automated-dispute/src/types/prophet.ts index 9776991..8592149 100644 --- a/packages/automated-dispute/src/types/prophet.ts +++ b/packages/automated-dispute/src/types/prophet.ts @@ -72,3 +72,9 @@ export interface Dispute { requestId: RequestId; }; } + +export type AccountingModules = { + requestModule: Address; + responseModule: Address; + escalationModule: Address; +}; diff --git a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts index 1bca810..f678dc7 100644 --- a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts @@ -5,12 +5,20 @@ import { ILogger } from "@ebo-agent/shared"; import { ProtocolProvider } from "../../src/providers/index.js"; import { EboProcessor } from "../../src/services"; import { EboActorsManager } from "../../src/services/index.js"; +import { AccountingModules } from "../../src/types/prophet.js"; import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey, } from "../services/eboActor/fixtures.js"; -export function buildEboProcessor(logger: ILogger) { +export function buildEboProcessor( + logger: ILogger, + accountingModules: AccountingModules = { + requestModule: "0x01", + responseModule: "0x02", + escalationModule: "0x03", + }, +) { const protocolProviderRpcUrls = ["http://localhost:8538"]; const protocolProvider = new ProtocolProvider( protocolProviderRpcUrls, @@ -24,7 +32,14 @@ export function buildEboProcessor(logger: ILogger) { const blockNumberService = new BlockNumberService(blockNumberRpcUrls, logger); const actorsManager = new EboActorsManager(); - const processor = new EboProcessor(protocolProvider, blockNumberService, actorsManager, logger); + + const processor = new EboProcessor( + accountingModules, + protocolProvider, + blockNumberService, + actorsManager, + logger, + ); return { processor, diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index 26559f8..b2927ad 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -2,14 +2,27 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { ProcessorAlreadyStarted } from "../../src/exceptions/index.js"; +import { PendingModulesApproval, ProcessorAlreadyStarted } from "../../src/exceptions/index.js"; import { ProtocolProvider } from "../../src/providers/index.js"; -import { EboEvent, EboEventName, RequestId } from "../../src/types/index.js"; +import { + AccountingModules, + EboEvent, + EboEventName, + Epoch, + RequestId, +} from "../../src/types/index.js"; import mocks from "../mocks/index.js"; import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "../services/eboActor/fixtures.js"; const logger = mocks.mockLogger(); const msBetweenChecks = 1; +const accountingModules: AccountingModules = { + requestModule: "0x01", + responseModule: "0x02", + escalationModule: "0x03", +}; + +const allModulesApproved = Object.values(accountingModules); describe("EboProcessor", () => { describe("start", () => { @@ -23,13 +36,29 @@ describe("EboProcessor", () => { vi.useRealTimers(); }); + it("throws if at least one module is pending approval", async () => { + const { processor, protocolProvider } = mocks.buildEboProcessor( + logger, + accountingModules, + ); + + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue([]); + + const result = processor.start(); + + expect(result).rejects.toThrow(PendingModulesApproval); + }); + it("bootstraps actors with onchain active requests when starting", async () => { - const { processor, actorsManager, protocolProvider } = mocks.buildEboProcessor(logger); + const { processor, actorsManager, protocolProvider } = mocks.buildEboProcessor( + logger, + accountingModules, + ); - const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; const requestCreatedEvent: EboEvent<"RequestCreated"> = { @@ -45,9 +74,12 @@ describe("EboProcessor", () => { }, }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( - currentEpoch.currentEpochBlockNumber + 10n, + currentEpoch.firstBlockNumber + 10n, ); vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([requestCreatedEvent]); @@ -58,7 +90,7 @@ describe("EboProcessor", () => { const expectedActorRequest = expect.objectContaining({ id: requestCreatedEvent.requestId, - epoch: currentEpoch.currentEpoch, + epoch: currentEpoch.number, }); expect(mockCreateActor).toHaveBeenCalledWith( @@ -70,17 +102,23 @@ describe("EboProcessor", () => { }); it("throws if called more than once", async () => { - const { processor, protocolProvider } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider } = mocks.buildEboProcessor( + logger, + accountingModules, + ); - const currentEpoch = { - currentEpoch: 1n, - currentEpochBlockNumber: 1n, - currentEpochTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), + const currentEpoch: Epoch = { + number: 1n, + firstBlockNumber: 1n, + startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue( - currentEpoch.currentEpochBlockNumber + 10n, + currentEpoch.firstBlockNumber + 10n, ); vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); @@ -90,7 +128,10 @@ describe("EboProcessor", () => { }); it("fetches events since epoch start when starting", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const { actor } = mocks.buildEboActor(request, logger); const currentEpoch = { @@ -114,6 +155,9 @@ describe("EboProcessor", () => { }, }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); @@ -132,7 +176,10 @@ describe("EboProcessor", () => { it("keeps the last block checked unaltered when something fails during sync", async () => { const initialCurrentBlock = 1n; - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const { actor } = mocks.buildEboActor(request, logger); const currentEpoch = { @@ -149,6 +196,10 @@ describe("EboProcessor", () => { }) .mockResolvedValueOnce([]); + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); + vi.spyOn(protocolProvider, "getLastFinalizedBlock") .mockResolvedValueOnce(initialCurrentBlock + 10n) .mockResolvedValueOnce(initialCurrentBlock + 20n); @@ -180,7 +231,10 @@ describe("EboProcessor", () => { }); it("fetches non-consumed events if event fetching fails", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const { actor } = mocks.buildEboActor(request, logger); const mockLastCheckedBlock = 5n; @@ -207,6 +261,9 @@ describe("EboProcessor", () => { }, }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); vi.spyOn(actorsManager, "createActor").mockReturnValue(actor); @@ -225,7 +282,10 @@ describe("EboProcessor", () => { }); it("enqueues and process every new event into the actor", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const currentEpoch = { number: 1n, @@ -235,6 +295,9 @@ describe("EboProcessor", () => { const currentBlock = currentEpoch.firstBlockNumber + 10n; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); @@ -288,7 +351,10 @@ describe("EboProcessor", () => { }); it("enqueues events into corresponding actors", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const currentEpoch = { number: 1n, @@ -298,6 +364,9 @@ describe("EboProcessor", () => { const currentBlock = currentEpoch.firstBlockNumber + 10n; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock); @@ -379,7 +448,10 @@ describe("EboProcessor", () => { it.skip("notifies if an actor throws while handling events"); it("creates a request when no actor is handling a chain's current epoch", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const currentEpoch = { number: 1n, @@ -387,6 +459,9 @@ describe("EboProcessor", () => { startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(1n); vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); @@ -411,7 +486,10 @@ describe("EboProcessor", () => { }); it("does not create a new request if a corresponding actor already exist", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const currentEpoch = { number: 1n, @@ -419,6 +497,9 @@ describe("EboProcessor", () => { startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(1n); vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); @@ -442,7 +523,10 @@ describe("EboProcessor", () => { }); it("handles errors during request creation", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const currentEpoch = { epoch: 1n, @@ -450,6 +534,9 @@ describe("EboProcessor", () => { startTimestamp: BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)), }; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(1n); vi.spyOn(protocolProvider, "getEvents").mockResolvedValue([]); @@ -469,7 +556,10 @@ describe("EboProcessor", () => { it.skip("notifies if a request failed to be created"); it("removes the actor from registry when terminating", async () => { - const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor(logger); + const { processor, protocolProvider, actorsManager } = mocks.buildEboProcessor( + logger, + accountingModules, + ); const currentEpoch = { currentEpoch: 1n, @@ -479,6 +569,9 @@ describe("EboProcessor", () => { const currentBlock = currentEpoch.currentEpochBlockNumber + 10n; + vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue( + allModulesApproved, + ); vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue(currentEpoch); vi.spyOn(protocolProvider, "getLastFinalizedBlock").mockResolvedValue(currentBlock);