UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

175 lines (163 loc) 5.67 kB
import type { ComposeCompileRequest, ComposeCompileResult, InputSpec, } from '@lifi/compose-spec'; import { createFlowBuilderCore, type FlowBuilderCore, type FlowOptions, type TypedFlow, } from './authoring/FlowBuilderCore.js'; import { type ComposeClient, createComposeClient } from './client.js'; import { bindGeneratedOps, type GeneratedOps, } from './generated/operations.generated.js'; import { buildRun, type ComposeRunInput } from './run/inputs.js'; import type { InputSchema } from './types.js'; export type { ComposeRunInput }; /** * A flow builder augmented with generated operation methods and a `compile` method * that submits the flow to the Compose backend for compilation. * * Created via {@link ComposeSdk.flow}. * * @typeParam T - The input schema describing the flow's required inputs. */ export type FlowBuilder<T extends InputSchema = InputSchema> = FlowBuilderCore<T> & GeneratedOps & { /** * Builds the flow document from the current builder state and submits it * to the Compose API for compilation. * * @param run - Runtime inputs, preconditions, and signer address. * @returns The compiled transaction calldata and metadata. * @throws {@link ComposeError} on network, validation, or server errors. * * @example * ```ts * const result = await builder.compile({ * inputs: { token: materialisers.balanceOf({}) }, * signer: '0xYourAddress...', * }); * console.log(result.calldata); * ``` */ readonly compile: ( run: ComposeRunInput<T>, ) => Promise<ComposeCompileResult>; }; /** * Configuration for creating a Compose SDK instance. */ export interface ComposeSdkOptions { /** Base URL of the Compose API (e.g. `"https://li.quest"`). */ readonly baseUrl: string; /** Optional custom `fetch` implementation. Defaults to `globalThis.fetch`. */ readonly fetch?: typeof globalThis.fetch; /** Optional LI.FI API key for authenticated access. Sent as the `x-lifi-api-key` header on every request. */ readonly apiKey?: string; } /** * The top-level Compose SDK interface. * * Provides methods to build flows, compile them into executable calldata, and * interact with the Compose API directly. * * @example * ```ts * import { createComposeSdk, resources, materialisers } from '@lifi/composer-sdk'; * * const sdk = createComposeSdk({ baseUrl: 'https://li.quest' }); * * const builder = sdk.flow(1, { * inputs: { usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1) }, * }); * * builder.lifi.swap('swap1', { * bind: { amountIn: builder.inputs.usdc }, * config: { resourceOut: resources.native(1) }, * }); * * const result = await builder.compile({ * inputs: { usdc: materialisers.balanceOf({}) }, * signer: '0xYourAddress...', * }); * ``` */ export interface ComposeSdk { /** Low-level HTTP client for the Compose API. */ readonly client: ComposeClient; /** * Creates a new flow builder targeting the given chain. * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet). * @param options - Flow configuration including input declarations. * @returns A {@link FlowBuilder} with generated operation methods and a `compile` method. * @throws Error if any resource input's `chainId` doesn't match the flow's `chainId`. */ readonly flow: <T extends InputSchema>( chainId: number, options: FlowOptions<T>, ) => FlowBuilder<T>; /** * Builds a raw compile request without sending it. Useful for inspecting the * request payload or submitting it through a custom transport. * @param flow - A built flow document (from `builder.build()`). * @param run - Runtime inputs, preconditions, and signer address. * @returns A {@link ComposeCompileRequest} ready to send to the Compose API. */ readonly request: <T extends InputSchema>( flow: TypedFlow<T>, run: ComposeRunInput<T>, ) => ComposeCompileRequest; } /** * Creates a new Compose SDK instance. * * @param options - SDK configuration including the API base URL and optional fetch implementation. * @returns A {@link ComposeSdk} instance. * * @example * ```ts * const sdk = createComposeSdk({ baseUrl: 'https://li.quest' }); * ``` */ export const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => { const composeClient = createComposeClient(options); const request = <T extends InputSchema>( flowDoc: TypedFlow<T>, run: ComposeRunInput<T>, ): ComposeCompileRequest => ({ flow: flowDoc, run: buildRun({ inputs: run.inputs as Record<string, InputSpec>, preconditions: run.preconditions, signer: run.signer, assumptions: run.assumptions as Record<string, bigint> | undefined, referrer: run.referrer, integratorFeeBps: run.integratorFeeBps, maxPriceImpactBps: run.maxPriceImpactBps, sweepTo: run.sweepTo, simulationPolicy: run.simulationPolicy, checkOnChainAllowances: run.checkOnChainAllowances, }), }); const flow = <T extends InputSchema>( chainId: number, opts: FlowOptions<T>, ): FlowBuilder<T> => { const core = createFlowBuilderCore(chainId, opts); const builder = Object.assign(core, bindGeneratedOps(core)); const compile = async ( run: ComposeRunInput<T>, ): Promise<ComposeCompileResult> => { const flowDoc = builder.build(); const req = request(flowDoc, run); return composeClient.compile(req); }; return Object.assign(builder, { compile }); }; return { client: composeClient, flow, request }; };