diff --git a/kleros-sdk/README.md b/kleros-sdk/README.md index 28d807b82..7813f0d34 100644 --- a/kleros-sdk/README.md +++ b/kleros-sdk/README.md @@ -1,11 +1,164 @@ # @kleros/kleros-sdk -_Archon's successor_ +**The official TypeScript SDK for interacting with the Kleros V2 protocol.** -To run the data mappings tests, at the root folder level, do: +_This SDK is the successor to Archon and provides developers with a comprehensive set of tools to build applications on top of Kleros._ + +## Table of Contents + +- [Features](#features) +- [Installation](#installation) +- [Getting Started](#getting-started) + - [Configuration](#configuration) + - [Quick Start Example](#quick-start-example) +- [Core Concepts](#core-concepts) + - [Public Client](#public-client) + - [Data Mappings](#data-mappings) + - [Requests](#requests) +- [API Documentation](#api-documentation) +- [Examples](#examples) +- [Contributing](#contributing) +- [License](#license) + +## Features + +* **Viem Integration**: Leverages the power and efficiency of [Viem](httpsa://viem.sh/) for Ethereum blockchain interactions. +* **Type-Safe**: Fully written in TypeScript for robust type checking and improved developer experience. +* **Dispute Resolution**: Tools to fetch dispute details, and interact with the Kleros arbitration process. +* **Data Handling**: Utilities for working with Kleros-specific data structures and evidence. +* **Subgraph Interaction**: Functionality to query Kleros subgraphs for indexed data. +* **IPFS Support**: Helpers for fetching data stored on IPFS. + +## Installation + +You can install the Kleros SDK using npm or yarn: ```bash +# Using npm +npm install @kleros/kleros-sdk viem + +# Using yarn +yarn add @kleros/kleros-sdk viem +``` + +**Note:** `@kleros/kleros-sdk` has `viem` as a peer dependency, so you need to install it separately in your project. + +## Getting Started + +### Configuration + +Before you can use the SDK, you need to configure it with a `viem` Public Client instance. This client will be used for all blockchain interactions. + +```typescript +import { configureSDK } from '@kleros/kleros-sdk'; +import { createPublicClient, http } from 'viem'; +import { mainnet } from 'viem/chains'; // Or your desired chain + +// Create a viem public client +const publicClient = createPublicClient({ + chain: mainnet, + transport: http(), // Replace with your preferred transport (e.g., Infura, Alchemy) +}); + +// Configure the Kleros SDK +configureSDK({ client: publicClient }); + +console.log('Kleros SDK configured!'); +``` + +### Quick Start Example + +Here's a simple example of how to fetch details for a specific dispute: + +```typescript +import { configureSDK, KlerosSDK } from '@kleros/kleros-sdk'; // Assuming KlerosSDK is the main class or namespace +import { createPublicClient, http } from 'viem'; +import { mainnet } from 'viem/chains'; + +// Configure the SDK (as shown above) +const publicClient = createPublicClient({ + chain: mainnet, + transport: http(), +}); +configureSDK({ client: publicClient }); + +// Example: Fetching dispute details (Illustrative - actual API might differ) +// Replace with actual SDK usage once API is fully explored +async function getDisputeExample(disputeId: string) { + try { + // Placeholder: Actual function to get dispute details needs to be identified + // For now, we'll assume a function getDisputeDetails exists or similar + // const disputeDetails = await KlerosSDK.getDisputeDetails(disputeId); + // console.log('Dispute Details:', disputeDetails); + + console.log(`Fetching details for dispute ${disputeId}... (Illustrative)`); + // This part will be updated once the actual API for fetching disputes is clear. + // For now, we refer to functions that might exist based on file names like 'getDispute.ts' or 'fetchDisputeDetails.ts' + // For example, if 'getDispute' is the correct function: + // import { getDispute } from '@kleros/kleros-sdk'; + // const dispute = await getDispute(disputeId, publicClient); // publicClient might be implicitly used or passed + // console.log(dispute); + + + } catch (error) { + console.error('Error fetching dispute details:', error); + } +} + +getDisputeExample('123'); // Replace '123' with an actual dispute ID +``` +*Note: The Quick Start example is illustrative. The exact API usage, especially for fetching dispute details, will be refined as the SDK's public API is further clarified in subsequent steps.* + +## Core Concepts + +### Public Client + +The SDK uses a `viem` Public Client for all on-chain interactions. You must provide this client during the SDK's configuration. This design gives you full control over the Ethereum connection (e.g., choice of RPC provider, chain). + +### Data Mappings + +The `dataMappings` module (found in `src/dataMappings`) is a powerful feature that allows for complex data retrieval and processing. It can execute a series of actions, such as: +* Calling smart contract functions +* Fetching JSON data from IPFS +* Querying Kleros subgraphs +It uses a template-based system to populate data structures based on the results of these actions. This is particularly useful for constructing the `metaEvidence` and `evidence` associated with disputes. + +### Requests + +The `requests` module (found in `src/requests`) provides functions for making specific queries, often to Kleros subgraphs via GraphQL. For example, `fetchDisputeDetails.ts` likely uses this module to retrieve detailed information about a dispute. + +## API Documentation + +Detailed API documentation will be generated from TSDoc comments and made available separately. (This will be addressed in Step 2 of the improvement plan). + +For now, developers can explore the exported functions and types directly within their IDEs, leveraging TypeScript's autocompletion features. + +## Examples + +Additional runnable examples demonstrating various SDK features will be added to the `examples/` directory within this package. (This will be addressed in a later step of the improvement plan). + +## Contributing + +Contributions are welcome! If you find a bug, have a feature request, or want to contribute to the codebase, please: + +1. Check the [issue tracker](https://github.com/kleros/kleros-v2/issues) for existing issues. +2. Open a new issue if yours isn't listed. +3. For code contributions, please fork the repository and submit a pull request to the `master` (or relevant development) branch. + +We use ESLint for linting and Prettier for formatting. Please ensure your contributions adhere to these standards. + +### Running Tests + +To run the test suite (powered by Vitest): + +```bash +# From the root of the kleros-v2 monorepo +yarn test packages/kleros-sdk + +# Or, if you are inside the kleros-sdk package directory yarn test ``` -🚧 ⚖️ 🚧 +## License + +This SDK is licensed under the [MIT License](./LICENSE). diff --git a/kleros-sdk/src/requests/fetchDisputeDetails.ts b/kleros-sdk/src/requests/fetchDisputeDetails.ts index adb58c3f8..f64f69cba 100644 --- a/kleros-sdk/src/requests/fetchDisputeDetails.ts +++ b/kleros-sdk/src/requests/fetchDisputeDetails.ts @@ -1,19 +1,31 @@ -import { RequestError } from "../errors"; -import { CombinedError, gql } from "@urql/core"; +import { RequestError, KlerosSDKError } from "../errors"; // Assuming KlerosSDKError as a base +import { CombinedError, gql, type TypedDocumentNode } from "@urql/core"; import getClient from "./gqlClient"; +/** + * Represents the structure of the response for a dispute details query. + * @internal + */ type DisputeDetailsQueryResponse = { dispute: { + /** The address of the arbitrable contract involved in the dispute. */ arbitrated: { id: string; }; + /** The chain ID where the arbitrable contract resides. */ arbitrableChainId: number; + /** The identifier of the dispute within the context of the arbitrable contract. */ externalDisputeId: number; + /** The identifier of the dispute template associated with this dispute. */ templateId: number; - }; + } | null; // Dispute can be null if not found }; -const query = gql` +/** + * GraphQL query to fetch core details of a dispute from a Kleros Core subgraph. + * @internal + */ +const query: TypedDocumentNode = gql` query DisputeDetails($id: ID!) { dispute(id: $id) { arbitrated { @@ -26,28 +38,63 @@ const query = gql` } `; -const fetchDisputeDetails = async (endpoint: string, id: bigint) => { +/** + * Fetches core details of a dispute from a specified Kleros Core subgraph. + * This function is intended for internal use by the SDK, typically called by higher-level + * functions like `getDispute`. + * + * @param {string} endpoint - The GraphQL endpoint URL of the Kleros Core subgraph. + * @param {bigint} id - The unique identifier of the dispute (as a BigInt). + * @returns {Promise} A promise that resolves to the dispute details + * from the subgraph. Returns `undefined` if the + * query is successful but yields no data. + * @throws {CombinedError} If Urql client encounters a GraphQL error (e.g., network issues, invalid query). + * This error object can contain multiple GraphQL errors. + * @throws {RequestError} If there's a non-GraphQL error during the request or if the response + * contains an error explicitly thrown by the promise chain. + * @throws {KlerosSDKError} For other unexpected errors during the execution. + * + * @example + * // Internal SDK usage: + * // const disputeId = 123n; + * // const coreSubgraphUrl = 'https://your-core-subgraph-url.com/graphql'; + * // try { + * // const details = await fetchDisputeDetails(coreSubgraphUrl, disputeId); + * // if (details?.dispute) { + * // console.log('Arbitrated contract:', details.dispute.arbitrated.id); + * // } else { + * // console.log('Dispute not found.'); + * // } + * // } catch (error) { + * // console.error('Failed to fetch dispute details:', error); + * // } + */ +const fetchDisputeDetails = async (endpoint: string, id: bigint): Promise => { const variables = { id: id.toString() }; try { const client = getClient(endpoint); - return client - .query(query, variables) - .toPromise() - .then((res) => { - if (res?.error) { - throw res.error; - } - return res?.data; - }); + const result = await client + .query(query, variables) + .toPromise(); + + if (result.error) { + // Let CombinedError be caught by the catch block for uniform error handling + throw result.error; + } + return result.data; } catch (error: unknown) { if (error instanceof CombinedError) { + // Re-throw to allow specific handling upstream if needed, or wrap if preferred throw error; } else if (error instanceof Error) { - throw new RequestError(`Error querying Dispute Details: ${error.message}`, endpoint); + // Wrap other errors in RequestError for consistent SDK error types + throw new RequestError(`Error querying Dispute Details for ID ${id}: ${error.message}`, endpoint, { cause: error }); } - throw new RequestError("An unknown error occurred while querying Dispute Details", endpoint); + // Fallback for unknown error types + throw new KlerosSDKError(`An unknown error occurred while querying Dispute Details for ID ${id}`, { cause: error }); } }; export default fetchDisputeDetails; +``` diff --git a/kleros-sdk/src/requests/fetchDisputeTemplateFromId.ts b/kleros-sdk/src/requests/fetchDisputeTemplateFromId.ts index acc57a3f3..ed2754603 100644 --- a/kleros-sdk/src/requests/fetchDisputeTemplateFromId.ts +++ b/kleros-sdk/src/requests/fetchDisputeTemplateFromId.ts @@ -1,14 +1,30 @@ -import { CombinedError, gql } from "@urql/core"; -import { RequestError } from "../errors"; +import { CombinedError, gql, type TypedDocumentNode } from "@urql/core"; +import { RequestError, KlerosSDKError } from "../errors"; // Assuming KlerosSDKError as a base import getClient from "./gqlClient"; +/** + * Represents the structure of the response for a dispute template query from the DTR subgraph. + * @internal + */ type DisputeTemplateQueryResponse = { disputeTemplate: { + /** + * The raw template data, typically a JSON string defining the structure and questions of the dispute. + */ templateData: string; + /** + * JSON string defining how to fetch and map additional data required by the template. + * This can include contract calls, IPFS fetches, or subgraph queries. + */ templateDataMappings: string; - }; + } | null; // disputeTemplate can be null if not found }; -const query = gql` + +/** + * GraphQL query to fetch a dispute template from a Dispute Template Registry (DTR) subgraph. + * @internal + */ +const query: TypedDocumentNode = gql` query DisputeTemplate($id: ID!) { disputeTemplate(id: $id) { templateData @@ -17,28 +33,64 @@ const query = gql` } `; -const fetchDisputeTemplateFromId = async (endpoint: string, id: number) => { +/** + * Fetches a dispute template from a specified Dispute Template Registry (DTR) subgraph + * based on its template ID. + * This function is intended for internal use by the SDK, primarily by `getDispute`. + * + * @param {string} endpoint - The GraphQL endpoint URL of the DTR subgraph. + * @param {number} id - The unique identifier of the dispute template. + * @returns {Promise} A promise that resolves to the dispute template data. + * Returns `undefined` if the query is successful but + * yields no data (e.g., template not found). + * @throws {CombinedError} If Urql client encounters a GraphQL error (e.g., network issues, invalid query). + * This error object can contain multiple GraphQL errors. + * @throws {RequestError} If there's a non-GraphQL error during the request or if the response + * contains an error explicitly thrown by the promise chain. + * @throws {KlerosSDKError} For other unexpected errors during the execution. + * + * @example + * // Internal SDK usage: + * // const templateId = 1; + * // const dtrSubgraphUrl = 'https://your-dtr-subgraph-url.com/graphql'; + * // try { + * // const templateResponse = await fetchDisputeTemplateFromId(dtrSubgraphUrl, templateId); + * // if (templateResponse?.disputeTemplate) { + * // console.log('Template Data:', templateResponse.disputeTemplate.templateData); + * // } else { + * // console.log('Dispute template not found.'); + * // } + * // } catch (error) { + * // console.error('Failed to fetch dispute template:', error); + * // } + */ +const fetchDisputeTemplateFromId = async (endpoint: string, id: number): Promise => { + // Convert ID to string for GraphQL variables, as GraphQL IDs are typically strings or numbers. const variables = { id: id.toString() }; try { const client = getClient(endpoint); - return client - .query(query, variables) - .toPromise() - .then((res) => { - if (res?.error) { - throw res.error; - } - return res?.data; - }); + const result = await client + .query(query, variables) + .toPromise(); + + if (result.error) { + // Let CombinedError be caught by the catch block for uniform error handling + throw result.error; + } + return result.data; } catch (error: unknown) { if (error instanceof CombinedError) { + // Re-throw to allow specific handling upstream if needed throw error; } else if (error instanceof Error) { - throw new RequestError(`Error querying Dispute Template: ${error.message}`, endpoint); + // Wrap other errors in RequestError for consistent SDK error types + throw new RequestError(`Error querying Dispute Template for ID ${id}: ${error.message}`, endpoint, { cause: error }); } - throw new RequestError("An unknown error occurred while querying Dispute Template", endpoint); + // Fallback for unknown error types + throw new KlerosSDKError(`An unknown error occurred while querying Dispute Template for ID ${id}`, { cause: error }); } }; export default fetchDisputeTemplateFromId; +``` diff --git a/kleros-sdk/src/requests/gqlClient.ts b/kleros-sdk/src/requests/gqlClient.ts index dcae74a31..a7f0fb066 100644 --- a/kleros-sdk/src/requests/gqlClient.ts +++ b/kleros-sdk/src/requests/gqlClient.ts @@ -1,14 +1,33 @@ -import { cacheExchange, Client, fetchExchange } from "@urql/core"; +import { cacheExchange, Client, fetchExchange, type Exchange } from "@urql/core"; +// A map to cache Urql Client instances by their endpoint URL. const clients = new Map(); -const getClient = (endpoint: string) => { +/** + * Retrieves or creates an Urql Client instance for a given GraphQL endpoint. + * Client instances are cached based on their endpoint URL to avoid redundant + * client creation and to leverage Urql's caching mechanisms more effectively. + * + * This function is intended for internal use within the SDK's request modules. + * + * @param {string} endpoint - The GraphQL endpoint URL for which to get the client. + * @returns {Client} An Urql Client instance configured for the specified endpoint. + * The client uses `cacheExchange` for caching query results and + * `fetchExchange` for making HTTP requests. + * + * @example + * // Internal usage: + * // import getClient from './gqlClient'; + * // const coreSubgraphClient = getClient('https://your-core-subgraph-endpoint.com/graphql'); + * // const result = await coreSubgraphClient.query(YOUR_QUERY, YOUR_VARIABLES).toPromise(); + */ +const getClient = (endpoint: string): Client => { let client = clients.get(endpoint); if (!client) { client = new Client({ url: endpoint, - exchanges: [cacheExchange, fetchExchange], + exchanges: [cacheExchange, fetchExchange].filter(Boolean) as Exchange[], // Ensure exchanges are correctly typed }); clients.set(endpoint, client); } diff --git a/kleros-sdk/src/sdk.ts b/kleros-sdk/src/sdk.ts index 00e9c2940..6fd043c6c 100644 --- a/kleros-sdk/src/sdk.ts +++ b/kleros-sdk/src/sdk.ts @@ -1,16 +1,62 @@ -import { createPublicClient, type PublicClient } from "viem"; +import { createPublicClient, type PublicClient, type Transport, type Chain } from "viem"; import { SdkConfig } from "./types"; import { SdkNotConfiguredError } from "./errors"; -let publicClient: PublicClient | undefined; +let publicClient: PublicClient | undefined; +/** + * Configures the Kleros SDK with necessary parameters, primarily the Viem Public Client. + * This function must be called before any other SDK functions that interact with the blockchain. + * + * @param {SdkConfig} config - The configuration object for the SDK. + * @param {PublicClient} config.client - An instance of Viem's PublicClient. + * This client is used for all blockchain interactions. + * It's recommended to use `createPublicClient` from `viem` + * to construct this client, configured with your desired + * chain and transport (e.g., HTTP RPC URL). + * + * @example + * import { configureSDK } from '@kleros/kleros-sdk'; + * import { createPublicClient, http } from 'viem'; + * import { mainnet } from 'viem/chains'; + * + * const client = createPublicClient({ + * chain: mainnet, + * transport: http('https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'), + * }); + * + * configureSDK({ client }); + */ export const configureSDK = (config: SdkConfig) => { if (config.client) { - publicClient = createPublicClient(config.client); + // Type assertion to satisfy the more specific type of publicClient + publicClient = config.client as PublicClient; } }; -export const getPublicClient = (): PublicClient | undefined => { +/** + * Retrieves the configured Viem PublicClient instance. + * This client is used by the SDK for blockchain interactions. + * + * @returns {PublicClient} The configured Viem PublicClient instance. + * @throws {SdkNotConfiguredError} If the SDK has not been configured via `configureSDK` prior to calling this function. + * + * @example + * import { getPublicClient, SdkNotConfiguredError } from '@kleros/kleros-sdk'; + * + * try { + * const client = getPublicClient(); + * // Now you can use the client for direct Viem calls if needed, + * // or rest assured that SDK functions will use this client. + * } catch (error) { + * if (error instanceof SdkNotConfiguredError) { + * console.error("SDK not configured. Please call configureSDK first."); + * } else { + * console.error("An unexpected error occurred:", error); + * } + * } + */ +export const getPublicClient = (): PublicClient => { if (!publicClient) { throw new SdkNotConfiguredError(); } diff --git a/kleros-sdk/src/types/index.ts b/kleros-sdk/src/types/index.ts index 1d4c58190..e54891717 100644 --- a/kleros-sdk/src/types/index.ts +++ b/kleros-sdk/src/types/index.ts @@ -1,17 +1,65 @@ -import { PublicClientConfig } from "viem"; +import type { PublicClient } from "viem"; +/** + * Configuration object for initializing the Kleros SDK. + */ export type SdkConfig = { - client: PublicClientConfig; + /** + * An instance of Viem's `PublicClient`. + * This client is used for all blockchain interactions by the SDK. + * It should be configured with the desired chain and transport (e.g., RPC URL). + * @see https://viem.sh/docs/clients/public.html + * + * @example + * import { createPublicClient, http } from 'viem'; + * import { mainnet } from 'viem/chains'; + * const client = createPublicClient({ + * chain: mainnet, + * transport: http('https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'), + * }); + * const sdkConfig: SdkConfig = { client }; + */ + client: PublicClient; }; +/** + * Optional parameters for functions like `getDispute`. + */ type GetDisputeParametersOptions = { + /** + * Optional SDK configuration. If not provided, the SDK will attempt to use + * a globally configured client (via `configureSDK`). + * This allows for overriding the global configuration for specific calls. + */ sdkConfig?: SdkConfig; + /** + * Optional additional context to be passed through to data mapping or templating functions. + * This can be used to provide extra information needed for specific dispute resolution flows. + */ additionalContext?: Record; }; +/** + * Parameters required for fetching details of a specific dispute. + */ export type GetDisputeParameters = { + /** + * The unique identifier of the dispute. + * This is typically a BigInt or a string that can be converted to a BigInt. + */ disputeId: bigint; + /** + * The GraphQL endpoint URL for the Kleros Core subgraph. + * This subgraph contains information about disputes, courts, and jurors. + */ coreSubgraph: string; + /** + * The GraphQL endpoint URL for the Dispute Template Registry (DTR) subgraph. + * This subgraph is used to fetch dispute templates which define how dispute data is structured and displayed. + */ dtrSubgraph: string; + /** + * Optional parameters including SDK configuration and additional context. + */ options?: GetDisputeParametersOptions; }; diff --git a/kleros-sdk/src/utils/getDispute.ts b/kleros-sdk/src/utils/getDispute.ts index c73fa1f24..781ed2ae5 100644 --- a/kleros-sdk/src/utils/getDispute.ts +++ b/kleros-sdk/src/utils/getDispute.ts @@ -1,24 +1,81 @@ import { executeActions } from "../dataMappings"; import { DisputeDetails, populateTemplate } from "../dataMappings/utils"; -import { NotFoundError } from "../errors"; +import { NotFoundError, KlerosSDKError } from "../errors"; // Assuming KlerosSDKError is a base error type import fetchDisputeDetails from "../requests/fetchDisputeDetails"; import fetchDisputeTemplateFromId from "../requests/fetchDisputeTemplateFromId"; -import { configureSDK } from "../sdk"; -import { GetDisputeParameters } from "../types"; +import { configureSDK, getPublicClient } from "../sdk"; // Import getPublicClient for internal checks +import { GetDisputeParameters, SdkConfig } from "../types"; /** - * Retrieves dispute parameters based on the provided dispute ID and subgraph endpoints. + * Fetches comprehensive details for a given dispute by its ID. + * This function orchestrates calls to the Kleros Core subgraph and the Dispute Template Registry (DTR) subgraph. + * It retrieves the core dispute data, fetches the corresponding dispute template, + * executes any data mappings defined in the template, and then populates the template + * with the retrieved and processed data. * - * @param {GetDisputeParameters} disputeParameters - The parameters required to get the dispute. - * @param {bigint} disputeParameters.disputeId - A unique numeric identifier of the dispute in the Kleros Core contract. - * @param {string} disputeParameters.coreSubgraph - Endpoint for the Kleros core subgraph to use. - * @param {string} disputeParameters.dtrSubgraph - Endpoint for the Kleros dispute template registry subgraph. - * @param {GetDisputeParametersOptions | undefined} disputeParameters.options - Optional parameters to configure the SDK and provide additional context, if not configured already. + * If `disputeParameters.options.sdkConfig` is provided, it will be used to configure + * the SDK for this specific call. Otherwise, the SDK must have been previously + * configured using `configureSDK()`. + * + * @param {GetDisputeParameters} disputeParameters - The parameters required to fetch the dispute. + * @param {bigint} disputeParameters.disputeId - The unique numeric identifier of the dispute. + * @param {string} disputeParameters.coreSubgraph - The GraphQL endpoint for the Kleros Core subgraph. + * @param {string} disputeParameters.dtrSubgraph - The GraphQL endpoint for the Kleros DTR subgraph. + * @param {object} [disputeParameters.options] - Optional parameters. + * @param {SdkConfig} [disputeParameters.options.sdkConfig] - SDK configuration for this specific call. + * @param {Record} [disputeParameters.options.additionalContext] - Additional context for data mappings. + * + * @returns {Promise} A promise that resolves to the populated dispute details. + * Returns `undefined` if the dispute or template cannot be fully processed (though typically throws errors for missing critical data). + * + * @throws {SdkNotConfiguredError} If the SDK is not configured (and no local `sdkConfig` is provided). + * @throws {NotFoundError} If the dispute details or the dispute template cannot be found in their respective subgraphs. + * @throws {KlerosSDKError} For errors during the execution of data mappings or other unexpected issues. + * + * @example + * import { getDispute, configureSDK, KlerosSDKError, NotFoundError } from '@kleros/kleros-sdk'; + * import { createPublicClient, http } from 'viem'; + * import { mainnet } from 'viem/chains'; + * + * async function main() { + * const client = createPublicClient({ + * chain: mainnet, + * transport: http('https://rpc.ankr.com/eth'), // Replace with your RPC + * }); + * configureSDK({ client }); + * + * const disputeId = 123n; // Use BigInt for dispute IDs + * const coreSubgraphUrl = 'https://api.thegraph.com/subgraphs/name/kleros/kleros-v2-core-mainnet'; // Replace with actual subgraph URL + * const dtrSubgraphUrl = 'https://api.thegraph.com/subgraphs/name/kleros/v2-dispute-template-registry-mainnet'; // Replace with actual subgraph URL + * + * try { + * const dispute = await getDispute({ + * disputeId, + * coreSubgraph: coreSubgraphUrl, + * dtrSubgraph: dtrSubgraphUrl, + * }); + * console.log('Fetched Dispute:', dispute); + * } catch (error) { + * if (error instanceof NotFoundError) { + * console.error('Dispute or template not found:', error.message); + * } else if (error instanceof KlerosSDKError) { + * console.error('Kleros SDK error:', error.message); + * } else { + * console.error('An unexpected error occurred:', error); + * } + * } + * } + * + * main(); */ export const getDispute = async (disputeParameters: GetDisputeParameters): Promise => { if (disputeParameters.options?.sdkConfig) { configureSDK(disputeParameters.options.sdkConfig); + } else { + // Ensure global SDK is configured if no local config is provided + getPublicClient(); } + const { disputeId, dtrSubgraph, coreSubgraph, options } = disputeParameters; const disputeDetails = await fetchDisputeDetails(coreSubgraph, disputeId); @@ -29,10 +86,10 @@ export const getDispute = async (disputeParameters: GetDisputeParameters): Promi const template = await fetchDisputeTemplateFromId(dtrSubgraph, disputeDetails.dispute.templateId); - if (!template) { + if (!template?.disputeTemplate) { // Check specifically for disputeTemplate existence throw new NotFoundError( "Dispute Template", - `Template not found for template ID: ${disputeDetails.dispute.templateId}` + `Template not found or is malformed for template ID: ${disputeDetails.dispute.templateId}` ); } @@ -42,15 +99,18 @@ export const getDispute = async (disputeParameters: GetDisputeParameters): Promi arbitrableAddress: disputeDetails.dispute.arbitrated.id, arbitrableChainID: disputeDetails.dispute.arbitrableChainId, externalDisputeID: disputeDetails.dispute.externalDisputeId, - ...options?.additionalContext, + ...(options?.additionalContext ?? {}), // Ensure additionalContext is an object }; let data = {}; if (templateDataMappings) { try { - data = await executeActions(JSON.parse(templateDataMappings), initialContext); + // Ensure templateDataMappings is valid JSON if it's a string + const parsedMappings = typeof templateDataMappings === 'string' ? JSON.parse(templateDataMappings) : templateDataMappings; + data = await executeActions(parsedMappings, initialContext); } catch (err: any) { - throw err; + // Wrap error for better context, assuming KlerosSDKError can take a cause + throw new KlerosSDKError(`Failed to execute data mappings for dispute ${disputeId}: ${err.message}`, { cause: err }); } }