Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: prophet types #49

Merged
merged 10 commits into from
Sep 20, 2024
956 changes: 443 additions & 513 deletions packages/automated-dispute/src/abis/bondEscalationModule.ts

Large diffs are not rendered by default.

164 changes: 137 additions & 27 deletions packages/automated-dispute/src/providers/protocolProvider.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { Caip2ChainId, Caip2Utils, InvalidChainId } from "@ebo-agent/blocknumber";
import {
Address,
BaseError,
ContractFunctionRevertedError,
createPublicClient,
createWalletClient,
decodeAbiParameters,
encodeAbiParameters,
fallback,
FallbackTransport,
getContract,
Expand All @@ -18,7 +20,17 @@ import {
import { privateKeyToAccount } from "viem/accounts";
import { arbitrum } from "viem/chains";

import type { Dispute, EboEvent, EboEventName, Epoch, Request, Response } from "../types/index.js";
import type {
Dispute,
DisputeId,
EboEvent,
EboEventName,
Epoch,
Request,
RequestId,
Response,
ResponseId,
} from "../types/index.js";
import {
bondEscalationModuleAbi,
eboRequestCreatorAbi,
Expand All @@ -38,6 +50,30 @@ import {
} from "../interfaces/index.js";
import { ErrorFactory } from "../services/errorFactory.js";

export const REQUEST_RESPONSE_MODULE_DATA_ABI_FIELDS = [
{ name: "accountingExtension", type: "address" },
{ name: "bondToken", type: "address" },
{ name: "bondSize", type: "uint256" },
{ name: "deadline", type: "uint256" },
{ name: "disputeWindow", type: "uint256" },
] as const;

export const REQUEST_DISPUTE_MODULE_DATA_ABI_FIELDS = [
{ name: "accountingExtension", type: "address" },
{ name: "bondToken", type: "address" },
{ name: "bondSize", type: "uint256" },
{ name: "maxNumberOfEscalations", type: "uint256" },
{ name: "bondEscalationDeadline", type: "uint256" },
{ name: "tyingBuffer", type: "uint256" },
{ name: "disputeWindow", type: "uint256" },
] as const;

export const RESPONSE_ABI_FIELDS = [
{ name: "chainId", type: "string" },
{ name: "epoch", type: "uint256" },
{ name: "block", type: "uint256" },
] as const;

// TODO: these constants should be env vars
const TRANSACTION_RECEIPT_CONFIRMATIONS = 1;
const TIMEOUT = 10000;
Expand Down Expand Up @@ -203,39 +239,20 @@ export class ProtocolProvider implements IProtocolProvider {
const eboRequestCreatorEvents: EboEvent<EboEventName>[] = [];

const oracleEvents = [
{
name: "ResponseProposed",
blockNumber: 2n,
logIndex: 1,
requestId: "0x01",
metadata: {
requestId: "0x01",
responseId: "0x02",
response: {
proposer: "0x12345678901234567890123456789012",
requestId: "0x01",
response: {
block: 1n,
chainId: "eip155:1",
epoch: 20n,
},
},
},
} as EboEvent<"ResponseProposed">,
{
name: "ResponseDisputed",
blockNumber: 3n,
logIndex: 1,
requestId: "0x01",
requestId: "0x01" as RequestId,
metadata: {
requestId: "0x01",
responseId: "0x02",
disputeId: "0x03",
requestId: "0x01" as RequestId,
responseId: "0x02" as ResponseId,
disputeId: "0x03" as DisputeId,
dispute: {
disputer: "0x12345678901234567890123456789012",
proposer: "0x12345678901234567890123456789012",
responseId: "0x02",
requestId: "0x01",
responseId: "0x02" as ResponseId,
requestId: "0x01" as RequestId,
},
},
} as EboEvent<"ResponseDisputed">,
Expand Down Expand Up @@ -286,6 +303,99 @@ export class ProtocolProvider implements IProtocolProvider {
// TODO: implement actual method
}

/**
* Decodes the Prophet's request responseModuleData bytes into an object.
*
* @param responseModuleData responseModuleData bytes
* @throws {BaseErrorType} when the responseModuleData decoding fails
* @returns a decoded object with responseModuleData properties
*/
static decodeRequestResponseModuleData(
responseModuleData: Request["prophetData"]["responseModuleData"],
): Request["decodedData"]["responseModuleData"] {
const decodedParameters = decodeAbiParameters(
REQUEST_RESPONSE_MODULE_DATA_ABI_FIELDS,
responseModuleData,
);

return {
accountingExtension: decodedParameters[0],
bondToken: decodedParameters[1],
bondSize: decodedParameters[2],
deadline: decodedParameters[3],
disputeWindow: decodedParameters[4],
};
}

/**
* Decodes the Prophet's request disputeModuelData bytes into an object.
*
* @param disputeModuelData disputeModuelData bytes
* @throws {BaseErrorType} when the disputeModuelData decoding fails
* @returns a decoded object with disputeModuelData properties
*/
static decodeRequestDisputeModuleData(
disputeModuleData: Request["prophetData"]["disputeModuleData"],
): Request["decodedData"]["disputeModuleData"] {
const decodedParameters = decodeAbiParameters(
REQUEST_DISPUTE_MODULE_DATA_ABI_FIELDS,
disputeModuleData,
);

return {
accountingExtension: decodedParameters[0],
bondToken: decodedParameters[1],
bondSize: decodedParameters[2],
maxNumberOfEscalations: decodedParameters[3],
bondEscalationDeadline: decodedParameters[4],
tyingBuffer: decodedParameters[5],
disputeWindow: decodedParameters[6],
};
}

/**
* Encodes a Prophet's response body object into bytes.
*
* @param response response body object
* @returns byte-encode response body
*/
static encodeResponse(
response: Response["decodedData"]["response"],
): Response["prophetData"]["response"] {
return encodeAbiParameters(RESPONSE_ABI_FIELDS, [
response.chainId,
response.epoch,
response.block,
]);
}

/**
* Decodes a Prophet's response body bytes into an object.
*
* @param response response body bytes
* @returns decoded response body object
*/
static decodeResponse(
response: Response["prophetData"]["response"],
): Response["decodedData"]["response"] {
const decodedParameters = decodeAbiParameters(RESPONSE_ABI_FIELDS, response);

const chainId = decodedParameters[0];

if (Caip2Utils.isCaip2ChainId(chainId)) {
return {
chainId: chainId,
epoch: decodedParameters[1],
block: decodedParameters[2],
};
} else {
throw new InvalidChainId(
`Could not decode response chain ID while decoding:\n${response}`,
);
}
}

// TODO: waiting for ChainId to be merged for _chains parameter
/**
* Creates a request on the EBO Request Creator contract by simulating the transaction
* and then executing it if the simulation is successful.
Expand Down
30 changes: 15 additions & 15 deletions packages/automated-dispute/src/services/eboActor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { BlockNumberService } from "@ebo-agent/blocknumber";
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { ILogger } from "@ebo-agent/shared";
import { BlockNumberService, Caip2ChainId } from "@ebo-agent/blocknumber";
import { Address, ILogger } from "@ebo-agent/shared";
import { Mutex } from "async-mutex";
import { Heap } from "heap-js";
import { BlockNumber, ContractFunctionRevertedError } from "viem";
Expand All @@ -14,6 +13,7 @@ import type {
Request,
Response,
ResponseBody,
ResponseId,
} from "../types/index.js";
import {
DisputeWithoutResponse,
Expand Down Expand Up @@ -266,7 +266,7 @@ export class EboActor {
await this.settleDisputes(blockNumber);

const request = this.getActorRequest();
const proposalDeadline = request.prophetData.responseModuleData.deadline;
const proposalDeadline = request.decodedData.responseModuleData.deadline;
const isProposalWindowOpen = blockNumber <= proposalDeadline;

if (isProposalWindowOpen) {
Expand Down Expand Up @@ -328,7 +328,7 @@ export class EboActor {
private canBeSettled(request: Request, dispute: Dispute, blockNumber: bigint): boolean {
if (dispute.status !== "Active") return false;

const { bondEscalationDeadline, tyingBuffer } = request.prophetData.disputeModuleData;
const { bondEscalationDeadline, tyingBuffer } = request.decodedData.disputeModuleData;
const deadline = bondEscalationDeadline + tyingBuffer;

return blockNumber > deadline;
Expand Down Expand Up @@ -412,7 +412,7 @@ export class EboActor {
const request = this.getActorRequest();
const dispute = this.registry.getResponseDispute(response);
const disputeWindow =
response.createdAt + request.prophetData.disputeModuleData.disputeWindow;
response.createdAt + request.decodedData.disputeModuleData.disputeWindow;

// Response is still able to be disputed
if (blockNumber <= disputeWindow) return false;
Expand Down Expand Up @@ -513,7 +513,7 @@ export class EboActor {

for (const proposedResponse of responses) {
const responseId = proposedResponse.id;
const proposedBody = proposedResponse.prophetData.response;
const proposedBody = proposedResponse.decodedData.response;

if (this.equalResponses(proposedBody, newResponse)) {
this.logger.info(
Expand Down Expand Up @@ -569,7 +569,7 @@ export class EboActor {
const response: Response["prophetData"] = {
proposer: proposerAddress,
requestId: request.id,
response: responseBody,
response: ProtocolProvider.encodeResponse(responseBody),
};

try {
Expand Down Expand Up @@ -598,23 +598,23 @@ export class EboActor {
*/
private async onResponseProposed(event: EboEvent<"ResponseProposed">): Promise<void> {
const eventResponse = event.metadata.response;
const actorResponse = await this.buildResponse(eventResponse.response.chainId);
const decodedResponse = ProtocolProvider.decodeResponse(eventResponse.response);
const actorResponse = await this.buildResponse(decodedResponse.chainId);

if (this.equalResponses(actorResponse, eventResponse.response)) {
if (this.equalResponses(actorResponse, decodedResponse)) {
this.logger.info(`Response ${event.metadata.responseId} was validated. Skipping...`);
return;
}

const request = this.getActorRequest();

const disputer = this.protocolProvider.getAccountAddress();

const dispute: Dispute["prophetData"] = {
disputer: disputer,
proposer: eventResponse.proposer,
responseId: event.metadata.responseId,
responseId: Address.normalize(event.metadata.responseId) as ResponseId,
requestId: request.id,
};

await this.protocolProvider.disputeResponse(request.prophetData, eventResponse, dispute);
}

Expand Down Expand Up @@ -690,12 +690,12 @@ export class EboActor {
*/
private async isValidDispute(proposedResponse: Response) {
const actorResponse = await this.buildResponse(
proposedResponse.prophetData.response.chainId,
proposedResponse.decodedData.response.chainId,
);

const equalResponses = this.equalResponses(
actorResponse,
proposedResponse.prophetData.response,
proposedResponse.decodedData.response,
);

return !equalResponses;
Expand Down
4 changes: 3 additions & 1 deletion packages/automated-dispute/src/services/eboActorsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export class EboActorsManager {
* @returns array of normalized request IDs
*/
public getRequestIds(): RequestId[] {
return [...this.requestActorMap.keys()].map((requestId) => Address.normalize(requestId));
return [...this.requestActorMap.keys()].map(
(requestId) => Address.normalize(requestId) as RequestId,
);
}

public getActorsRequests(): ActorRequest[] {
Expand Down
13 changes: 6 additions & 7 deletions packages/automated-dispute/src/services/eboProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { isNativeError } from "util/types";
import { BlockNumberService } from "@ebo-agent/blocknumber";
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { BlockNumberService, Caip2ChainId } from "@ebo-agent/blocknumber";
import { Address, EBO_SUPPORTED_CHAIN_IDS, ILogger } from "@ebo-agent/shared";

import { PendingModulesApproval, ProcessorAlreadyStarted } from "../exceptions/index.js";
Expand Down Expand Up @@ -205,7 +204,7 @@ export class EboProcessor {
const groupedEvents = new Map<RequestId, EboEventStream>();

for (const event of events) {
const requestId = Address.normalize(event.requestId);
const requestId = Address.normalize(event.requestId) as RequestId;
const requestEvents = groupedEvents.get(requestId) || [];

groupedEvents.set(requestId, [...requestEvents, event]);
Expand All @@ -221,11 +220,11 @@ export class EboProcessor {
* @param eventsRequestIds request IDs observed in an events batch
* @returns request IDS to sync
*/
private calculateSynchableRequests(eventsRequestIds: RequestId[]) {
private calculateSynchableRequests(eventsRequestIds: RequestId[]): RequestId[] {
const actorsRequestIds = this.actorsManager.getRequestIds();
const uniqueRequestIds = new Set([...eventsRequestIds, ...actorsRequestIds]);

return [...uniqueRequestIds].map((requestId) => Address.normalize(requestId));
return [...uniqueRequestIds].map((requestId) => Address.normalize(requestId) as RequestId);
}

/**
Expand Down Expand Up @@ -309,8 +308,8 @@ export class EboProcessor {
* @returns a new `EboActor` instance, `null` if the actor was not created
*/
private createNewActor(event: EboEvent<"RequestCreated">) {
const actorRequest = {
id: Address.normalize(event.requestId),
const actorRequest: ActorRequest = {
id: Address.normalize(event.requestId) as RequestId,
epoch: event.metadata.epoch,
chainId: event.metadata.chainId,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CommandAlreadyRun, CommandNotRun } from "../../../exceptions/index.js";
import { EboRegistry, EboRegistryCommand } from "../../../interfaces/index.js";
import { ProtocolProvider } from "../../../providers/index.js";
import { EboEvent, Request } from "../../../types/index.js";

export class AddRequest implements EboRegistryCommand {
Expand All @@ -14,11 +15,20 @@ export class AddRequest implements EboRegistryCommand {
event: EboEvent<"RequestCreated">,
registry: EboRegistry,
): AddRequest {
const eventRequest = event.metadata.request;
const request: Request = {
id: event.requestId,
chainId: event.metadata.chainId,
epoch: event.metadata.epoch,
createdAt: event.blockNumber,
decodedData: {
disputeModuleData: ProtocolProvider.decodeRequestDisputeModuleData(
eventRequest.disputeModuleData,
),
responseModuleData: ProtocolProvider.decodeRequestResponseModuleData(
eventRequest.responseModuleData,
),
},
prophetData: event.metadata.request,
status: "Active",
};
Expand Down
Loading