This page enumerates the public API of @lifi/composer-sdk. For the authoritative TypeScript types, browse the package source at unpkg.com/browse/@lifi/composer-sdk.
There is no sdk.compile(). Compile lives on the builder: builder.compile(run). sdk.request(flow, run) is the escape hatch for custom transports.
createComposeSdk(options)
Declared in sdk.ts.
const sdk = createComposeSdk({
baseUrl: 'https://composer.li.quest',
apiKey: process.env.LIFI_API_KEY,
fetch: globalThis.fetch,
});
ComposeSdkOptions
| Field | Type | Notes |
|---|
baseUrl | string | Base URL of the Compose API. |
apiKey | string | Required during the technical preview. Sent as the x-lifi-api-key header on every request. Get one at portal.li.fi. |
fetch | typeof globalThis.fetch? | Defaults to the global fetch. |
ComposeSdk
Members:
| Member | Signature | Notes |
|---|
client | ComposeClient | Low-level HTTP client (compile, getManifest, getZapPacks). See sdk.client. |
flow | <T>(chainId, options: FlowOptions<T>) => FlowBuilder<T> | Creates a new flow builder. |
request | <T>(flow: TypedFlow<T>, run: ComposeRunInput<T>) => ComposeCompileRequest | Builds a compile request without sending it. |
sdk.flow(chainId, options) → FlowBuilder
const builder = sdk.flow(1, {
name: 'my-flow', // optional; defaults to a UUID
inputs: {
amountIn: resources.erc20(WETH, 1),
deadline: 'uint256',
},
});
FlowOptions<T>
| Field | Type | Notes |
|---|
name | string? | Human-readable id. Defaults to crypto.randomUUID(). |
inputs | T extends InputSchema | Record of input name → Resource or SolType. |
The generic T carries through to typed handles at builder.inputs.<name>.
FlowBuilder<T>
A FlowBuilder is a FlowBuilderCore<T> (declared in FlowBuilderCore.ts) augmented with a typed method per op plus a compile method.
Inherited FlowBuilderCore members
| Member | Signature | Notes |
|---|
context | ContextAccessor | { sender, executionAddress } — typed refs to runtime context values. |
inputs | InputHandles<T> | Typed handle per declared input. Resource inputs → ResourceInputHandle; scalar → InputHandle<T>. |
untypedOp | (id, op, { bind, config, guards? }) => void | Escape hatch for ops not yet covered by a typed builder method. Accepts raw Ref values. |
build | () => TypedFlow<T> | Serialises the builder state to a Flow document. |
Op methods
The SDK exposes a typed method per op the Composer API supports. Examples:
builder.lifi.swap(id, args), builder.lifi.zap(id, args)
builder.core.call(id, args), builder.core.asResource(id, args), builder.core.balanceOf(id, args), and the arithmetic methods builder.core.add(id, args), subtract, multiply, divideDown, divideUp, bpsDown, bpsUp — one method per arithmetic op.
Each method returns a typed Record<portName, OutputHandle> for the op’s outputs. See the live Ops catalog.
builder.compile(run)
One-shot: calls build(), forms a ComposeCompileRequest via sdk.request, POSTs it to /compose, and returns a ComposeCompileResult.
const result = await builder.compile({
signer: '0xYourSigner',
inputs: {
amountIn: materialisers.directDeposit({ amount: '1000000000000000000' }),
},
sweepTo: builder.context.sender,
});
Throws a ComposeError on network, validation, or server errors.
sdk.request(flow, run) → ComposeCompileRequest
Use when you need the raw request payload — queueing, server-side proxy, signing the request, inspection in tests.
const flow = builder.build();
const request = sdk.request(flow, {
signer: '0xYourSigner',
inputs: { amountIn: materialisers.directDeposit({ amount: '1000000000000000000' }) },
});
const response = await fetch('https://composer.li.quest/compose', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(request),
});
sdk.client
The low-level HTTP client. Most integrations only need builder.compile(); reach for the client when you build a request via sdk.request() and submit it yourself, or when you need protocol/manifest discovery.
| Method | Signature | Notes |
|---|
compile | (request: ComposeCompileRequest) => Promise<ComposeCompileResult> | POST /compose. The transport builder.compile() calls under the hood. |
getManifest | () => Promise<ComposeManifest> | GET /compose/manifest. The live catalog of ops, materialisers, and guards the backend accepts — the same source that powers the Ops, Materialisers, and Guards catalogs. |
getZapPacks | (options?: GetZapPacksOptions) => Promise<readonly ZapPackOverview[]> | GET /compose/zap-packs. The dynamic routing-edge catalog — which tokens can be routed into which protocol positions on which chains. Use it to discover valid lifi.zap resourceOut targets. |
// Discover which tokens can be zapped into Aave positions.
const packs = await sdk.client.getZapPacks({ protocols: 'aave' });
for (const pack of packs) {
for (const edge of pack.edges) {
// edge.type ('enter-position' | 'exit-position' | …), edge.in, edge.out
}
}
getZapPacks accepts an optional { protocols?: string | readonly string[] } filter. Results are not cached by the SDK — cache as appropriate for your refresh needs. See the routing edges catalog for the rendered live view.
ComposeCompileResult
A discriminated union on the status field. Branch on result.status before accessing shape-specific fields.
type ComposeCompileResult =
| (ComposeCompileSuccessData & { status: 'success' })
| (ComposeCompilePartialData & {
status: 'partial';
error: { kind: ComposeErrorKind; message: string };
simulationRevert: SimulationRevert;
});
status: 'success' — returned under the default simulationPolicy: 'strict' when the compile and simulation both succeed.
status: 'partial' — returned only when simulationPolicy: 'allow-revert' was passed on the run and the simulation reverted. The transactionRequest is still present; the revert diagnostics are exposed for the caller to surface.
Both shapes carry the core fields: transactionRequest, userProxy, producedResources, producedHandles (values for any output handles marked expose: true), optional approvals, optional priceImpact, and optional fees.
Declared in run/inputs.ts.
| Field | Type | Notes |
|---|
inputs | { [K in keyof T]: InputSpecOf<T[K]> } | Per-input value — bigint, hex string, or materialiser descriptor. |
signer | Address | 0x-prefixed signer address. |
preconditions | readonly Precondition[]? | Invariants asserted before execution. |
assumptions | { [K in keyof T]?: bigint }? | Assumed amounts when materialisers resolve at execution time. |
referrer | string? | Integrator referrer id. |
integratorFeeBps | number? | Integrator fee in basis points (1bp = 0.01%, max 9000). Defaults to 0. A non-zero value requires an integration-scoped API key — the integration is derived from the key, not this field. |
maxPriceImpactBps | number? | Reject compile if aggregate USD price impact exceeds the bound. |
sweepTo | SweepTo? | Destination for terminal proxy-held resources. Address literal or { $ref: "context.sender" }. |
simulationPolicy | "strict" | "allow-revert"? | Default "strict". |
checkOnChainAllowances | boolean? | Omit approvals already satisfied on-chain. |
Handles
Declared in authoring/handles.ts.
InputHandle<T> — { _tag: 'input', inputName, __outputKind?: T }. Carries the input’s output kind as a phantom type parameter.
ResourceInputHandle — InputHandle<'resource'> & { resource: Resource }.
OutputHandle<T> — { _tag: 'output', nodeId, portName, __outputKind?: T }.
Bindable<T> — the union of values accepted in a typed bind slot: InputHandle<T>, OutputHandle<T>, TypedRef<T>. For 'uint256' slots, 'resource'-tagged handles are also accepted (resources are uint256 amounts).
Convert handles to raw refs with handleToRef (exported from the same module).
Namespaces
resources — helpers to declare token resources (resources.erc20(token, chainId), resources.native(chainId)).
materialisers — generated helpers for registered materialisers (materialisers.directDeposit({ amount }), materialisers.balanceOf({ ... }), …).
guards — generated helpers for registered guards (guards.slippage({ port, bps }), …).
raw — low-level escape hatches: raw.ref<T>(path) creates a TypedRef<T>; raw.guard(kind, config?) builds an AppliedGuard for guard kinds not yet covered by a typed helper; raw.materialiser(kind, config?) builds a MaterialiserInput for the same reason.
TypedFlow<T>
The Flow document returned by builder.build(). Structurally identical to Flow from @lifi/compose-spec, with a phantom __inputs?: T carrying the input schema through TypeScript inference (__inputs does not exist at runtime).
Error types
ComposeError (re-exported from @lifi/compose-spec) carries:
{
kind: ComposeErrorKind; // e.g. "validation_error" | "guard_error" | "simulation_revert" | …
message: string;
path?: string;
}
See Error codes for the full catalog and the HTTP status each kind maps to.
See also
- Quickstart — five-minute first-flow walkthrough.
- Build a Flow — production setup, inputs, ops, materialisers, guards, preconditions, submission.
- Flow wire format — the JSON the SDK produces.