diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 505a536..1ad2795 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -25,7 +25,6 @@ import { arbitrum, arbitrumSepolia, mainnet, sepolia } from "viem/chains"; import type { BondEscalation, - BondEscalationStatus, Dispute, DisputeId, EboEvent, @@ -57,6 +56,7 @@ import { IWriteProvider, ProtocolContractsAddresses, } from "../interfaces/index.js"; +import { ProphetCodec } from "../services/index.js"; type RpcConfig = { chainId: Caip2ChainId; @@ -1114,32 +1114,11 @@ export class ProtocolProvider implements IProtocolProvider { const bondEscalation: BondEscalation = { disputeId: result.disputeId, - status: this.decodeBondEscalationStatus(result.status), + status: ProphetCodec.decodeBondEscalationStatus(result.disputeId, result.status), amountOfPledgesForDispute: result.amountOfPledgesForDispute, amountOfPledgesAgainstDispute: result.amountOfPledgesAgainstDispute, }; return bondEscalation; } - - /** - * Decodes the BondEscalationStatus enum from the contract. - * - * @param status - The numeric status from the contract. - * @returns The corresponding BondEscalationStatus string. - */ - private decodeBondEscalationStatus(status: number): BondEscalationStatus { - switch (status) { - case 0: - return "Active"; - case 1: - return "Resolved"; - case 2: - return "Escalated"; - case 3: - return "NoResolution"; - default: - throw new Error(`Unknown BondEscalationStatus: ${status}`); - } - } } diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index dc7046f..0e9f74b 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -1,3 +1,4 @@ +import { isNativeError } from "util/types"; import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Caip2ChainId, ILogger, stringify, UnixTimestamp } from "@ebo-agent/shared"; import { Mutex } from "async-mutex"; @@ -467,15 +468,21 @@ export class EboActor { response: Response, dispute: Dispute, ): Promise { + let escalationData; try { - this.logger.info(`Settling dispute ${dispute.id}...`); - - const escalationData = await this.protocolProvider.read.getEscalation(request.id); + escalationData = await this.protocolProvider.read.getEscalation(request.id); + } catch (err: unknown) { + this.logger.error( + `Failed to fetch escalation data for request ${request.id}: ${isNativeError(err) ? err.message : err}`, + ); + return; + } + try { const amountFor = escalationData.amountOfPledgesForDispute; const amountAgainst = escalationData.amountOfPledgesAgainstDispute; - if (amountFor > amountAgainst) { + if (amountFor !== amountAgainst) { await this.protocolProvider.settleDispute( request.prophetData, response.prophetData, @@ -483,7 +490,7 @@ export class EboActor { ); this.logger.info(`Dispute ${dispute.id} settled.`); - } else if (amountFor <= amountAgainst) { + } else if (amountFor === amountAgainst) { await this.protocolProvider.escalateDispute( request.prophetData, response.prophetData, @@ -492,8 +499,6 @@ export class EboActor { this.logger.info(`Dispute ${dispute.id} escalated.`); } - - this.logger.info(`Dispute ${dispute.id} settled.`); } catch (err) { if (err instanceof CustomContractError) { this.logger.warn(`Call reverted for dispute ${dispute.id} due to: ${err.name}`); @@ -505,8 +510,7 @@ export class EboActor { registry: this.registry, }); } else { - this.logger.error(`Failed to escalate dispute ${dispute.id}: ${err}`); - + this.logger.error(`Failed to settle dispute ${dispute.id}: ${err}`); throw err; } } diff --git a/packages/automated-dispute/src/services/prophetCodec.ts b/packages/automated-dispute/src/services/prophetCodec.ts index 6e54fa0..3026a7f 100644 --- a/packages/automated-dispute/src/services/prophetCodec.ts +++ b/packages/automated-dispute/src/services/prophetCodec.ts @@ -9,6 +9,7 @@ import { toHex, } from "viem"; +import type { BondEscalationStatus } from "../types/prophet.js"; import { ProphetDecodingError } from "../exceptions/index.js"; import { DisputeStatus, Request, Response } from "../types/prophet.js"; @@ -256,4 +257,26 @@ export class ProphetCodec { // TODO: throw ProphetEncodingError return index; } + + /** + * Decodes the BondEscalationStatus enum from the contract. + * + * @param id - The ID of the request. + * @param status - The numeric status from the contract. + * @returns The corresponding BondEscalationStatus string. + */ + static decodeBondEscalationStatus(id: Hex, status: number): BondEscalationStatus { + switch (status) { + case 0: + return "Active"; + case 1: + return "Resolved"; + case 2: + return "Escalated"; + case 3: + return "NoResolution"; + default: + throw new ProphetDecodingError(id, toHex(status.toString())); + } + } } diff --git a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts index ac3751d..bb1fb90 100644 --- a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts @@ -1,7 +1,7 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; import { Caip2ChainId, ILogger, UnixTimestamp } from "@ebo-agent/shared"; import { Mutex } from "async-mutex"; -import { Block } from "viem"; +import { Block, pad } from "viem"; import { vi } from "vitest"; import { ProtocolProvider } from "../../src/providers/index.js"; @@ -63,6 +63,7 @@ export function buildEboActor(request: Request, logger: ILogger) { amountOfPledgesForDispute: BigInt(10), amountOfPledgesAgainstDispute: BigInt(5), }); + vi.spyOn(protocolProvider, "getCurrentEpoch").mockResolvedValue({ number: BigInt(1), firstBlockNumber: BigInt(100), @@ -170,7 +171,7 @@ export function buildResponse(request: Request, attributes: Partial = }; const baseResponse: Response = { - id: attributes.id || (("0x" + "02".repeat(32)) as ResponseId), + id: attributes.id || (pad("0x02") as ResponseId), createdAt: { timestamp: (request.createdAt.timestamp + 1n) as UnixTimestamp, blockNumber: request.createdAt.blockNumber + 1n, @@ -198,7 +199,7 @@ export function buildDispute( attributes: Partial = {}, ): Dispute { const baseDispute: Dispute = { - id: attributes.id || (("0x" + "03".repeat(32)) as DisputeId), + id: attributes.id || (pad("0x03") as DisputeId), status: "Active", createdAt: { timestamp: (response.createdAt.timestamp + 1n) as UnixTimestamp, diff --git a/packages/automated-dispute/tests/services/eboActor.spec.ts b/packages/automated-dispute/tests/services/eboActor.spec.ts index 7104c90..e9f8624 100644 --- a/packages/automated-dispute/tests/services/eboActor.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor.spec.ts @@ -518,7 +518,7 @@ describe("EboActor", () => { }); describe("settleDispute", () => { - it("escalates dispute when amountOfPledgesForDispute <= amountOfPledgesAgainstDispute", async () => { + it("escalates dispute when amountOfPledgesForDispute === amountOfPledgesAgainstDispute", async () => { const { actor, protocolProvider } = mocks.buildEboActor(request, logger); const response = mocks.buildResponse(request); const dispute = mocks.buildDispute(request, response); @@ -527,7 +527,7 @@ describe("EboActor", () => { disputeId: dispute.id, status: "Active", amountOfPledgesForDispute: BigInt(5), - amountOfPledgesAgainstDispute: BigInt(10), + amountOfPledgesAgainstDispute: BigInt(5), }); const escalateDisputeMock = vi @@ -564,11 +564,15 @@ describe("EboActor", () => { ); }); - it("settles dispute when amountOfPledgesForDispute > amountOfPledgesAgainstDispute", async () => { + it("settles dispute when amountOfPledgesForDispute !== amountOfPledgesAgainstDispute", async () => { const { actor, protocolProvider } = mocks.buildEboActor(request, logger); const response = mocks.buildResponse(request); const dispute = mocks.buildDispute(request, response); + const settleDisputeMock = vi + .spyOn(protocolProvider, "settleDispute") + .mockResolvedValue(); + vi.spyOn(protocolProvider.read, "getEscalation").mockResolvedValue({ disputeId: dispute.id, status: "Active", @@ -576,10 +580,6 @@ describe("EboActor", () => { amountOfPledgesAgainstDispute: BigInt(5), }); - const settleDisputeMock = vi - .spyOn(protocolProvider, "settleDispute") - .mockResolvedValue(); - await actor["settleDispute"](request, response, dispute); expect(settleDisputeMock).toHaveBeenCalledWith( diff --git a/packages/automated-dispute/tests/services/eboActor/fixtures.ts b/packages/automated-dispute/tests/services/eboActor/fixtures.ts index 1b572fd..d3b7ca1 100644 --- a/packages/automated-dispute/tests/services/eboActor/fixtures.ts +++ b/packages/automated-dispute/tests/services/eboActor/fixtures.ts @@ -1,5 +1,5 @@ import { Caip2ChainId, UnixTimestamp } from "@ebo-agent/shared"; -import { Address, Hex } from "viem"; +import { Address, Hex, pad } from "viem"; import { ProtocolContractsAddresses } from "../../../src/interfaces/index.js"; import { ProphetCodec } from "../../../src/services/prophetCodec.js"; @@ -24,7 +24,7 @@ export const DEFAULT_MOCKED_PROTOCOL_CONTRACTS: ProtocolContractsAddresses = { }; export const DEFAULT_MOCKED_RESPONSE_DATA: Response = { - id: ("0x" + "02".repeat(32)) as ResponseId, + id: pad("0x02") as ResponseId, createdAt: { timestamp: 1625097600n as UnixTimestamp, blockNumber: 1n, @@ -68,7 +68,7 @@ const DEFAULT_REQUEST_MODULES_DATA = { }; export const DEFAULT_MOCKED_REQUEST_CREATED_DATA: Request = { - id: ("0x" + "01".repeat(32)) as RequestId, + id: pad("0x01") as RequestId, createdAt: { timestamp: BigInt(Date.UTC(2024, 0, 1, 0, 0, 0, 0) / 1000) as UnixTimestamp, blockNumber: 1n, @@ -103,7 +103,7 @@ export const DEFAULT_MOCKED_REQUEST_CREATED_DATA: Request = { }; export const DEFAULT_MOCKED_DISPUTE_DATA: Dispute = { - id: ("0x" + "03".repeat(32)) as DisputeId, + id: pad("0x03") as DisputeId, createdAt: 1625097800n, status: "Active", prophetData: { diff --git a/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts b/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts index 391cd25..16c2d9e 100644 --- a/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts +++ b/packages/automated-dispute/tests/services/eboActor/onLastBlockupdated.spec.ts @@ -1,4 +1,5 @@ import { UnixTimestamp } from "@ebo-agent/shared"; +import { pad } from "viem"; import { describe, expect, it, vi } from "vitest"; import { DisputeWithoutResponse } from "../../../src/exceptions/index.js"; @@ -15,7 +16,7 @@ describe("EboActor", () => { const { disputeModuleData } = request.decodedData; const responseToSettle = mocks.buildResponse(request, { - id: ("0x" + "02".repeat(32)) as ResponseId, + id: pad("0x02") as ResponseId, }); const disputeToSettle = mocks.buildDispute(request, responseToSettle, { createdAt: { @@ -66,7 +67,7 @@ describe("EboActor", () => { const newBlockNumber = disputeDeadline + 1n; vi.spyOn(protocolProvider.read, "getEscalation").mockResolvedValue({ - disputeId: ("0x" + "03".repeat(32)) as DisputeId, + disputeId: pad("0x03") as DisputeId, status: "Active", amountOfPledgesForDispute: BigInt(10), amountOfPledgesAgainstDispute: BigInt(5),