@lifi/compose-spec
Version:
Public wire-format types and schemas for Compose flows
128 lines (122 loc) • 5.57 kB
text/typescript
/**
* Canonical request types for the `POST /compose/route` convenience endpoint.
* Amounts accept `bigint | string` for authoring convenience; the JSON wire
* format is always a decimal string, which the SDK serialises for you.
*
* `/compose/route` trades authoring power for brevity. Instead of a full flow
* document it takes a single from/to token pair; the server builds a
* `lifi.zap` flow with a `directDeposit` input on the caller's behalf and runs
* it through the same compile pipeline as `POST /compose`. As of today the flow
* it builds is a single zap step — one edge of the server's routing catalog —
* and it is meant to author richer flows later, which this request shape is
* deliberately agnostic about. Its response body is byte-identical to that of
* `POST /compose` — the enveloped `ComposeCompileSuccessData` on HTTP 200 and
* `ComposeCompilePartialData` on HTTP 206 — so this file declares only the
* request shape and reuses `ComposeCompileResult` from `./compile.js` for the
* result.
*
* These are the single source of truth for the request shape, hand-authored
* here for the same reason as `./compile.ts` and `./simulate.ts` rather than
* derived from the validating schema: that schema is the Zod one in
* `@lifi/api-schemas` (`src/routes/composeRoute.ts`), a package that depends on
* this one, so importing its inferred types here would invert the dependency —
* and the public SDK ships this package as its only peer dependency, never the
* server's schema package. The two definitions are held in lockstep by a
* compile-time conformance assertion
* (`api-schemas/src/routes/composeRoute.typecheck.ts`), which fails the build
* if either side drifts.
*/
import type { SimulationPolicy, SweepTo } from './compile.js';
/**
* Spend an exact amount of `fromToken`.
*
* `amount` is in the token's smallest unit. Accepts `bigint | string`; the wire
* format is a non-negative decimal string.
*/
export interface RouteAmountExact {
readonly type: 'EXACT';
readonly amount: bigint | string;
}
/**
* Spend the signer's entire `fromToken` balance, whatever it is at execution
* time.
*
* The on-chain amount is resolved by a deposit-all materialiser, so it is not
* known when the route is compiled. `simAmount` is the stand-in the server
* quotes and simulates against — make it a realistic estimate of the balance,
* because it determines the quote and the derived preconditions, not the amount
* actually moved. Accepts `bigint | string`; the wire format is a non-negative
* decimal string.
*
* Not supported when `fromToken` is the chain's native sentinel: a gas coin
* arrives via `msg.value`, so there is no on-chain balance to sweep, and the
* server rejects the combination with a `validation_error`. Use
* {@link RouteAmountExact} for native inputs.
*/
export interface RouteAmountAll {
readonly type: 'ALL';
readonly simAmount: bigint | string;
}
/**
* How much of `fromToken` a route spends. Two variants, discriminated by
* `type`.
*/
export type RouteAmount = RouteAmountExact | RouteAmountAll;
/** Request body for `POST /compose/route`. */
export interface ComposeRouteRequest {
/** EVM chain id. Both tokens must live on this chain — routes are same-chain. */
readonly chainId: number;
/**
* Token to spend. Use the chain's native sentinel address for the gas coin
* (the zero address on EVM-native chains).
*/
readonly fromToken: string;
/** Token to receive. */
readonly toToken: string;
/** How much of `fromToken` to spend — an exact amount or the whole balance. */
readonly amount: RouteAmount;
/** Address that funds the route and, by default, owns its output. */
readonly signer: string;
/**
* Slippage tolerance in basis points, applied as a guard on the route's
* output. Range 0–10000. Defaults to `100` (1%) server-side when omitted.
*/
readonly slippageBps?: number;
/**
* Reject the route when its quoted price impact exceeds this many basis
* points. Range 0–10000. Omitted ⇒ no price-impact guard is installed.
*/
readonly maxPriceImpactBps?: number;
/**
* Integrator fee in basis points, taken from the input amount. Range 0–9000.
* Defaults to `0` server-side when omitted. A non-zero value requires an
* integration-scoped API key; the server rejects it otherwise.
*/
readonly integratorFeeBps?: number;
/** Opaque referrer tag forwarded to the underlying quote provider. */
readonly referrer?: string;
/**
* Owner of the route's output; defaults to `signer`. Routes whose protocol
* mints to a named receiver (surfaced as `recipient: 'required'` by
* `GET /compose/zap-packs`) bind this address at mint time rather than
* transferring afterwards. Accepts an address or `{ $ref: 'context.sender' }`;
* `{ $ref: 'context.executionAddress' }` is rejected.
*/
readonly sweepTo?: SweepTo;
/**
* `'strict'` (the default) fails the request when simulation detects a
* revert; `'allow-revert'` returns a partial result with revert diagnostics
* instead.
*/
readonly simulationPolicy?: SimulationPolicy;
/**
* Also simulate the final user-facing transaction after the structured
* simulation succeeds. Reverts honour `simulationPolicy`. Defaults to `false`.
*/
readonly simulateUserProgram?: boolean;
/**
* Filter the returned `approvals` against current on-chain allowances,
* omitting approvals that are already sufficient. Defaults to `false`.
*/
readonly checkOnChainAllowances?: boolean;
}