diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts index bcf25ab..74607c9 100644 --- a/packages/automated-dispute/src/interfaces/protocolProvider.ts +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -5,12 +5,14 @@ import type { AccessControl, BondEscalation, Dispute, + DisputeId, EboEvent, EboEventName, Epoch, Request, RequestId, Response, + ResponseId, } from "../types/index.js"; import { ProtocolContractsNames } from "../constants.js"; @@ -80,6 +82,20 @@ export interface IReadProvider { * @returns A Promise that resolves to the BondEscalation data. */ getEscalation(requestId: RequestId): Promise; + + /** + * Fetches the response data for a given response ID. + * + * @param responseId response ID + */ + getResponse(responseId: ResponseId): Promise; + + /** + * Fetches the dispute data for a given dispute ID. + * + * @param disputeId dispute ID + */ + getDispute(disputeId: DisputeId): Promise; } /** diff --git a/packages/automated-dispute/src/providers/protocolProvider.ts b/packages/automated-dispute/src/providers/protocolProvider.ts index 9daa196..9250beb 100644 --- a/packages/automated-dispute/src/providers/protocolProvider.ts +++ b/packages/automated-dispute/src/providers/protocolProvider.ts @@ -217,6 +217,8 @@ export class ProtocolProvider implements IProtocolProvider { getAccountingModuleAddress: this.getAccountingModuleAddress.bind(this), getApprovedModules: this.getApprovedModules.bind(this), getEscalation: this.getEscalation.bind(this), + getResponse: this.getResponse.bind(this), + getDispute: this.getDispute.bind(this), }; /** @@ -1197,6 +1199,55 @@ export class ProtocolProvider implements IProtocolProvider { } } + async getResponse(responseId: ResponseId): Promise { + const responses = await this.oracleContract.getEvents.ResponseProposed({ + _responseId: responseId, + }); + + if (!responses || responses.length === 0) return undefined; + if (responses.length > 1) { + this.logger.warn("Multiple responses found for the same response ID", { responseId }); + + return undefined; + } + + const response = responses[0]?.args?._response; + + if (!response) return undefined; + + return { + proposer: response.proposer as Address, + response: response.response as Hex, + requestId: response.requestId as RequestId, + }; + } + + async getDispute(disputeId: DisputeId): Promise { + const disputes = await this.oracleContract.getEvents.ResponseDisputed({ + _disputeId: disputeId, + }); + + if (!disputes || disputes.length === 0) return undefined; + if (disputes.length > 1) { + this.logger.warn("Multiple disputes found for the same dispute ID", { + disputeId: disputeId, + }); + + return undefined; + } + + const dispute = disputes[0]?.args?._dispute; + + if (!dispute) return undefined; + + return { + disputer: dispute.disputer as Address, + proposer: dispute.proposer as Address, + requestId: dispute.requestId as RequestId, + responseId: dispute.responseId as ResponseId, + }; + } + /** * Fetches the escalation data for a given request ID. * diff --git a/packages/automated-dispute/tests/services/protocolProvider.spec.ts b/packages/automated-dispute/tests/services/protocolProvider.spec.ts index b3e4f0d..f4b16a4 100644 --- a/packages/automated-dispute/tests/services/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/services/protocolProvider.spec.ts @@ -30,7 +30,7 @@ import { } from "../../src/exceptions/index.js"; import { ProtocolProvider } from "../../src/index.js"; import { ProtocolContractsAddresses } from "../../src/interfaces/index.js"; -import { EboEvent } from "../../src/types/index.js"; +import { DisputeId, EboEvent, RequestId, ResponseId } from "../../src/types/index.js"; import { mockLogger } from "../mocks/logger.mocks.js"; import { DEFAULT_MOCKED_DISPUTE_DATA, @@ -1306,6 +1306,209 @@ describe("ProtocolProvider", () => { }); }); + describe("getResponse", () => { + it("returns the response", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + + const responseId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as ResponseId; + + const mockResponse = { + proposer: "0x123", + requestId: "0x456", + response: "0x789", + }; + + protocolProvider["oracleContract"].getEvents = { + ResponseProposed: vi + .fn() + .mockResolvedValue([{ args: { _response: mockResponse } }]), + AccessModuleSet: vi.fn(), + DisputeEscalated: vi.fn(), + DisputeResolved: vi.fn(), + DisputeStatusUpdated: vi.fn(), + RequestCreated: vi.fn(), + OracleRequestFinalized: vi.fn(), + ResponseDisputed: vi.fn(), + }; + + await expect(protocolProvider.getResponse(responseId)).resolves.toEqual(mockResponse); + }); + + it("returns undefined when multiple responses found", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + + const mockResponse = { + proposer: "0x123", + requestId: "0x456", + response: "0x789", + }; + + const responseId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as ResponseId; + + protocolProvider["oracleContract"].getEvents = { + ResponseProposed: vi + .fn() + .mockResolvedValue([ + { args: { _response: mockResponse } }, + { args: { _response: mockResponse } }, + ]), + AccessModuleSet: vi.fn(), + DisputeEscalated: vi.fn(), + DisputeResolved: vi.fn(), + DisputeStatusUpdated: vi.fn(), + RequestCreated: vi.fn(), + OracleRequestFinalized: vi.fn(), + ResponseDisputed: vi.fn(), + }; + + await expect(protocolProvider.getResponse(responseId)).resolves.toBeUndefined(); + }); + + it("returns undefined when no response found", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + + const responseId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as ResponseId; + + protocolProvider["oracleContract"].getEvents = { + ResponseProposed: vi.fn().mockResolvedValue([]), + AccessModuleSet: vi.fn(), + DisputeEscalated: vi.fn(), + DisputeResolved: vi.fn(), + DisputeStatusUpdated: vi.fn(), + RequestCreated: vi.fn(), + OracleRequestFinalized: vi.fn(), + ResponseDisputed: vi.fn(), + }; + + await expect(protocolProvider.getResponse(responseId)).resolves.toBeUndefined(); + }); + }); + + describe("getDispute", () => { + it("returns the dispute", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + + const disputeId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as DisputeId; + + const mockDispute = { + _requestId: "0x123", + _disputeId: disputeId, + _dispute: { + disputer: "0x01" as Address, + proposer: "0x01" as Address, + requestId: "0x123" as RequestId, + responseId: "0x456" as ResponseId, + }, + }; + + protocolProvider["oracleContract"].getEvents = { + ResponseDisputed: vi.fn().mockResolvedValue([{ args: mockDispute }]), + DisputeEscalated: vi.fn(), + AccessModuleSet: vi.fn(), + DisputeResolved: vi.fn(), + DisputeStatusUpdated: vi.fn(), + RequestCreated: vi.fn(), + OracleRequestFinalized: vi.fn(), + ResponseProposed: vi.fn(), + }; + + await expect(protocolProvider.getDispute(disputeId)).resolves.toEqual( + mockDispute._dispute, + ); + }); + + it("returns undefined when multiple disputes found", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + const disputeId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as DisputeId; + + const mockDispute = { + args: { + _requestId: "0x123", + _disputeId: disputeId, + _dispute: { + disputer: "0x01" as Address, + proposer: "0x01" as Address, + requestId: "0x123" as RequestId, + responseId: "0x456" as ResponseId, + }, + }, + }; + + protocolProvider["oracleContract"].getEvents = { + ResponseDisputed: vi.fn().mockResolvedValue([mockDispute, mockDispute]), + AccessModuleSet: vi.fn(), + DisputeResolved: vi.fn(), + DisputeStatusUpdated: vi.fn(), + RequestCreated: vi.fn(), + OracleRequestFinalized: vi.fn(), + DisputeEscalated: vi.fn(), + ResponseProposed: vi.fn(), + }; + + await expect(protocolProvider.getDispute(disputeId)).resolves.toBeUndefined(); + }); + + it("returns undefined when no dispute found", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcConfigBase, + mockContractAddress, + mockedPrivateKey, + mockServiceProviderAddress, + mockLogger(), + mockBlockNumberService, + ); + const disputeId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as DisputeId; + + protocolProvider["oracleContract"].getEvents = { + ResponseDisputed: vi.fn().mockResolvedValue([]), + AccessModuleSet: vi.fn(), + DisputeResolved: vi.fn(), + DisputeStatusUpdated: vi.fn(), + RequestCreated: vi.fn(), + OracleRequestFinalized: vi.fn(), + DisputeEscalated: vi.fn(), + ResponseProposed: vi.fn(), + }; + + await expect(protocolProvider.getDispute(disputeId)).resolves.toBeUndefined(); + }); + }); + describe("Service Provider Address", () => { it("uses the provided serviceProviderAddress from config", () => { const protocolProvider = new ProtocolProvider(