diff --git a/.gitignore b/.gitignore index d9cc783..5d534a8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,10 @@ node_modules/ dist/ # Coverage -coverage/ \ No newline at end of file +coverage/ + +# IDE project settings +.idea/ + +# Mac +.DS_Store \ No newline at end of file diff --git a/package.json b/package.json index 6354c6a..f86cec4 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "devDependencies": { "@commitlint/cli": "19.3.0", "@commitlint/config-conventional": "19.2.2", + "@defi-wonderland/prophet-core": "0.0.0-5e1cf557", + "@defi-wonderland/prophet-modules": "0.0.0-e52f8cce", "@ianvs/prettier-plugin-sort-imports": "4.3.1", "@types/node": "20.14.12", "@typescript-eslint/eslint-plugin": "7.16.1", diff --git a/packages/automated-dispute/src/abis/eboRequestCreator.ts b/packages/automated-dispute/src/abis/eboRequestCreator.ts new file mode 100644 index 0000000..128910c --- /dev/null +++ b/packages/automated-dispute/src/abis/eboRequestCreator.ts @@ -0,0 +1,409 @@ +export const eboRequestCreatorAbi = [ + { + type: "constructor", + inputs: [ + { name: "_oracle", type: "address", internalType: "contract IOracle" }, + { name: "_epochManager", type: "address", internalType: "contract IEpochManager" }, + { name: "_arbitrator", type: "address", internalType: "address" }, + { name: "_council", type: "address", internalType: "address" }, + { + 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" }, + ], + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "ORACLE", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IOracle" }], + stateMutability: "view", + }, + { + type: "function", + name: "START_EPOCH", + inputs: [], + outputs: [{ name: "", type: "uint256", internalType: "uint256" }], + stateMutability: "view", + }, + { + type: "function", + name: "addChain", + inputs: [{ name: "_chainId", type: "string", internalType: "string" }], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "arbitrator", + inputs: [], + outputs: [{ name: "__arbitrator", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "confirmCouncil", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "council", + inputs: [], + outputs: [{ name: "__council", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "createRequests", + inputs: [ + { name: "_epoch", type: "uint256", internalType: "uint256" }, + { name: "_chainIds", type: "string[]", internalType: "string[]" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "epochManager", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IEpochManager" }], + stateMutability: "view", + }, + { + type: "function", + name: "pendingCouncil", + inputs: [], + outputs: [{ name: "__pendingCouncil", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "removeChain", + inputs: [{ name: "_chainId", type: "string", internalType: "string" }], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + 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" }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "requestIdPerChainAndEpoch", + inputs: [ + { name: "_chainId", type: "string", internalType: "string" }, + { name: "_epoch", type: "uint256", internalType: "uint256" }, + ], + outputs: [{ name: "_requestId", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + name: "setArbitrator", + inputs: [{ name: "__arbitrator", type: "address", internalType: "address" }], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setDisputeModuleData", + inputs: [ + { name: "_disputeModule", type: "address", internalType: "address" }, + { + name: "_disputeModuleData", + type: "tuple", + internalType: "struct IBondEscalationModule.RequestParameters", + components: [ + { + name: "accountingExtension", + 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" }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setEpochManager", + inputs: [ + { name: "_epochManager", type: "address", internalType: "contract IEpochManager" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setFinalityModuleData", + inputs: [ + { name: "_finalityModule", type: "address", internalType: "address" }, + { name: "_finalityModuleData", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setPendingCouncil", + inputs: [{ name: "__pendingCouncil", type: "address", internalType: "address" }], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setRequestModuleData", + inputs: [ + { 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: "accountingExtension", + type: "address", + internalType: "contract IAccountingExtension", + }, + { name: "paymentAmount", type: "uint256", internalType: "uint256" }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setResolutionModuleData", + inputs: [ + { name: "_resolutionModule", type: "address", internalType: "address" }, + { name: "_resolutionModuleData", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setResponseModuleData", + inputs: [ + { name: "_responseModule", type: "address", internalType: "address" }, + { + name: "_responseModuleData", + type: "tuple", + internalType: "struct IBondedResponseModule.RequestParameters", + components: [ + { + name: "accountingExtension", + 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" }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "ChainAdded", + inputs: [{ name: "_chainId", type: "string", indexed: true, internalType: "string" }], + anonymous: false, + }, + { + type: "event", + name: "ChainRemoved", + 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: "_disputeModuleData", + type: "tuple", + indexed: false, + internalType: "struct IBondEscalationModule.RequestParameters", + components: [ + { + name: "accountingExtension", + 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" }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "EpochManagerSet", + inputs: [ + { + name: "_epochManager", + type: "address", + indexed: true, + internalType: "contract IEpochManager", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "FinalityModuleDataSet", + inputs: [ + { name: "_finalityModule", type: "address", indexed: true, internalType: "address" }, + { name: "_finalityModuleData", type: "bytes", indexed: false, internalType: "bytes" }, + ], + anonymous: false, + }, + { + type: "event", + name: "RequestCreated", + inputs: [ + { name: "_requestId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "_epoch", type: "uint256", indexed: true, internalType: "uint256" }, + { name: "_chainId", type: "string", indexed: true, internalType: "string" }, + ], + anonymous: false, + }, + { + type: "event", + name: "RequestModuleDataSet", + inputs: [ + { 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: "accountingExtension", + type: "address", + internalType: "contract IAccountingExtension", + }, + { name: "paymentAmount", type: "uint256", internalType: "uint256" }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ResolutionModuleDataSet", + inputs: [ + { name: "_resolutionModule", type: "address", indexed: true, internalType: "address" }, + { name: "_resolutionModuleData", type: "bytes", indexed: false, internalType: "bytes" }, + ], + anonymous: false, + }, + { + type: "event", + name: "ResponseModuleDataSet", + inputs: [ + { name: "_responseModule", type: "address", indexed: true, internalType: "address" }, + { + name: "_responseModuleData", + type: "tuple", + indexed: false, + internalType: "struct IBondedResponseModule.RequestParameters", + components: [ + { + name: "accountingExtension", + 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" }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "SetArbitrator", + inputs: [{ name: "_arbitrator", type: "address", indexed: true, internalType: "address" }], + anonymous: false, + }, + { + type: "event", + name: "SetCouncil", + inputs: [{ name: "_council", type: "address", indexed: true, internalType: "address" }], + anonymous: false, + }, + { + type: "event", + name: "SetPendingCouncil", + inputs: [ + { name: "_pendingCouncil", type: "address", indexed: true, internalType: "address" }, + ], + anonymous: false, + }, + { type: "error", name: "Arbitrable_OnlyArbitrator", inputs: [] }, + { type: "error", name: "Arbitrable_OnlyCouncil", inputs: [] }, + { type: "error", name: "Arbitrable_OnlyPendingCouncil", inputs: [] }, + { type: "error", name: "EBORequestCreator_ChainAlreadyAdded", inputs: [] }, + { type: "error", name: "EBORequestCreator_ChainNotAdded", inputs: [] }, + { type: "error", name: "EBORequestCreator_InvalidEpoch", inputs: [] }, + { type: "error", name: "EBORequestCreator_InvalidNonce", inputs: [] }, +] as const; diff --git a/packages/automated-dispute/src/abis/index.ts b/packages/automated-dispute/src/abis/index.ts index b4a23ae..a62db90 100644 --- a/packages/automated-dispute/src/abis/index.ts +++ b/packages/automated-dispute/src/abis/index.ts @@ -1,2 +1,3 @@ export * from "./oracle.js"; export * from "./epochManager.js"; +export * from "./eboRequestCreator.js"; diff --git a/packages/automated-dispute/src/constants.ts b/packages/automated-dispute/src/constants.ts index 55948ff..9f0f68b 100644 --- a/packages/automated-dispute/src/constants.ts +++ b/packages/automated-dispute/src/constants.ts @@ -1 +1 @@ -export const ProtocolContractsNames = ["oracle", "epochManager"] as const; +export const ProtocolContractsNames = ["oracle", "epochManager", "eboRequestCreator"] as const; diff --git a/packages/automated-dispute/src/eboActor.ts b/packages/automated-dispute/src/eboActor.ts index 889c04b..4731385 100644 --- a/packages/automated-dispute/src/eboActor.ts +++ b/packages/automated-dispute/src/eboActor.ts @@ -7,8 +7,12 @@ import { ContractFunctionRevertedError } from "viem"; import { DisputeWithoutResponse } from "./exceptions/eboActor/disputeWithoutResponse.exception.js"; import { + EBORequestCreator_ChainNotAdded, + EBORequestCreator_InvalidEpoch, + EBORequestModule_InvalidRequester, InvalidActorState, InvalidDisputeStatus, + Oracle_InvalidRequestBody, PastEventEnqueueError, RequestMismatch, ResponseAlreadyProposed, @@ -487,7 +491,17 @@ export class EboActor { await this.proposeResponse(chainId); } catch (err) { if (err instanceof ResponseAlreadyProposed) this.logger.info(err.message); - else throw err; + else if (err instanceof EBORequestCreator_InvalidEpoch) { + // TODO: Handle error + } else if (err instanceof Oracle_InvalidRequestBody) { + // TODO: Handle error + } else if (err instanceof EBORequestModule_InvalidRequester) { + // TODO: Handle error + } else if (err instanceof EBORequestCreator_ChainNotAdded) { + // TODO: Handle error + } else { + throw err; + } } } diff --git a/packages/automated-dispute/src/exceptions/chainNotAdded.exception.ts b/packages/automated-dispute/src/exceptions/chainNotAdded.exception.ts new file mode 100644 index 0000000..2e5912d --- /dev/null +++ b/packages/automated-dispute/src/exceptions/chainNotAdded.exception.ts @@ -0,0 +1,6 @@ +export class EBORequestCreator_ChainNotAdded extends Error { + constructor() { + super("Chain not added"); + this.name = "EBORequestCreator_ChainNotAdded"; + } +} diff --git a/packages/automated-dispute/src/exceptions/index.ts b/packages/automated-dispute/src/exceptions/index.ts index f01857f..407bf03 100644 --- a/packages/automated-dispute/src/exceptions/index.ts +++ b/packages/automated-dispute/src/exceptions/index.ts @@ -8,3 +8,7 @@ export * from "./requestAlreadyHandled.exception.js"; export * from "./requestMismatch.exception.js"; export * from "./responseAlreadyProposed.exception.js"; export * from "./rpcUrlsEmpty.exception.js"; +export * from "./chainNotAdded.exception.js"; +export * from "./invalidEpoch.exception.js"; +export * from "./invalidRequestBody.exception.js"; +export * from "./invalidRequester.exception.js"; diff --git a/packages/automated-dispute/src/exceptions/invalidEpoch.exception.ts b/packages/automated-dispute/src/exceptions/invalidEpoch.exception.ts new file mode 100644 index 0000000..f5dd1d1 --- /dev/null +++ b/packages/automated-dispute/src/exceptions/invalidEpoch.exception.ts @@ -0,0 +1,6 @@ +export class EBORequestCreator_InvalidEpoch extends Error { + constructor() { + super("Invalid epoch"); + this.name = "EBORequestCreator_InvalidEpoch"; + } +} diff --git a/packages/automated-dispute/src/exceptions/invalidRequestBody.exception.ts b/packages/automated-dispute/src/exceptions/invalidRequestBody.exception.ts new file mode 100644 index 0000000..969e261 --- /dev/null +++ b/packages/automated-dispute/src/exceptions/invalidRequestBody.exception.ts @@ -0,0 +1,6 @@ +export class Oracle_InvalidRequestBody extends Error { + constructor() { + super("Invalid request body"); + this.name = "Oracle_InvalidRequestBody"; + } +} diff --git a/packages/automated-dispute/src/exceptions/invalidRequester.exception.ts b/packages/automated-dispute/src/exceptions/invalidRequester.exception.ts new file mode 100644 index 0000000..fdf831b --- /dev/null +++ b/packages/automated-dispute/src/exceptions/invalidRequester.exception.ts @@ -0,0 +1,6 @@ +export class EBORequestModule_InvalidRequester extends Error { + constructor() { + super("Invalid requester"); + this.name = "EBORequestModule_InvalidRequester"; + } +} diff --git a/packages/automated-dispute/src/interfaces/index.ts b/packages/automated-dispute/src/interfaces/index.ts index 38d57ab..fe9d437 100644 --- a/packages/automated-dispute/src/interfaces/index.ts +++ b/packages/automated-dispute/src/interfaces/index.ts @@ -1,2 +1,3 @@ export * from "./eboRegistry.js"; export * from "./eboRegistryCommand.js"; +export * from "./protocolProvider.js"; diff --git a/packages/automated-dispute/src/interfaces/protocolProvider.ts b/packages/automated-dispute/src/interfaces/protocolProvider.ts new file mode 100644 index 0000000..ce236c7 --- /dev/null +++ b/packages/automated-dispute/src/interfaces/protocolProvider.ts @@ -0,0 +1,174 @@ +import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; +import { Timestamp } from "@ebo-agent/shared"; +import { Address } from "viem"; + +import type { EboEvent, EboEventName } from "../types/events.js"; +import type { Dispute, Request, Response } from "../types/prophet.js"; +import { ProtocolContractsNames } from "../constants.js"; + +export type ProtocolContract = (typeof ProtocolContractsNames)[number]; +export type ProtocolContractsAddresses = Record; + +/** + * IReadProvider defines the read operations that can be performed on the protocol. + */ +export interface IReadProvider { + /** + * Gets the current epoch, along with the block number and its timestamp. + * + * @returns A promise that resolves with the current epoch, block number, and timestamp. + */ + getCurrentEpoch(): Promise<{ + currentEpoch: bigint; + currentEpochBlockNumber: bigint; + currentEpochTimestamp: Timestamp; + }>; + + /** + * Gets the last finalized block number. + * + * @returns A promise that resolves with the block number of the last finalized block. + */ + getLastFinalizedBlock(): Promise; + + /** + * Retrieves events from the protocol within a specified block range. + * + * @param _fromBlock The starting block number. + * @param _toBlock The ending block number. + * @returns A promise that resolves with an array of protocol events. + */ + getEvents(_fromBlock: bigint, _toBlock: bigint): Promise[]>; + + /** + * Checks whether the specified address has staked assets. + * + * @param _address The address to check. + * @returns A promise that resolves with a boolean indicating whether the address has staked assets. + */ + hasStakedAssets(_address: Address): Promise; + + /** + * Gets the list of available chains that the protocol supports. + * + * @returns A promise that resolves with an array of chain IDs. + */ + getAvailableChains(): Promise; +} + +/** + * IWriteProvider defines the write operations that can be performed on the protocol. + */ +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. + * @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: string[]): Promise; + + /** + * Proposes a response to a request. + * + * @param _requestId The ID of the request. + * @param _epoch The epoch of the request. + * @param _chainId The chain ID where the request was made. + * @param _blockNumber The block number associated with the response. + * @returns A promise that resolves when the response is proposed. + */ + proposeResponse( + _requestId: string, + _epoch: bigint, + _chainId: Caip2ChainId, + _blockNumber: bigint, + ): Promise; + + /** + * Disputes a proposed response. + * + * @param _requestId The ID of the request. + * @param _responseId The ID of the response to dispute. + * @param _proposer The address of the proposer. + * @returns A promise that resolves when the response is disputed. + */ + disputeResponse(_requestId: string, _responseId: string, _proposer: Address): Promise; + + /** + * Pledges support for a dispute. + * + * @param _request The request data for the dispute. + * @param _dispute The dispute data. + * @returns A promise that resolves when the pledge is made. + */ + pledgeForDispute( + _request: Request["prophetData"], + _dispute: Dispute["prophetData"], + ): Promise; + + /** + * Pledges against a dispute. + * + * @param _request The request data for the dispute. + * @param _dispute The dispute data. + * @returns A promise that resolves when the pledge is made. + */ + pledgeAgainstDispute( + _request: Request["prophetData"], + _dispute: Dispute["prophetData"], + ): Promise; + + /** + * Settles a dispute by finalizing the response. + * + * @param _request The request data. + * @param _response The response data. + * @param _dispute The dispute data. + * @returns A promise that resolves when the dispute is settled. + */ + settleDispute( + _request: Request["prophetData"], + _response: Response["prophetData"], + _dispute: Dispute["prophetData"], + ): Promise; + + /** + * Escalates a dispute to a higher authority or layer. + * + * @param _request The request data. + * @param _response The response data. + * @param _dispute The dispute data. + * @returns A promise that resolves when the dispute is escalated. + */ + escalateDispute( + _request: Request["prophetData"], + _response: Response["prophetData"], + _dispute: Dispute["prophetData"], + ): Promise; + + /** + * Finalizes a request after the response and dispute resolution are complete. + * + * @param _request The request data. + * @param _response The response data. + * @returns A promise that resolves when the request is finalized. + */ + finalize(_request: Request["prophetData"], _response: Response["prophetData"]): Promise; +} + +/** + * IProtocolProvider defines the interface for a protocol provider that includes both read and write operations. + */ +export interface IProtocolProvider { + /** + * The write operations available on the protocol. + */ + write: IWriteProvider; + + /** + * The read operations available on the protocol. + */ + read: IReadProvider; +} diff --git a/packages/automated-dispute/src/protocolProvider.ts b/packages/automated-dispute/src/protocolProvider.ts index 68608ea..90b96c6 100644 --- a/packages/automated-dispute/src/protocolProvider.ts +++ b/packages/automated-dispute/src/protocolProvider.ts @@ -2,29 +2,56 @@ import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js"; import { Timestamp } from "@ebo-agent/shared"; import { Address, + BaseError, + ContractFunctionRevertedError, createPublicClient, + createWalletClient, fallback, FallbackTransport, getContract, GetContractReturnType, + Hex, http, HttpTransport, PublicClient, + WalletClient, } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; import { arbitrum } from "viem/chains"; import type { EboEvent, EboEventName } from "./types/events.js"; import type { Dispute, Request, Response } from "./types/prophet.js"; -import { epochManagerAbi, oracleAbi } from "./abis/index.js"; +import { eboRequestCreatorAbi, epochManagerAbi, oracleAbi } from "./abis/index.js"; import { RpcUrlsEmpty } from "./exceptions/rpcUrlsEmpty.exception.js"; -import { ProtocolContractsAddresses } from "./types/protocolProvider.js"; +import { + IProtocolProvider, + IReadProvider, + IWriteProvider, + ProtocolContractsAddresses, +} from "./interfaces/index.js"; +import { ErrorFactory } from "./services/errorFactory.js"; + +// TODO: these constants should be env vars +const TRANSACTION_RECEIPT_CONFIRMATIONS = 1; +const TIMEOUT = 10000; +const RETRY_INTERVAL = 150; -export class ProtocolProvider { - private client: PublicClient>; - private oracleContract: GetContractReturnType; +export class ProtocolProvider implements IProtocolProvider { + private readClient: PublicClient>; + private writeClient: WalletClient>; + private oracleContract: GetContractReturnType< + typeof oracleAbi, + typeof this.writeClient, + Address + >; private epochManagerContract: GetContractReturnType< typeof epochManagerAbi, - typeof this.client, + typeof this.readClient, + Address + >; + private eboRequestCreatorContract: GetContractReturnType< + typeof eboRequestCreatorAbi, + typeof this.writeClient, Address >; @@ -32,28 +59,80 @@ export class ProtocolProvider { * Creates a new ProtocolProvider instance * @param rpcUrls The RPC URLs to connect to the Arbitrum chain * @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 */ - constructor(rpcUrls: string[], contracts: ProtocolContractsAddresses) { + constructor(rpcUrls: string[], contracts: ProtocolContractsAddresses, privateKey: Hex) { if (rpcUrls.length === 0) { throw new RpcUrlsEmpty(); } - this.client = createPublicClient({ + + this.readClient = createPublicClient({ chain: arbitrum, - transport: fallback(rpcUrls.map((url) => http(url))), + transport: fallback( + rpcUrls.map((url) => + http(url, { + timeout: TIMEOUT, + retryDelay: RETRY_INTERVAL, + }), + ), + ), }); + + const account = privateKeyToAccount(privateKey); + + this.writeClient = createWalletClient({ + chain: arbitrum, + transport: fallback( + rpcUrls.map((url) => + http(url, { + timeout: TIMEOUT, + retryDelay: RETRY_INTERVAL, + }), + ), + ), + account: account, + }); + // Instantiate all the protocol contracts this.oracleContract = getContract({ address: contracts.oracle, abi: oracleAbi, - client: this.client, + client: this.writeClient, }); this.epochManagerContract = getContract({ address: contracts.epochManager, abi: epochManagerAbi, - client: this.client, + client: this.readClient, + }); + this.eboRequestCreatorContract = getContract({ + address: contracts.eboRequestCreator, + abi: eboRequestCreatorAbi, + client: { + public: this.readClient, + wallet: this.writeClient, + }, }); } + public write: IWriteProvider = { + createRequest: this.createRequest.bind(this), + proposeResponse: this.proposeResponse.bind(this), + disputeResponse: this.disputeResponse.bind(this), + pledgeForDispute: this.pledgeForDispute.bind(this), + pledgeAgainstDispute: this.pledgeAgainstDispute.bind(this), + settleDispute: this.settleDispute.bind(this), + escalateDispute: this.escalateDispute.bind(this), + finalize: this.finalize.bind(this), + }; + + public read: IReadProvider = { + getCurrentEpoch: this.getCurrentEpoch.bind(this), + getLastFinalizedBlock: this.getLastFinalizedBlock.bind(this), + getEvents: this.getEvents.bind(this), + hasStakedAssets: this.hasStakedAssets.bind(this), + getAvailableChains: this.getAvailableChains.bind(this), + }; + /** * Gets the current epoch, the block number and its timestamp of the current epoch * @@ -69,7 +148,7 @@ export class ProtocolProvider { this.epochManagerContract.read.currentEpochBlock(), ]); - const currentEpochBlock = await this.client.getBlock({ + const currentEpochBlock = await this.readClient.getBlock({ blockNumber: currentEpochBlockNumber, }); @@ -81,7 +160,7 @@ export class ProtocolProvider { } async getLastFinalizedBlock(): Promise { - const { number } = await this.client.getBlock({ blockTag: "finalized" }); + const { number } = await this.readClient.getBlock({ blockTag: "finalized" }); return number; } @@ -169,10 +248,60 @@ export class ProtocolProvider { } // TODO: waiting for ChainId to be merged for _chains parameter - async createRequest(_epoch: bigint, _chains: string[]): Promise { - // TODO: implement actual method + /** + * Creates a request on the EBO Request Creator contract by simulating the transaction + * and then executing it if the simulation is successful. + * + * This function first simulates the `createRequests` call on the EBO Request Creator contract + * to validate that the transaction will succeed. If the simulation is successful, the transaction + * is executed by the `writeContract` method of the wallet client. The function also handles any + * potential errors that may occur during the simulation or transaction execution. + * + * @param {bigint} epoch - The epoch for which the request is being created. + * @param {string[]} chains - An array of chain identifiers where the request should be created. + * @throws {Error} Throws an error if the chains array is empty or if the transaction fails. + * @throws {EBORequestCreator_InvalidEpoch} Throws if the epoch is invalid. + * @throws {Oracle_InvalidRequestBody} Throws if the request body is invalid. + * @throws {EBORequestModule_InvalidRequester} Throws if the requester is invalid. + * @throws {EBORequestCreator_ChainNotAdded} Throws if the specified chain is not added. + * @returns {Promise} A promise that resolves when the request is successfully created. + */ + async createRequest(epoch: bigint, chains: string[]): Promise { + if (chains.length === 0) { + throw new Error("Chains array cannot be empty"); + } - return; + try { + const { request } = await this.readClient.simulateContract({ + address: this.eboRequestCreatorContract.address, + abi: eboRequestCreatorAbi, + functionName: "createRequests", + args: [epoch, chains], + account: this.writeClient.account, + }); + + const hash = await this.writeClient.writeContract(request); + + const receipt = await this.readClient.waitForTransactionReceipt({ + hash, + confirmations: TRANSACTION_RECEIPT_CONFIRMATIONS, + }); + + if (receipt.status !== "success") { + throw new Error("Transaction failed"); + } + } catch (error) { + if (error instanceof BaseError) { + const revertError = error.walk( + (err) => err instanceof ContractFunctionRevertedError, + ); + if (revertError instanceof ContractFunctionRevertedError) { + const errorName = revertError.data?.errorName ?? ""; + throw ErrorFactory.createError(errorName); + } + } + throw error; + } } async proposeResponse( diff --git a/packages/automated-dispute/src/services/errorFactory.ts b/packages/automated-dispute/src/services/errorFactory.ts new file mode 100644 index 0000000..37fd125 --- /dev/null +++ b/packages/automated-dispute/src/services/errorFactory.ts @@ -0,0 +1,33 @@ +import { EBORequestCreator_ChainNotAdded } from "../exceptions/chainNotAdded.exception.js"; +import { EBORequestCreator_InvalidEpoch } from "../exceptions/invalidEpoch.exception.js"; +import { Oracle_InvalidRequestBody } from "../exceptions/invalidRequestBody.exception.js"; +import { EBORequestModule_InvalidRequester } from "../exceptions/invalidRequester.exception.js"; + +/** + * A factory class for creating specific error instances based on the provided error name. + */ +export class ErrorFactory { + /** + * Creates an instance of a specific error class based on the provided error name. + * + * @param {string} errorName - The name of the error to create. + * @returns {Error} An instance of the corresponding error class. + * @throws {Error} If the provided error name is unknown. + */ + public static createError(errorName: string): Error { + // TODO: need to define structure of each error + // TODO: Need to define some base contract reverted error to distinguish from other errors + switch (errorName) { + case "EBORequestCreator_InvalidEpoch": + return new EBORequestCreator_InvalidEpoch(); + case "Oracle_InvalidRequestBody": + return new Oracle_InvalidRequestBody(); + case "EBORequestModule_InvalidRequester": + return new EBORequestModule_InvalidRequester(); + case "EBORequestCreator_ChainNotAdded": + return new EBORequestCreator_ChainNotAdded(); + default: + return new Error(`Unknown error: ${errorName}`); + } + } +} diff --git a/packages/automated-dispute/src/types/index.ts b/packages/automated-dispute/src/types/index.ts index f1e142e..518c0ca 100644 --- a/packages/automated-dispute/src/types/index.ts +++ b/packages/automated-dispute/src/types/index.ts @@ -1,3 +1,3 @@ export * from "./events.js"; -export * from "./protocolProvider.js"; +export * from "../interfaces/protocolProvider.js"; export * from "./prophet.js"; diff --git a/packages/automated-dispute/src/types/protocolProvider.ts b/packages/automated-dispute/src/types/protocolProvider.ts deleted file mode 100644 index a4387ee..0000000 --- a/packages/automated-dispute/src/types/protocolProvider.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Address } from "viem"; - -import { ProtocolContractsNames } from "../constants.js"; - -export type ProtocolContract = (typeof ProtocolContractsNames)[number]; -export type ProtocolContractsAddresses = Record; diff --git a/packages/automated-dispute/tests/eboActor/fixtures.ts b/packages/automated-dispute/tests/eboActor/fixtures.ts index e6b3e80..e7c7ab7 100644 --- a/packages/automated-dispute/tests/eboActor/fixtures.ts +++ b/packages/automated-dispute/tests/eboActor/fixtures.ts @@ -2,9 +2,13 @@ import { Address } from "viem"; import { Request, RequestId } from "../../src/types/prophet"; +export const mockedPrivateKey = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + export const DEFAULT_MOCKED_PROTOCOL_CONTRACTS = { oracle: "0x123456" as Address, epochManager: "0x654321" as Address, + eboRequestCreator: "0xabcdef" as Address, }; export const DEFAULT_MOCKED_REQUEST_CREATED_DATA: Request = { diff --git a/packages/automated-dispute/tests/eboActorsManager.spec.ts b/packages/automated-dispute/tests/eboActorsManager.spec.ts index 7760bad..55b9265 100644 --- a/packages/automated-dispute/tests/eboActorsManager.spec.ts +++ b/packages/automated-dispute/tests/eboActorsManager.spec.ts @@ -10,6 +10,7 @@ import { ProtocolProvider } from "../src/protocolProvider.js"; import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, DEFAULT_MOCKED_REQUEST_CREATED_DATA, + mockedPrivateKey, } from "./eboActor/fixtures.js"; import mocks from "./mocks/index.js"; @@ -31,6 +32,7 @@ describe("EboActorsManager", () => { protocolProvider = new ProtocolProvider( protocolProviderRpcUrls, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, + mockedPrivateKey, ); const blockNumberRpcUrls = new Map([ diff --git a/packages/automated-dispute/tests/errorFactory.spec.ts b/packages/automated-dispute/tests/errorFactory.spec.ts new file mode 100644 index 0000000..b82bbe7 --- /dev/null +++ b/packages/automated-dispute/tests/errorFactory.spec.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from "vitest"; + +import { EBORequestCreator_ChainNotAdded } from "../src/exceptions/chainNotAdded.exception.js"; +import { EBORequestCreator_InvalidEpoch } from "../src/exceptions/invalidEpoch.exception.js"; +import { Oracle_InvalidRequestBody } from "../src/exceptions/invalidRequestBody.exception.js"; +import { EBORequestModule_InvalidRequester } from "../src/exceptions/invalidRequester.exception.js"; +import { ErrorFactory } from "../src/services/errorFactory.js"; + +describe("ErrorFactory", () => { + it("creates EBORequestCreator_InvalidEpoch error", () => { + const error = ErrorFactory.createError("EBORequestCreator_InvalidEpoch"); + expect(error).toBeInstanceOf(EBORequestCreator_InvalidEpoch); + }); + + it("creates Oracle_InvalidRequestBody error", () => { + const error = ErrorFactory.createError("Oracle_InvalidRequestBody"); + expect(error).toBeInstanceOf(Oracle_InvalidRequestBody); + }); + + it("creates EBORequestModule_InvalidRequester error", () => { + const error = ErrorFactory.createError("EBORequestModule_InvalidRequester"); + expect(error).toBeInstanceOf(EBORequestModule_InvalidRequester); + }); + + it("creates EBORequestCreator_ChainNotAdded error", () => { + const error = ErrorFactory.createError("EBORequestCreator_ChainNotAdded"); + expect(error).toBeInstanceOf(EBORequestCreator_ChainNotAdded); + }); + + it("creates generic Error for unknown error name", () => { + const error = ErrorFactory.createError("UnknownError"); + expect(error).toBeInstanceOf(Error); + expect(error.message).toBe("Unknown error: UnknownError"); + }); +}); diff --git a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts index 7bfb3a0..4238c06 100644 --- a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts @@ -7,7 +7,7 @@ import { EboActor } from "../../src/eboActor.js"; import { ProtocolProvider } from "../../src/protocolProvider.js"; import { EboMemoryRegistry } from "../../src/services/index.js"; import { Dispute, Request, Response } from "../../src/types/index.js"; -import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS } from "../eboActor/fixtures.js"; +import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey } from "../eboActor/fixtures.js"; /** * Builds a base `EboActor` scaffolded with all its dependencies. @@ -23,6 +23,7 @@ export function buildEboActor(request: Request, logger: ILogger) { const protocolProvider = new ProtocolProvider( protocolProviderRpcUrls, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, + mockedPrivateKey, ); const blockNumberRpcUrls = new Map([ diff --git a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts index e1191dd..277c26e 100644 --- a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts @@ -5,13 +5,14 @@ import { ILogger } from "@ebo-agent/shared"; import { EboActorsManager } from "../../src/eboActorsManager"; import { ProtocolProvider } from "../../src/protocolProvider"; import { EboProcessor } from "../../src/services"; -import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS } from "../eboActor/fixtures"; +import { DEFAULT_MOCKED_PROTOCOL_CONTRACTS, mockedPrivateKey } from "../eboActor/fixtures"; export function buildEboProcessor(logger: ILogger) { const protocolProviderRpcUrls = ["http://localhost:8538"]; const protocolProvider = new ProtocolProvider( protocolProviderRpcUrls, DEFAULT_MOCKED_PROTOCOL_CONTRACTS, + mockedPrivateKey, ); const blockNumberRpcUrls = new Map([ diff --git a/packages/automated-dispute/tests/protocolProvider.spec.ts b/packages/automated-dispute/tests/protocolProvider.spec.ts index 337c649..083d794 100644 --- a/packages/automated-dispute/tests/protocolProvider.spec.ts +++ b/packages/automated-dispute/tests/protocolProvider.spec.ts @@ -1,20 +1,23 @@ -import { createPublicClient, fallback, getContract, http } from "viem"; +import { createPublicClient, createWalletClient, fallback, getContract, http } from "viem"; import { arbitrum } from "viem/chains"; import { afterEach, beforeEach, describe, expect, it, Mock, vi } from "vitest"; +import { eboRequestCreatorAbi } from "../src/abis/eboRequestCreator.js"; import { epochManagerAbi } from "../src/abis/epochManager.js"; import { oracleAbi } from "../src/abis/oracle.js"; import { RpcUrlsEmpty } from "../src/exceptions/rpcUrlsEmpty.exception.js"; import { ProtocolProvider } from "../src/index.js"; import { ProtocolContractsAddresses } from "../src/types/index.js"; +import { mockedPrivateKey } from "./eboActor/fixtures.js"; vi.mock("viem", async () => { const actual = await vi.importActual("viem"); return { ...actual, - http: vi.fn(), - fallback: vi.fn(), + http: vi.fn((url, options) => ({ url, ...options })), + fallback: vi.fn((transports) => transports), createPublicClient: vi.fn(), + createWalletClient: vi.fn(), getContract: vi.fn(), }; }); @@ -24,13 +27,15 @@ describe("ProtocolProvider", () => { const mockContractAddress: ProtocolContractsAddresses = { oracle: "0x1234567890123456789012345678901234567890", epochManager: "0x1234567890123456789012345678901234567890", + eboRequestCreator: "0x1234567890123456789012345678901234567890", }; + beforeEach(() => { (getContract as Mock).mockImplementation(({ address, abi }) => { - if (abi == oracleAbi && address == mockContractAddress.oracle) { + if (abi === oracleAbi && address === mockContractAddress.oracle) { return {}; } - if (abi == epochManagerAbi && address == mockContractAddress.epochManager) { + if (abi === epochManagerAbi && address === mockContractAddress.epochManager) { return { read: { currentEpoch: vi.fn(), @@ -38,8 +43,34 @@ describe("ProtocolProvider", () => { }, }; } + if (abi === eboRequestCreatorAbi && address === mockContractAddress.eboRequestCreator) { + return { + simulate: { + createRequests: vi.fn(), + }, + write: { + createRequests: vi.fn(), + }, + }; + } throw new Error("Invalid contract address or ABI"); }); + + (createPublicClient as Mock).mockImplementation(() => ({ + simulateContract: vi.fn().mockResolvedValue({ + request: { + functionName: "createRequests", + args: [], + }, + }), + getBlock: vi.fn(), + waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: "success" }), + })); + + (createWalletClient as Mock).mockReturnValue({ + writeContract: vi.fn().mockResolvedValue("0xmockedTransactionHash"), + }); + (http as Mock).mockImplementation((url) => url); (fallback as Mock).mockImplementation((transports) => transports); }); @@ -50,27 +81,64 @@ describe("ProtocolProvider", () => { describe("constructor", () => { it("creates a new ProtocolProvider instance successfully", () => { - const protocolProvider = new ProtocolProvider(mockRpcUrls, mockContractAddress); + const protocolProvider = new ProtocolProvider( + mockRpcUrls, + mockContractAddress, + mockedPrivateKey, + ); expect(createPublicClient).toHaveBeenCalledWith({ chain: arbitrum, - transport: fallback(mockRpcUrls.map((url) => http(url))), + transport: fallback( + mockRpcUrls.map((url) => + http(url, { + timeout: protocolProvider["TIMEOUT"], + retryDelay: protocolProvider["RETRY_INTERVAL"], + }), + ), + ), }); + + expect(createWalletClient).toHaveBeenCalledWith({ + chain: arbitrum, + transport: fallback( + mockRpcUrls.map((url) => + http(url, { + timeout: protocolProvider["TIMEOUT"], + retryDelay: protocolProvider["RETRY_INTERVAL"], + }), + ), + ), + account: expect.objectContaining({ + address: expect.any(String), + publicKey: expect.any(String), + signMessage: expect.any(Function), + signTransaction: expect.any(Function), + signTypedData: expect.any(Function), + source: "privateKey", + type: "local", + }), + }); + expect(getContract).toHaveBeenCalledWith({ address: mockContractAddress.oracle, abi: oracleAbi, - client: protocolProvider["client"], + client: protocolProvider["writeClient"], }); + expect(getContract).toHaveBeenCalledWith({ address: mockContractAddress.epochManager, abi: epochManagerAbi, - client: protocolProvider["client"], + client: protocolProvider["readClient"], }); }); it("throws if rpcUrls are empty", () => { - expect(() => new ProtocolProvider([], mockContractAddress)).toThrowError(RpcUrlsEmpty); + expect( + () => new ProtocolProvider([], mockContractAddress, mockedPrivateKey), + ).toThrowError(RpcUrlsEmpty); }); }); + describe("getCurrentEpoch", () => { it("returns currentEpoch and currentEpochBlock successfully", async () => { const mockEpoch = BigInt(1); @@ -81,7 +149,11 @@ describe("ProtocolProvider", () => { getBlock: vi.fn().mockResolvedValue({ timestamp: mockEpochTimestamp }), }); - const protocolProvider = new ProtocolProvider(mockRpcUrls, mockContractAddress); + const protocolProvider = new ProtocolProvider( + mockRpcUrls, + mockContractAddress, + mockedPrivateKey, + ); (protocolProvider["epochManagerContract"].read.currentEpoch as Mock).mockResolvedValue( mockEpoch, @@ -95,9 +167,14 @@ describe("ProtocolProvider", () => { expect(result.currentEpoch).toBe(mockEpoch); expect(result.currentEpochBlockNumber).toBe(mockEpochBlock); + expect(result.currentEpochTimestamp).toBe(mockEpochTimestamp); }); it("throws when current epoch request fails", async () => { - const protocolProvider = new ProtocolProvider(mockRpcUrls, mockContractAddress); + const protocolProvider = new ProtocolProvider( + mockRpcUrls, + mockContractAddress, + mockedPrivateKey, + ); const error = new Error("Failed to get current epoch"); const mockEpochBlock = BigInt(12345); @@ -110,8 +187,13 @@ describe("ProtocolProvider", () => { await expect(protocolProvider.getCurrentEpoch()).rejects.toThrow(error); }); + it("throws when current epoch block request fails", async () => { - const protocolProvider = new ProtocolProvider(mockRpcUrls, mockContractAddress); + const protocolProvider = new ProtocolProvider( + mockRpcUrls, + mockContractAddress, + mockedPrivateKey, + ); const error = new Error("Failed to get current epoch block"); const mockEpoch = BigInt(12345); @@ -178,4 +260,62 @@ describe("ProtocolProvider", () => { it("returns if the RPC client finalizes the pledge"); it("throws if the RPC client fails"); }); + + describe("createRequest", () => { + const mockRpcUrls = ["http://localhost:8545"]; + const mockContractAddress: ProtocolContractsAddresses = { + oracle: "0x1234567890123456789012345678901234567890", + epochManager: "0x1234567890123456789012345678901234567890", + eboRequestCreator: "0x1234567890123456789012345678901234567890", + }; + + it("creates a request successfully", async () => { + const protocolProvider = new ProtocolProvider( + mockRpcUrls, + mockContractAddress, + mockedPrivateKey, + ); + + const mockEpoch = 1n; + const mockChains = ["eip155:1", "eip155:42161"]; + + const mockWriteContractResponse = "0xmockedTransactionHash"; + (protocolProvider["writeClient"].writeContract as Mock).mockResolvedValue( + mockWriteContractResponse, + ); + + await protocolProvider.createRequest(mockEpoch, mockChains); + + expect(protocolProvider["readClient"].simulateContract).toHaveBeenCalledWith({ + address: undefined, + abi: eboRequestCreatorAbi, + functionName: "createRequests", + args: [mockEpoch, mockChains], + account: undefined, + }); + + expect(protocolProvider["writeClient"].writeContract).toHaveBeenCalledWith( + expect.objectContaining({ + functionName: "createRequests", + args: [], + }), + ); + }); + + it("throws if chains array is empty", async () => { + const protocolProvider = new ProtocolProvider( + ["http://localhost:8545"], + { + oracle: "0x1234567890123456789012345678901234567890", + epochManager: "0x1234567890123456789012345678901234567890", + eboRequestCreator: "0x1234567890123456789012345678901234567890", + }, + mockedPrivateKey, + ); + + await expect(protocolProvider.createRequest(1n, [])).rejects.toThrow( + "Chains array cannot be empty", + ); + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dada3bd..c07e656 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,12 @@ importers: "@commitlint/config-conventional": specifier: 19.2.2 version: 19.2.2 + "@defi-wonderland/prophet-core": + specifier: 0.0.0-5e1cf557 + version: 0.0.0-5e1cf557 + "@defi-wonderland/prophet-modules": + specifier: 0.0.0-e52f8cce + version: 0.0.0-e52f8cce "@ianvs/prettier-plugin-sort-imports": specifier: 4.3.1 version: 4.3.1(prettier@3.3.3) @@ -378,6 +384,24 @@ packages: integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==, } + "@defi-wonderland/prophet-core@0.0.0-2e39539b": + resolution: + { + integrity: sha512-EdYpDEO1XeO08uQikhOQ6NzG0LWYxANFk272j4vCyLSJ8kRyJNMv69JJCLcq5kV0B9IzXybmqjreemkZ05z3kQ==, + } + + "@defi-wonderland/prophet-core@0.0.0-5e1cf557": + resolution: + { + integrity: sha512-NDNyAnw/qUtoKInfwG12LSy3z1UkXilxwtup6VFMsYZeC+OIKFFslVDaG7QSuuWU1Z5o4Zr5oNDq1N30rnEVMw==, + } + + "@defi-wonderland/prophet-modules@0.0.0-e52f8cce": + resolution: + { + integrity: sha512-zNx602Z/GuisTqi120JLgoWcs3wiMKDYvnXJ/6xdYUCNNBQKk2GyCmS6M5Hu1vTbSfDlNRrugy1cDOkG5sSGdg==, + } + "@esbuild/aix-ppc64@0.21.5": resolution: { @@ -736,6 +760,12 @@ packages: } engines: { node: ">= 8" } + "@openzeppelin/contracts@4.9.5": + resolution: + { + integrity: sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg==, + } + "@pkgjs/parseargs@0.11.0": resolution: { @@ -2875,6 +2905,13 @@ packages: } engines: { node: ">=18" } + solmate@https://codeload.github.com/transmissions11/solmate/tar.gz/bfc9c25865a274a7827fea5abf6e4fb64fc64e6c: + resolution: + { + tarball: https://codeload.github.com/transmissions11/solmate/tar.gz/bfc9c25865a274a7827fea5abf6e4fb64fc64e6c, + } + version: 6.1.0 + source-map-js@1.2.0: resolution: { @@ -3691,6 +3728,16 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 + "@defi-wonderland/prophet-core@0.0.0-2e39539b": {} + + "@defi-wonderland/prophet-core@0.0.0-5e1cf557": {} + + "@defi-wonderland/prophet-modules@0.0.0-e52f8cce": + dependencies: + "@defi-wonderland/prophet-core": 0.0.0-2e39539b + "@openzeppelin/contracts": 4.9.5 + solmate: https://codeload.github.com/transmissions11/solmate/tar.gz/bfc9c25865a274a7827fea5abf6e4fb64fc64e6c + "@esbuild/aix-ppc64@0.21.5": optional: true @@ -3858,6 +3905,8 @@ snapshots: "@nodelib/fs.scandir": 2.1.5 fastq: 1.17.1 + "@openzeppelin/contracts@4.9.5": {} + "@pkgjs/parseargs@0.11.0": optional: true @@ -5051,6 +5100,9 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + solmate@https://codeload.github.com/transmissions11/solmate/tar.gz/bfc9c25865a274a7827fea5abf6e4fb64fc64e6c: + {} + source-map-js@1.2.0: {} split2@4.2.0: {}