@lifi/compose-spec
Version:
Public wire-format types and schemas for Compose flows
205 lines (187 loc) • 8.45 kB
text/typescript
import type { VerificationDescriptor } from './descriptor.js';
import type { InputSpec, Precondition } from './flow.js';
import type { Flow } from './flowSchema.js';
import type { Outputs } from './output.js';
import type { ContinuationPlanWire, Phase1ArtifactWire } from './zodSchemas.js';
export type SimulationPolicy = 'strict' | 'allow-revert';
/**
* Destination for sweeping proxy-held terminal resources after execution.
* Either a literal EVM address or a `{ $ref }` context reference
* (e.g. `{ $ref: "context.sender" }`).
*/
export type SweepTo = string | { readonly $ref: string };
export interface ComposeRun {
readonly inputs: Record<string, InputSpec>;
readonly preconditions?: readonly Precondition[];
readonly signer: string;
readonly assumptions?: Record<string, bigint>;
readonly referrer?: string;
readonly integratorFeeBps?: number;
readonly maxPriceImpactBps?: number;
readonly sweepTo?: SweepTo;
readonly simulationPolicy?: SimulationPolicy;
/**
* Also simulate the final user-facing transaction. Defaults to `false`;
* reverts honour `simulationPolicy` and a success is echoed back as
* `userProgramSimulation`.
*/
readonly simulateUserProgram?: boolean;
readonly checkOnChainAllowances?: boolean;
}
export interface ComposeCompileRequest {
readonly flow: Flow;
readonly run: ComposeRun;
}
export interface PriceImpact {
readonly inputValueUsd: number;
readonly outputValueUsd: number;
readonly impactBps: number;
readonly unpricedInputs: readonly string[];
readonly unpricedOutputs: readonly string[];
}
export interface ApprovalEntry {
readonly token: string;
readonly spender: string;
readonly amount: string;
readonly transactionRequest: {
readonly to: string;
readonly data: string;
readonly value: '0';
};
}
export interface SimulationRevert {
readonly code: number;
readonly rawErrorBytes: string;
readonly decodeResult?: {
readonly errorCandidates?: readonly {
readonly decodedErrorSignature: string;
readonly decodedParams: readonly string[];
}[];
readonly error?: string;
};
}
export interface ComposeTransactionRequest {
readonly to: string;
readonly chainId: number;
readonly data: string;
readonly value: string;
readonly gasLimit?: string;
}
// Hand-mirrored from `ComposeActionSchema`, the OpenAPI source of truth in
// `@lifi/api-schemas`.
export interface ComposeAction {
readonly kind: 'transaction';
readonly purpose: 'approval' | 'execute' | 'fundEscrow';
readonly submitter: 'signer' | 'anyone';
readonly transactionRequest: ComposeTransactionRequest;
readonly nodeId?: string;
}
// Continuation fields shared by the success and partial-error responses, so the
// two shapes cannot drift. `phase1ByNode` / `continuationPlans` reuse the
// Zod-inferred wire types from `zodSchemas.ts`, pinning these SDK-facing types
// to the server contract rather than hand-transcribing them.
interface ComposeContinuationData {
// Phase-1 action to sign/fund now, keyed by settle node. Absent for an atomic flow.
readonly phase1ByNode?: Record<string, Phase1ArtifactWire>;
// Self-serve second leg to re-submit later, keyed by settle node. Present only
// for providers that hand the next leg back to the caller (manual-relay).
readonly continuationPlans?: Record<string, ContinuationPlanWire>;
}
// One funding leg the compiled fill consumes: the resource input's name, its
// token (native → zero address) and chain, and the statically resolved amount
// as a decimal string (verified continuations, plan GD-8, seam C5). `nodeId`
// names the `continuation.settle` node this input funds (the `phase1ByNode` /
// `actions` key); absent when the input funds no settle leg (e.g. a plain
// swap's input).
export interface SpendEntry {
readonly input: string;
readonly token: string;
readonly chainId: number;
readonly amount: string;
readonly nodeId?: string;
}
// One fee row per (input, recipient), as the compiled fill charges it.
// Hand-mirrored from api-schemas' `ComposeFeeSchema` (the OpenAPI source of
// truth; this package cannot depend on it). `recipient` names WHO is charged
// — never an address; the address is the sibling `recipientAddress`. `amount`
// is absent when the gross is only known on-chain at execution time (a
// balance-materialiser input).
export interface ComposeFee {
readonly input: string;
// Zero address for native, otherwise the ERC-20 contract.
readonly token: string;
readonly recipient: 'lifi' | 'integrator';
readonly recipientAddress: string;
readonly bps: number;
readonly amount?: string;
}
// The optional user-program simulation result, present only when the request
// opted in via `run.simulateUserProgram` and the simulation succeeded.
// Hand-mirrored from api-schemas' `ComposeUserProgramSimulationOkSchema`.
export interface ComposeUserProgramSimulation {
readonly status: 'ok';
readonly returnData: string;
}
// The solver-extractable fill (verified continuations, GD-8/GD-6, seam C5),
// present on every compiled success. Hand-mirrored to api-schemas' `fill`
// object (the OpenAPI source of truth; this package cannot depend on it).
// `spends` is what the fill consumes per funding input; `validUntilMs` is the
// fill freshness bound (earliest prepared-node expiry) — ABSENT = no expiry.
// The fill is transaction-scoped; the leg-scoped C4 verification echo lives in
// the sibling `verificationByNode` record.
export interface ComposeFill {
readonly spends: readonly SpendEntry[];
readonly validUntilMs?: number;
}
export interface ComposeCompileSuccessData extends ComposeContinuationData {
// Every linear resource output port, consumed or terminal, keyed
// `<nodeId>.<portName>`. See `Output` for the per-entry rules. Hand-mirrored
// from api-schemas' `OutputSchema` (the OpenAPI source of truth; this
// package cannot depend on it); the SDK decodes the wire strings to `bigint`.
readonly outputs: Outputs;
// The OpenAPI source of truth documents the optionality and funding modes.
readonly transactionRequest?: ComposeTransactionRequest;
readonly actions?: readonly ComposeAction[];
readonly userProxy: string;
readonly priceImpact?: PriceImpact;
readonly approvals?: readonly ApprovalEntry[];
// The per-(input, recipient) fee rows the compiled fill charges. Absent when
// no fee applies.
readonly fees?: readonly ComposeFee[];
// The optional user-program simulation echo, present only when the request
// opted in via `run.simulateUserProgram` and the simulation succeeded.
readonly userProgramSimulation?: ComposeUserProgramSimulation;
// GD-8 settlement-fill surface (seam C5): the solver-extractable `fill`,
// present on every success response — the funding legs it consumes and the
// freshness bound (absent = no expiry).
readonly fill?: ComposeFill;
// The C4 verification echo, keyed by the flow node that carried the
// commitment (the `continuation.commitment` node on a settlement
// re-submission, the `continuation.settle` node on authoring-time provider
// verification). Present only when the compile committed a validation
// program; today at most one entry (the compiler rejects multi-node
// committed verification).
readonly verificationByNode?: Record<string, VerificationDescriptor>;
}
export interface ComposeCompilePartialData extends ComposeContinuationData {
// Mirrors `ComposeCompileSuccessData.outputs`. A revert measured nothing, so
// estimates are quotes and minimums are op-sourced.
readonly outputs: Outputs;
readonly transactionRequest: ComposeTransactionRequest;
readonly userProxy: string;
readonly simulationRevert: SimulationRevert;
readonly approvals?: readonly ApprovalEntry[];
readonly actions?: readonly ComposeAction[];
// The per-(input, recipient) fee rows the compiled fill charges. Absent when
// no fee applies.
readonly fees?: readonly ComposeFee[];
// The optional user-program simulation echo, present only when the request
// opted in via `run.simulateUserProgram` and the simulation succeeded.
readonly userProgramSimulation?: ComposeUserProgramSimulation;
}
export type ComposeCompileResult =
| (ComposeCompileSuccessData & { readonly status: 'success' })
| (ComposeCompilePartialData & {
readonly status: 'partial';
readonly error: { readonly kind: string; readonly message: string };
});