@lifi/composer-sdk 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,22 @@
1
1
  # @lifi/composer-sdk
2
2
 
3
- TypeScript SDK for building and submitting LI.FI Compose flows.
3
+ TypeScript SDK for building LI.FI Compose flows and compiling them into submit-ready transactions.
4
+
5
+ Full documentation: [docs.li.fi/composer](https://docs.li.fi/composer/overview). SDK API reference: [SDK API](https://docs.li.fi/composer/composer-api/reference/sdk-api.md).
4
6
 
5
7
  ## Install
6
8
 
7
- `@lifi/compose-spec` is a peer dependency and must be installed alongside the SDK at the same version (they are versioned in lockstep).
9
+ `@lifi/compose-spec` is a peer dependency. Install it at exactly the same version as the SDK; the two are versioned in lockstep. Node.js 24 or later is required.
8
10
 
9
11
  ```bash
10
12
  npm install @lifi/composer-sdk @lifi/compose-spec
11
13
  ```
12
14
 
13
- ## Upgrading to 0.3.0
14
-
15
- **Breaking:** `apiKey` is now required on `createComposeSdk()`.
16
-
17
- The Compose API has always rejected unauthenticated requests, but `apiKey` was typed as optional, so omitting it compiled and only failed on the first request. It is now a required `string`, and a missing or blank value throws a `ComposeError` (`VALIDATION_ERROR`) at construction time — including for plain-JavaScript callers that bypass the type check.
18
-
19
- ```diff
20
- -const sdk = createComposeSdk({ baseUrl: 'https://composer.li.quest' });
21
- +const sdk = createComposeSdk({
22
- + baseUrl: 'https://composer.li.quest',
23
- + apiKey: process.env.LIFI_API_KEY!,
24
- +});
25
- ```
26
-
27
- Create a key at [portal.li.fi](https://portal.li.fi).
15
+ `apiKey` is required (create one at [portal.li.fi](https://portal.li.fi)). A missing or blank key throws a `ComposeError` with code `VALIDATION_ERROR` when you call `createComposeSdk()`, including from plain JavaScript that skips the type check. It has been required since 0.3.0.
28
16
 
29
17
  ## Quick start
30
18
 
31
- Swap WETH to USDC, then zap the USDC into an Aave lending position — all in a single transaction.
19
+ Swap WETH to USDC, then zap the USDC into an Aave lending position, in one transaction.
32
20
 
33
21
  ```ts
34
22
  import {
@@ -43,13 +31,12 @@ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
43
31
  const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c'; // Aave aEthUSDC
44
32
  const OWNER = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
45
33
 
46
- // Create the SDK pointed at the Compose API.
47
34
  const sdk = createComposeSdk({
48
35
  baseUrl: 'https://composer.li.quest',
49
- apiKey: process.env.LIFI_API_KEY!, // required
36
+ apiKey: process.env.LIFI_API_KEY!,
50
37
  });
51
38
 
52
- // Build a two-step flow on Ethereum mainnet.
39
+ // A flow on Ethereum mainnet with one token input.
53
40
  const builder = sdk.flow(1, {
54
41
  name: 'swap-and-zap-weth-to-aave',
55
42
  inputs: {
@@ -57,7 +44,7 @@ const builder = sdk.flow(1, {
57
44
  },
58
45
  });
59
46
 
60
- // Step 1: Swap WETH → USDC via LI.FI.
47
+ // Step 1: swap WETH to USDC via LI.FI.
61
48
  const swapOutputs = builder.lifi.swap('swap', {
62
49
  bind: { amountIn: builder.inputs.amountIn },
63
50
  config: {
@@ -66,8 +53,8 @@ const swapOutputs = builder.lifi.swap('swap', {
66
53
  },
67
54
  });
68
55
 
69
- // Step 2: Zap the swapped USDC into Aave.
70
- // The swap's amountOut handle threads directly into the zap's amountIn.
56
+ // Step 2: zap the swapped USDC into Aave. The swap's amountOut handle
57
+ // feeds the zap's amountIn directly.
71
58
  builder.lifi.zap('zap', {
72
59
  bind: { amountIn: swapOutputs.amountOut },
73
60
  config: {
@@ -78,7 +65,6 @@ builder.lifi.zap('zap', {
78
65
 
79
66
  const flow = builder.build();
80
67
 
81
- // Compile the flow into transaction calldata.
82
68
  const request = sdk.request(flow, {
83
69
  signer: OWNER,
84
70
  inputs: {
@@ -87,88 +73,108 @@ const request = sdk.request(flow, {
87
73
  }),
88
74
  },
89
75
  sweepTo: builder.context.sender,
90
- // Opt into partial results. Without this, the default 'strict' policy
91
- // throws a ComposeError (HTTP 422) when simulation detects a revert,
92
- // and the `partial` branch below is never reached.
76
+ // Opt into partial results. Under the default 'strict' policy a simulated
77
+ // revert throws a ComposeError instead, and the `partial` branch is unreachable.
93
78
  simulationPolicy: 'allow-revert',
94
79
  });
95
80
 
96
81
  const result = await sdk.client.compile(request);
97
82
 
98
83
  if (result.status === 'success') {
99
- // Full success — transactionRequest includes gasLimit.
84
+ // transactionRequest includes gasLimit.
100
85
  console.log(result.transactionRequest);
101
86
 
102
- // What the flow produced, one entry per output port. The zap's output is
103
- // terminal (nothing downstream spends it), so it carries the owner it is
104
- // delivered to; the swap's output is `role: 'consumed'` by the zap.
87
+ // One entry per output port. Nothing downstream spends the zap's output, so
88
+ // it is terminal and carries its owner. The swap's output is `consumed` by the zap.
105
89
  const position = result.outputs['zap.amountOut'];
106
90
  if (position?.role === 'terminal') {
107
91
  console.log(position.owner, position.amount.estimate?.value);
108
92
  }
109
93
  } else {
110
- // result.status === 'partial' — simulation reverted.
111
- // Transaction is still available but without gasLimit.
94
+ // status === 'partial': simulation reverted. The transaction is still
95
+ // returned, without gasLimit.
112
96
  console.log(result.simulationRevert);
113
97
  }
114
98
  ```
115
99
 
100
+ `builder.compile(run)` is shorthand for `sdk.client.compile(sdk.request(builder.build(), run))`.
101
+
116
102
  ## Core concepts
117
103
 
118
- **Flows and operations** — A flow is a sequence of on-chain operations. You declare inputs, chain operations together, and the backend compiles everything into a single transaction. Operations are namespaced (e.g., `builder.lifi.swap`, `builder.core.split`).
104
+ **Flows and operations**: a flow is an ordered list of on-chain operations. You declare inputs, chain operations, and the backend compiles everything into one transaction. Operations are namespaced, e.g. `builder.lifi.swap` and `builder.core.split`. Each call takes a node id that must be unique in the flow.
119
105
 
120
- **Resources** — `resources.erc20(address, chainId)` and `resources.native(chainId)` describe the tokens flowing through your operations. They carry chain and address metadata used for routing and validation.
106
+ **Resources**: `resources.erc20(address, chainId)` and `resources.native(chainId)` describe the tokens flowing through the flow. A resource input must be on the flow's chain.
121
107
 
122
- **Handles** — Operations produce typed output handles (e.g. `OutputHandle<'resource'>`, `OutputHandle<'uint256'>`) that you bind to downstream inputs. The type system enforces compatibility at compile time — a resource handle can flow into a `uint256` slot (since resources are amounts), but an `address` handle cannot.
108
+ **Handles**: operations return typed output handles, e.g. `OutputHandle<'resource'>` or `OutputHandle<'uint256'>`, which you bind to downstream inputs. TypeScript enforces the port types. A resource handle fits a `uint256` slot (resources are amounts), but an `address` handle does not.
123
109
 
124
- **Runtime inputs (materialisers)** — Materialisers resolve input values at execution time rather than at build time. `directDeposit` is exact by default when you provide an amount; pass `allowNonExact: true` to permit capped ERC-20 deposits or deposit-all behavior. `balanceOf` reads the wallet's current balance; `call` measures a balance delta after an arbitrary contract call.
110
+ **Runtime inputs (materialisers)**: a materialiser tells the backend how to fund a flow input when the transaction executes.
125
111
 
126
- **Preconditions** — Expected on-chain state at execution time: `erc20Balance` and `nativeBalance` assert wallet holdings, `erc20Allowance` asserts token approvals.
112
+ - `directDeposit({ amount })` pulls exactly `amount`.
113
+ - `directDeposit({ amount, allowNonExact: true })` permits a capped ERC-20 deposit of up to `amount`; `directDeposit({ allowNonExact: true })` deposits with no on-chain cap (ERC-20 only).
114
+ - `balanceOf({ owner })` reads a current balance.
115
+ - `call({ target, calldata })` measures the balance delta after an arbitrary contract call.
127
116
 
128
- **Guards** — Protect against slippage and other runtime conditions. Applied per-operation via the `guards` field.
117
+ **Preconditions**: on-chain state the backend assumes when it simulates, e.g. a balance the signer will hold at execution time. The factories are `erc20Balance`, `nativeBalance`, and `erc20Allowance`.
129
118
 
130
- **Outputs** — A compile result carries `outputs`, a record keyed `'<nodeId>.<portName>'` holding every resource output the flow produces. Each entry is `role: 'terminal'` (delivered to `owner`, with `delivery.when` telling you whether it lands with this transaction) or `role: 'consumed'` (spent by the node named in `consumedBy`), so narrow on `role` before reading either field. `amount.estimate` and `amount.minimum` are both optional: an estimate is absent when nothing measured or quoted the port, and a minimum is absent when no floor is enforced. `terminalOutputs(result.outputs)` narrows the record to the terminal entries. Full field reference: [docs/sdk-interface.md](./docs/sdk-interface.md#outputs).
119
+ **Guards**: runtime checks on an operation's output, attached through its `guards` field. `guards.slippage({ port, bps })` floors an output at `bps` below its simulated amount. The `port` argument is typed against the ports a guard can protect. For example, `lifi.swap`'s `amountOut` already carries the aggregator's own floor, so it rejects a slippage guard at compile time.
131
120
 
132
- ## API surface
121
+ **Outputs**: a compile result carries `outputs`, keyed `'<nodeId>.<portName>'`, with one entry per resource output. An entry is either:
133
122
 
134
- **SDK factory**
123
+ - `role: 'terminal'`: delivered to `owner`. `delivery.when` says whether it lands with this transaction (`'now'`) or later (`'future'`).
124
+ - `role: 'consumed'`: spent by the node named in `consumedBy`.
135
125
 
136
- - `createComposeSdk({ baseUrl, apiKey, fetch? })` — creates the SDK instance
126
+ Narrow on `role` before reading those fields. `amount.estimate` and `amount.minimum` are both optional. An absent minimum means no floor is enforced; there is no `0` default. `terminalOutputs(result.outputs)` keeps only the terminal entries.
137
127
 
138
- **Flow building**
128
+ ## API surface
139
129
 
140
- - `sdk.flow(chainId, options)` — creates a `FlowBuilder`
141
- - `builder.<namespace>.<operation>(id, { bind, config })` — adds an operation, returns typed `OutputHandle<T>` per port
142
- - `builder.untypedOp(id, op, args)` — escape hatch for operations not in the manifest (returns `void`; use `raw.ref<T>()` to reference its outputs)
143
- - `builder.build()` — produces a `Flow` document
144
- - `sdk.request(flow, { signer, inputs, preconditions, sweepTo, ... })` — builds a compile request
130
+ **SDK**
145
131
 
146
- **HTTP client**
132
+ - `createComposeSdk({ baseUrl, apiKey, fetch? })`: `baseUrl` must be an HTTP(S) URL. A trailing slash is stripped.
133
+ - `sdk.flow(chainId, { inputs, name? })`: returns a `FlowBuilder`. `name` defaults to a random UUID.
134
+ - `builder.<namespace>.<operation>(nodeId, { bind, config, guards? })`: adds a node and returns one typed handle per output port.
135
+ - `builder.untypedOp(nodeId, op, { bind, config, guards? })`: escape hatch for ops without a typed method. Returns `void`; reference its outputs with `raw.ref<T>(path)`.
136
+ - `builder.build()`: returns the `Flow` document.
137
+ - `builder.compile(run)`: builds, then compiles.
138
+ - `sdk.request(flow, run)`: builds a `ComposeCompileRequest` from a flow and a `ComposeRunInput`.
139
+ - `sdk.route(request)`: compiles a token pair without authoring a flow. See [Routing a token pair](#routing-a-token-pair).
140
+ - `sdk.simulate(request)`: simulates a raw transaction. See [Simulating a raw transaction](#simulating-a-raw-transaction).
141
+ - `sdk.getSupportedChains()`: same as `sdk.client.getChains()`.
147
142
 
148
- - `sdk.client.compile(request)` — sends the flow to the backend and returns a `ComposeCompileResult` (discriminated union: `status: 'success'` or `status: 'partial'`)
149
- - `sdk.client.getManifest()` — fetches the operation manifest
150
- - `sdk.client.getChains()` / `sdk.getSupportedChains()` — fetches the chains this deployment supports as `readonly ChainSummary[]` (`{ chainId, name }`), ascending by `chainId`. A chain is listed once the VM contract is confirmed deployed on it, which is the same condition `compile`, `route` and `simulate` validate against — so a chain missing from this list is rejected by those methods. The converse does not hold: a listed chain can still have no routing edge for a given pair, reported by `route` as `no_route_error`. `name` is a display label maintained by the deployment, so render it but key on `chainId`; a chain with no configured label reports `Chain <id>`. The list tracks the backend's chain registry, which refreshes periodically, and is not cached by the SDK.
151
- - `sdk.client.getZapPacks(options?)` — fetches the available routing edges grouped by protocol, returning `ZapPackOverview[]`. The catalog is dynamic and is not cached by the SDK. Filter through `GetZapPacksOptions`. An edge with `recipient: 'required'` requires the zap's `recipient` binding, delivers the output externally, and produces an output that cannot be chained or swept. An edge with `availability: 'future'` produces an output that settles only after the initiating transaction; `route` compiles it for you, and on the authoring path it needs `lifi.zapAsync` with a bound `recipient`. Delivery timing governs custody only — a future output still reports an `amount.estimate` (`basis: 'simulated'`, an in-transaction estimate; the delivered amount arrives after settlement), with an `amount.minimum` present only when a minimum source applied. The catalog publishes no settlement duration: a deferred output's ETA rides on the compile result as each future terminal output's `delivery.etaSeconds` in `outputs` (expected seconds until the proceeds land, present only on a `delivery.when: 'future'` output and only when the protocol publishes an estimate; absence means unknown, not zero).
152
- - `sdk.client.simulate(request)` / `sdk.simulate(request)` — simulates a raw, pre-encoded transaction and returns a `SimulateResult` (discriminated union: `status: 'ok' | 'revert' | 'error'`). See [Simulating a raw transaction](#simulating-a-raw-transaction).
153
- - `sdk.client.route(request)` / `sdk.route(request)` — compiles a single from/to token pair on one chain via `POST /compose/route`, returning the same `ComposeCompileResult` as `compile()`. See [Routing a token pair](#routing-a-token-pair).
143
+ **`sdk.client` (HTTP)**
144
+
145
+ - `compile(request)`: `POST /compose`. Returns a `ComposeCompileResult` with `status: 'success'` or `status: 'partial'`.
146
+ - `route(request)`: `POST /compose/route`. Returns the same union.
147
+ - `getManifest()`: `GET /compose/manifest`, the live operation registry.
148
+ - `getChains()`: `GET /chains`. Returns `readonly ChainSummary[]` (`{ chainId, name }`), ascending by `chainId`.
149
+ - A chain is listed once the VM contract is confirmed deployed on it. `compile`, `route`, and `simulate` validate against the same condition, so they reject an unlisted chain. A listed chain can still have no routing edge for a given pair.
150
+ - `name` is a display label that can change, so key on `chainId`. A chain with no configured label reports `Chain <id>`.
151
+ - `getZapPacks(options?)`: `GET /compose/zap-packs`. Returns the routing edges grouped by protocol as `ZapPackOverview[]`. Filter with `{ protocols: 'aave' }` or an array. See [Discovering routable pairs](#discovering-routable-pairs).
152
+ - `simulate(request)`: `POST /simulate`.
153
+
154
+ The SDK caches none of these responses.
154
155
 
155
156
  **Helpers**
156
157
 
157
- - `resources.erc20(address, chainId)` / `resources.native(chainId)` — resource constructors
158
- - `guards.*` — guard factories (e.g., slippage)
159
- - `materialisers.*` — materialiser factories (directDeposit, balanceOf, call)
160
- - `preconditions.*` — precondition factories (erc20Balance, nativeBalance, erc20Allowance)
161
- - `raw.ref<T>(path)` — create a typed `$ref` pointer for use in bind slots (escape hatch for `untypedOp` outputs)
162
- - `raw.guard(kind, config?)` / `raw.materialiser(kind, config?)` — low-level factories for guards and materialisers
163
- - `buildSimulateRequest({ result, chainId, signer, trackedBalances, requirements?, block?, value? })` — assembles a `SimulateRequest` from a compile result (pure, no I/O)
164
- - `routeAmount.exact(amount)` / `routeAmount.all(simAmount)` — amount constructors for `sdk.route(...)`
165
- - `buildReachability(packs, { maxHops?, allowAsyncRoutes? })` — computes the pairs `route` can connect by chaining catalog edges (pure, no I/O). See [Discovering routable pairs](#discovering-routable-pairs).
158
+ - `resources.erc20(address, chainId)` / `resources.native(chainId)`.
159
+ - `guards.slippage`, `guards.coreNumericInvariant`.
160
+ - `materialisers.directDeposit`, `materialisers.balanceOf`, `materialisers.call`.
161
+ - `preconditions.erc20Balance`, `preconditions.nativeBalance`, `preconditions.erc20Allowance`.
162
+ - `raw.ref<T>(path)`, `raw.literal(kind, value)`, `raw.guard(kind, config?)`, `raw.materialiser(kind, config?)`: low-level constructors for cases the typed helpers do not cover.
163
+ - `routeAmount.exact(amount)` / `routeAmount.all(simAmount)`: amount constructors for `route`.
164
+ - `buildReachability(packs, { maxHops?, allowAsyncRoutes? })`: the pairs `route` can connect (pure, no I/O).
165
+ - `buildSimulateRequest(...)`: turns a compile result into a `SimulateRequest` (pure, no I/O).
166
+ - `terminalOutputs(outputs)`.
167
+ - `isComposeError`, `isComposePreparationError`, `isComposeSdkOutdatedError`, `isComposeServerOutdatedError`: type guards. `ComposeError` is exported as a type only, so narrow with these rather than `instanceof`.
166
168
 
167
169
  ## Routing a token pair
168
170
 
169
- Authoring a flow is the general path: you decide the operations and how they chain. `sdk.route(...)` is the shortcut for the case where the backend can decide for you — you hand it a from/to token pair, an amount, and a signer, and it returns the same submit-ready `ComposeCompileResult` that `compile()` returns.
171
+ `sdk.route(...)` is the shortcut for when the backend can choose the operations. You pass a from/to token pair on one chain, an amount, and a signer. It returns the same `ComposeCompileResult` that `compile()` does.
172
+
173
+ The backend plans the shortest chain of routing-catalog edges for the pair: entering or exiting a protocol position, a wrap or unwrap, a mint or burn. The route can be up to the server's hop ceiling (2 by default). The chain may open with one `lifi.swap` when the input has to be sold first; the swap counts as a hop, so at the default ceiling it precedes at most one catalog edge. That swap only targets a hub token or the underlying of the next deposit, so `route` is not a general swap aggregator.
170
174
 
171
- The backend plans a short chain of steps for that pair: up to a few edges of its routing catalog — entering or exiting a protocol position, a wrap or unwrap, a mint or burn — optionally opened by one `lifi.swap` when the input token has to be sold first. Every intermediate step settles inside the proxy in the same transaction; only the last step may pay out to a recipient or land after settlement (and the latter only with `allowAsyncRoutes`). A route that opens with a swap requires `sweepTo`, because the swap can leave a residue of the sold token in the proxy. A pair the planner cannot connect comes back as `no_route_error` whose `details` report the hop budget (`maxHops`) and whether the search was cut short (`searchTruncated`). It is not a general swap aggregator: a swap is only used as a first step into a hub token or into the underlying of the next deposit.
175
+ - Node ids are `hop0`, `hop1`, … and the last node is always `zap`, so the delivered output is `result.outputs['zap.amountOut']`.
176
+ - Every intermediate step settles inside the proxy in the same transaction. Only the last step may pay out to a recipient, or land after settlement (only with `allowAsyncRoutes`).
177
+ - A route that opens with a swap requires `sweepTo`, because the swap can leave unspent input in the proxy. Omitting it fails with `validation_error`.
172
178
 
173
179
  ```ts
174
180
  import { createComposeSdk, routeAmount } from '@lifi/composer-sdk';
@@ -181,20 +187,20 @@ const sdk = createComposeSdk({
181
187
  const result = await sdk.route({
182
188
  chainId: 1,
183
189
  fromToken: '0x0000000000000000000000000000000000000000', // native ETH
184
- toToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH — a `wrap` edge
190
+ toToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH, a `wrap` edge
185
191
  amount: routeAmount.exact(100_000_000_000_000_000n), // 0.1 ETH
186
192
  signer: '0xYourAddress...',
187
193
  slippageBps: 100,
188
194
  });
189
195
 
190
196
  if (result.status === 'success') {
191
- console.log(result.transactionRequest); // to, data, value, gasLimit
197
+ console.log(result.transactionRequest); // to, chainId, data, value, gasLimit
192
198
  }
193
199
  ```
194
200
 
195
201
  ### Discovering routable pairs
196
202
 
197
- The catalog is dynamic — protocols, chains, and token blacklists move — so discover pairs instead of hardcoding them. `getZapPacks` lists single routing edges; `route` chains up to the server's hop budget of them, so the pairs it connects are the closure of the catalog, not the catalog itself. `buildReachability` computes that closure locally with the planner's rule:
203
+ The catalog changes as protocols, chains, and token blacklists move, so discover pairs instead of hardcoding them. `getZapPacks` lists single edges; `route` chains them. `buildReachability` computes the chained closure locally, using the planner's rule:
198
204
 
199
205
  ```ts
200
206
  import { buildReachability } from '@lifi/composer-sdk';
@@ -208,56 +214,76 @@ for (const pair of reach.routableFrom(aUsdc)) {
208
214
  }
209
215
  ```
210
216
 
211
- `pair.in` and `pair.out` are the `fromToken` and `toToken` of a `route` call on `pair.in.chainId`. The rule the helper applies, and `route` enforces:
217
+ `pair.in` and `pair.out` are the `fromToken` and `toToken` of a `route` call on `pair.in.chainId`. The rule the helper applies, and the server enforces:
212
218
 
213
- - every edge except the last must leave its output in the proxy for the next edge to consume, so an edge with `recipient: 'required'` or `availability: 'future'` can only be the last hop
214
- - the last edge must be synchronous unless you pass `allowAsyncRoutes: true` to both `buildReachability` and `route`
215
- - a route never visits the same token twice, and never exceeds `maxHops`
219
+ - Every edge except the last must leave its output in the proxy for the next edge to consume. An edge with `recipient: 'required'` or `availability: 'future'` can only be the last hop.
220
+ - The last edge must be synchronous, unless you pass `allowAsyncRoutes: true` to both `buildReachability` and `route`.
221
+ - A route never visits the same token twice and never exceeds `maxHops`.
216
222
 
217
- `maxHops` defaults to 2, the deployment default; the server reports its effective value in `no_route_error.details.maxHops`, so raise the option if that differs. `inAmountMin` / `inAmountMax` bound the amount when the protocol constrains an edge; the helper cannot apply them to an intermediate hop without an amount, so a reported pair is routable structurally, exactly as a listed edge is. Routes that open with a `lifi.swap` depend on what the aggregator can sell and are not enumerable.
223
+ The helper can overstate or miss what `route` accepts in a few ways:
224
+
225
+ - `maxHops` defaults to 2, the deployment default. The server reports its effective value in `no_route_error` `details.maxHops`; pass that value if it differs.
226
+ - The helper cannot check `inAmountMin` / `inAmountMax` on an intermediate hop without an amount. A reported pair is routable structurally, just as a listed edge is.
227
+ - The server drops blacklisted tokens as intermediates, so a reported pair can still return `no_route_error`.
228
+ - Routes that open with a `lifi.swap` depend on what the aggregator can sell, so the helper does not report them.
218
229
 
219
230
  Two edge fields change how you call `route`:
220
231
 
221
- - **`recipient: 'required'`** — the protocol mints to a named receiver, so set `sweepTo` when the output should not land with the signer.
222
- - **`availability: 'future'`** — the output only exists once the transaction settles. `route` skips these unless you pass `allowAsyncRoutes: true`, so by default a pair reachable only this way is a `no_route_error` and every terminal output delivers now (`delivery.when: 'now'`). Opt in and `sweepTo` names the future output's owner, no slippage floor is applied, and you must track settlement rather than treat the transaction as completing the job. A synchronous edge still wins whenever the pair has one, so read each terminal output's `delivery.when` in `result.outputs` to know which representation you got.
232
+ - **`recipient: 'required'`**: the protocol mints to a named receiver. Set `sweepTo` when the output should not land with the signer.
233
+ - **`availability: 'future'`**: the output only exists once the transaction settles, and `route` uses such an edge only with `allowAsyncRoutes: true`. Once opted in:
234
+ - `sweepTo` names the future output's owner.
235
+ - No slippage floor applies.
236
+ - You must track settlement.
237
+ - A synchronous route still wins whenever one exists, so read each terminal output's `delivery.when`.
223
238
 
224
- ### Amounts
239
+ A future output still reports `amount.estimate` (`basis: 'simulated'`, the in-transaction estimate). The catalog carries no settlement time. The compile result carries it as `delivery.etaSeconds` on a future terminal output; that field is absent when the protocol publishes no estimate, and absence means unknown, not zero.
225
240
 
226
- The `amount` field is a discriminated union with two variants, built by `routeAmount`:
241
+ ### Amounts
227
242
 
228
- - **`routeAmount.exact(amount)`** — spend exactly `amount` of `fromToken`, in its smallest unit. Use this when you know the figure.
229
- - **`routeAmount.all(simAmount)`** — spend the signer's whole `fromToken` balance, resolved on-chain at execution time. `simAmount` is only an estimate: the quote, the price impact, and the returned approvals are computed against it, but the amount actually moved is the real balance when the transaction lands. Pass your best estimate of the balance — a wildly wrong figure produces a poor quote. Not supported for a native `fromToken`: a gas coin arrives via `msg.value`, so there is no balance to sweep, and the server rejects the request.
243
+ - **`routeAmount.exact(amount)`**: spend exactly `amount` of `fromToken`, in its smallest unit.
244
+ - **`routeAmount.all(simAmount)`**: spend the signer's whole `fromToken` balance, read on-chain at execution time.
245
+ - The quote, price impact, and returned approvals are computed against `simAmount`. The transaction moves the real balance, so pass a realistic estimate.
246
+ - Not supported for a native `fromToken`: a gas coin arrives via `msg.value`, so there is no balance to sweep.
230
247
 
231
- Both accept a `bigint` or a decimal string. `bigint` values are serialised for you.
248
+ Both accept a `bigint` or a decimal string.
232
249
 
233
250
  ### Options
234
251
 
235
- `sdk.route(...)` shares its optional fields with the authoring path, and the server applies the same defaults. Omit a field to take the default; the SDK sends only what you set.
252
+ The SDK sends only the fields you set. The server applies the defaults.
236
253
 
237
- - **`slippageBps`** — tolerance in basis points. Defaults to `100` (1%).
238
- - **`maxPriceImpactBps`** — reject the route if the priced impact exceeds this. No cap by default.
239
- - **`integratorFeeBps`** — your fee, in basis points, taken from the input amount before the route executes. Requires an integration-scoped `apiKey`; a non-zero value without one is rejected.
240
- - **`referrer`** — attribution string passed to the routing provider.
241
- - **`sweepTo`** — address that receives the output and any leftover dust. Accepts a literal address or `{ $ref: 'context.sender' }`. Defaults to the signer.
242
- - **`checkOnChainAllowances`** — when `true`, the server reads current allowances and omits approvals that are already sufficient. Defaults to `false`.
243
- - **`simulationPolicy`** — `'strict'` (default) or `'allow-revert'`, exactly as on the authoring path. See [Simulation policy and partial results](#simulation-policy-and-partial-results).
244
- - **`simulateUserProgram`** — when `true`, additionally simulates the final user-facing transaction after the structured simulation succeeds. Defaults to `false`.
245
- - **`allowAsyncRoutes`** — when `true`, accept a route whose output arrives only after the initiating transaction settles. Defaults to `false`, which leaves such a pair a `no_route_error` and guarantees every terminal output delivers now (`delivery.when: 'now'`). Set it only if you track settlement and can live without a slippage floor.
254
+ - **`slippageBps`**: default `100` (1%). An end-to-end tolerance on the route's final output, not a per-hop one.
255
+ - **`maxPriceImpactBps`**: reject the route when its priced impact exceeds this. Default `500`; `0` disables the check.
256
+ - **`integratorFeeBps`**: your fee, taken from the input amount. Range 0 to 9000, default `0`. A non-zero value requires an integration-scoped `apiKey`.
257
+ - **`referrer`**: opaque tag forwarded to the underlying quote provider.
258
+ - **`sweepTo`**: owner of everything the route leaves behind. An address or `{ $ref: 'context.sender' }`; `context.executionAddress` is rejected. Defaults to the signer, except that a route with a swap requires it.
259
+ - **`checkOnChainAllowances`**: omit approvals that current allowances already cover. Default `false`.
260
+ - **`simulationPolicy`**: `'strict'` (default) or `'allow-revert'`. See [Simulation policy and partial results](#simulation-policy-and-partial-results).
261
+ - **`simulateUserProgram`**: also simulate the final user-facing transaction after the structured simulation succeeds. Default `false`.
262
+ - **`allowAsyncRoutes`**: accept a route whose output arrives after the transaction settles. Default `false`, which guarantees every terminal output is `delivery.when: 'now'`.
246
263
 
247
264
  ### When to author a flow instead
248
265
 
249
- `route` gives you a short server-planned chain: the server picks the catalog edges (and at most one opening swap) for you. Reach for `sdk.flow(...)` when you need anything it does not express: a chain you choose yourself or one longer than the server's hop ceiling, splitting one input across several destinations, guards on intermediate outputs, explicit preconditions, arithmetic, or funding from proxy-held balances. Swapping WETH to USDC and then depositing the USDC into Aave is a route when the server can plan it (USDC is the deposit's underlying) and a flow when you want to control the swap yourself or add steps around it; depositing USDC straight into Aave is a route. An asynchronous position (`availability: 'future'`) as the destination is a route once you pass `allowAsyncRoutes: true` — the server picks `lifi.zapAsync` for you — but authoring the flow yourself is still the way to combine it with other steps.
266
+ Use `sdk.flow(...)` for anything `route` cannot express:
267
+
268
+ - a chain you choose yourself, or one longer than the hop ceiling
269
+ - one input split across several destinations
270
+ - guards on intermediate outputs
271
+ - explicit preconditions or arithmetic
272
+ - funding from proxy-held balances
273
+ - combining an asynchronous position with other steps
274
+
275
+ For example, swapping WETH to USDC and depositing the USDC into Aave is a route when the planner can connect it, and a flow when you want to control the swap or add steps.
250
276
 
251
277
  ### Errors
252
278
 
253
- `route` maps failures the same way `compile` does — see [Error handling](#error-handling). Two are specific to this path:
279
+ `route` maps failures like `compile` does (see [Error handling](#error-handling)). Two outcomes are specific to it:
254
280
 
255
- - **`NOT_FOUND`** with `kind: 'no_route_error'` — the planner cannot connect the pair on that chain within `details.maxHops` catalog edges; `details.searchTruncated` reports whether the search hit its work bound. `buildReachability(await sdk.client.getZapPacks())` reports which pairs do connect.
256
- - **`VALIDATION_ERROR`** — the pair is routable but the request is rejected, for example a price impact above `maxPriceImpactBps`, or a simulation revert under the default `'strict'` policy.
281
+ - **`NOT_FOUND`** with `kind: 'no_route_error'`: the planner cannot connect the pair within `details.maxHops` edges. `details.searchTruncated` reports whether the search hit its work bound.
282
+ - **`VALIDATION_ERROR`**: the pair connects but the request is rejected. Examples: price impact above `maxPriceImpactBps`, or a simulated revert under `'strict'`.
257
283
 
258
284
  ## Simulation policy and partial results
259
285
 
260
- By default, the Compose backend simulates the compiled transaction and returns an error (HTTP 422) if simulation detects a revert. You can opt into receiving a **partial result** instead by passing `simulationPolicy: 'allow-revert'`:
286
+ The backend simulates every compiled transaction. Under the default `simulationPolicy: 'strict'`, a revert throws a `ComposeError` with code `VALIDATION_ERROR` (HTTP 422). With `'allow-revert'` you get a **partial result** (HTTP 206) instead:
261
287
 
262
288
  ```ts
263
289
  const result = await builder.compile({
@@ -267,57 +293,35 @@ const result = await builder.compile({
267
293
  });
268
294
 
269
295
  if (result.status === 'success') {
270
- // Simulation succeeded. transactionRequest includes gasLimit.
271
- const tx = result.transactionRequest;
272
- console.log(tx.gasLimit); // string
296
+ console.log(result.transactionRequest?.gasLimit); // string
273
297
  } else {
274
- // result.status === 'partial'
275
- // Simulation reverted, but a transaction is still available (without gasLimit).
298
+ // status === 'partial': the transaction is returned without gasLimit;
299
+ // estimate gas yourself.
276
300
  console.log(result.error.kind); // 'simulation_revert'
277
- console.log(result.error.message); // human-readable revert description
301
+ console.log(result.error.message);
278
302
 
279
- // Revert diagnostics
280
303
  const revert = result.simulationRevert;
281
- console.log(revert.code); // e.g. 3
282
- console.log(revert.rawErrorBytes); // raw ABI-encoded error
283
-
284
- // Decoded error candidates (when available)
285
- if (revert.decodeResult?.errorCandidates) {
286
- for (const c of revert.decodeResult.errorCandidates) {
287
- console.log(c.decodedErrorSignature, c.decodedParams);
288
- }
304
+ console.log(revert.code, revert.rawErrorBytes);
305
+ for (const c of revert.decodeResult?.errorCandidates ?? []) {
306
+ console.log(c.decodedErrorSignature, c.decodedParams);
289
307
  }
290
308
 
291
- // The transactionRequest is still usable — the caller must estimate gas themselves.
292
309
  const tx = result.transactionRequest;
293
310
  console.log(tx.to, tx.data, tx.value);
294
311
  }
295
312
  ```
296
313
 
297
- The `simulationPolicy` field accepts two values:
298
-
299
- - `'strict'` (default) — revert causes a thrown `ComposeError` with code `VALIDATION_ERROR`
300
- - `'allow-revert'` — revert returns a partial result with `status: 'partial'`
301
-
302
- You can also pass `checkOnChainAllowances: true` to have the server filter the returned `approvals` array against current on-chain allowances, omitting approvals that are already sufficient:
303
-
304
- ```ts
305
- const result = await builder.compile({
306
- signer: OWNER,
307
- inputs: { amountIn: materialisers.balanceOf({ owner: OWNER }) },
308
- checkOnChainAllowances: true,
309
- });
310
- ```
314
+ Pass `checkOnChainAllowances: true` to have the server drop entries from `approvals` that current on-chain allowances already cover.
311
315
 
312
316
  ## Simulating a raw transaction
313
317
 
314
- `sdk.client.simulate(...)` (and the `sdk.simulate(...)` pass-through) answer a question the compile pipeline does not: _if I send this exact transaction, how do specific token balances change and how much gas does it burn?_ It takes a raw, pre-encoded transaction (a `to`, hex `data`, optional native `value`), funds a sender, runs it in one `eth_call`, and reports the watched balances before/after, their signed deltas, and the inner-call gas.
318
+ `sdk.client.simulate(...)` (and `sdk.simulate(...)`) answers a question compile does not: if I send this exact transaction, how do specific balances change, and how much gas does it burn? It takes a pre-encoded transaction (`to`, hex `data`, optional `value`). It funds the sender as `requirements` describe, runs the transaction in one `eth_call`, and reports the watched balances before and after, their signed deltas, and the inner-call gas.
315
319
 
316
320
  ```ts
317
321
  import { createComposeSdk } from '@lifi/composer-sdk';
318
322
 
319
323
  const sdk = createComposeSdk({
320
- baseUrl: 'https://li.quest',
324
+ baseUrl: 'https://composer.li.quest',
321
325
  apiKey: process.env.LIFI_API_KEY!,
322
326
  });
323
327
 
@@ -326,13 +330,13 @@ const result = await sdk.client.simulate({
326
330
  from: '0x1111111111111111111111111111111111111111',
327
331
  to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
328
332
  data: '0xa9059cbb...', // pre-encoded transfer calldata
329
- value: 0n, // bigint accepted; serialised to "0"
333
+ value: 0n,
330
334
  requirements: [
331
335
  {
332
336
  type: 'Erc20Balance',
333
337
  wallet: '0x1111111111111111111111111111111111111111',
334
338
  token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
335
- balance: 1_000_000n, // bigint accepted
339
+ balance: 1_000_000n,
336
340
  },
337
341
  ],
338
342
  trackedBalances: [
@@ -349,25 +353,27 @@ const result = await sdk.client.simulate({
349
353
 
350
354
  switch (result.status) {
351
355
  case 'ok':
352
- // Successful simulation.
353
356
  console.log(result.gasUsed, result.deltas);
354
357
  break;
355
358
  case 'revert':
356
- // The simulation ran but the transaction reverted on-chain — NOT an error.
359
+ // The simulation ran and the transaction reverted. Not an error.
357
360
  console.log(result.revertReason, result.decodeResult);
358
361
  break;
359
362
  case 'error':
360
- // The request was well-formed but the simulation could not be set up/run.
363
+ // Well-formed request, but the simulation could not be set up or run.
361
364
  console.log(result.message);
362
365
  break;
363
366
  }
364
367
  ```
365
368
 
366
- `requirements` is the funding-instruction union (`Erc20Balance`, `NativeBalance`, `Erc20Allowance`); use the zero address as a `trackedBalances` `token` to watch native balance. Amount fields accept `bigint` (serialised to decimal strings) or strings. The caps `SIMULATE_MAX_TRACKED_BALANCES` and `SIMULATE_MAX_REQUIREMENTS` (both `40`) are exported for reference.
367
-
368
- Unlike `compile`, a `revert` is returned (not thrown): a revert is a _successful simulation_ whose execution reverted. Only transport failures and HTTP 400/401/403/404/429/5xx throw `ComposeError`.
369
+ - `requirements` entries are `Erc20Balance`, `NativeBalance`, or `Erc20Allowance`. Use the zero address as a `trackedBalances` token to watch the native balance.
370
+ - Amount fields accept `bigint` or decimal strings.
371
+ - `SIMULATE_MAX_TRACKED_BALANCES` and `SIMULATE_MAX_REQUIREMENTS` (both `40`) are exported.
372
+ - A `revert` is returned, not thrown. Only transport failures and HTTP 400/401/403/404/429/5xx throw `ComposeError`.
373
+ - `gasUsed` is inner-call execution gas only; it excludes the 21000 base cost and calldata gas.
374
+ - EOA-only behaviour, such as `msg.sender == tx.origin` checks, is not simulated faithfully, because the call runs through injected VM bytecode rather than a real EOA.
369
375
 
370
- To simulate a transaction you just compiled, `buildSimulateRequest(...)` assembles the request without re-typing the transaction fields. `chainId` and `signer` are required (a compile result carries neither), and `trackedBalances`/`requirements` cannot be inferred from a flow:
376
+ To simulate a transaction you just compiled, `buildSimulateRequest(...)` fills in the transaction fields from the compile result. A compile result carries neither `chainId` nor `signer`, so pass both. Nothing in a flow says which balances to watch, so pass `trackedBalances` and any `requirements` too. It throws when the result has no single `transactionRequest` (a flow funded by an external action); submit that result's `actions` instead.
371
377
 
372
378
  ```ts
373
379
  import { buildSimulateRequest } from '@lifi/composer-sdk';
@@ -387,13 +393,11 @@ const req = buildSimulateRequest({
387
393
  const sim = await sdk.client.simulate(req);
388
394
  ```
389
395
 
390
- Two caveats from the endpoint: `gasUsed` is the inner-call execution gas only (it excludes the 21000 base tx cost and calldata gas), and EOA-only behaviour (e.g. `msg.sender == tx.origin` checks) is not faithfully simulated because the call runs through injected VM bytecode rather than a real EOA. See [`docs/references/simulate-endpoint.md`](../../../docs/references/simulate-endpoint.md) for the full contract.
391
-
392
- Two runnable examples cover both paths: [`src/examples/simulateRawTransaction.ts`](src/examples/simulateRawTransaction.ts) builds a raw `SimulateRequest` directly, and [`src/examples/simulateCompiledSwap.ts`](src/examples/simulateCompiledSwap.ts) shows the end-to-end compile → `buildSimulateRequest` → `simulate` arc.
396
+ Runnable versions: [`src/examples/simulateRawTransaction.ts`](src/examples/simulateRawTransaction.ts) and [`src/examples/simulateCompiledSwap.ts`](src/examples/simulateCompiledSwap.ts).
393
397
 
394
398
  ## Error handling
395
399
 
396
- All SDK errors are thrown as `ComposeError` with a `code` property:
400
+ HTTP and transport failures are thrown as a `ComposeError`. Builder mistakes (a duplicate node id, a missing output port) throw a plain `Error` at the call site. Narrow with `isComposeError`:
397
401
 
398
402
  ```ts
399
403
  import { isComposeError } from '@lifi/composer-sdk';
@@ -402,13 +406,26 @@ try {
402
406
  const result = await sdk.client.compile(request);
403
407
  } catch (err) {
404
408
  if (isComposeError(err)) {
405
- console.error(err.code, err.message);
406
- // Codes: VALIDATION_ERROR, SERVER_ERROR, RATE_LIMITED, NETWORK_ERROR, ...
409
+ console.error(err.code, err.status, err.kind, err.message);
410
+ console.error(err.path, err.port, err.fix); // when the server names a location or a fix
407
411
  }
408
412
  }
409
413
  ```
410
414
 
411
- A `preparation_error` (HTTP 422) means some prepared ops failed while others succeeded — a mixed basket with an unroutable leg. Narrow with `isComposePreparationError` to read which ops failed, then drop those `callId`s from the flow and resubmit:
415
+ | `code` | HTTP status | Typical cause |
416
+ | ------------------ | ----------- | --------------------------------------------------------------- |
417
+ | `VALIDATION_ERROR` | 400, 422 | Malformed or rejected flow or request, simulated revert, guard |
418
+ | `UNAUTHENTICATED` | 401 | Missing or invalid API key |
419
+ | `FORBIDDEN` | 403 | The server refuses the request |
420
+ | `NOT_FOUND` | 404 | `no_route_error` |
421
+ | `RATE_LIMITED` | 429 | Too many requests |
422
+ | `SERVER_ERROR` | 5xx | Backend failure or a dependency temporarily unavailable |
423
+ | `NETWORK_ERROR` | none | The request never got a response |
424
+ | `UNKNOWN_ERROR` | other | Unexpected status |
425
+
426
+ `kind` is the backend's finer-grained classification, e.g. `simulation_revert`, `guard_error`, or `price_impact_exceeded`. When `kind` is `simulation_revert`, `err.details` has the same shape as `result.simulationRevert`.
427
+
428
+ A `preparation_error` (HTTP 422) means some prepared ops (ops that fetch external quotes at compile time, such as `lifi.swap`) failed while others succeeded, e.g. a mixed basket with one unroutable leg. Narrow with `isComposePreparationError`, drop the failed node ids from the flow, and resubmit:
412
429
 
413
430
  ```ts
414
431
  import { isComposePreparationError } from '@lifi/composer-sdk';
@@ -420,72 +437,68 @@ try {
420
437
  for (const op of err.failedOps) {
421
438
  console.error(op.callId, op.op, op.kind, op.message);
422
439
  }
423
- // Prepared ops that succeeded. Both arrays list prepared ops only, so
424
- // retry by removing the `failedOps` callIds from the original flow.
440
+ // Only the prepared ops that succeeded, not the rest of the flow. To retry,
441
+ // remove the failedOps callIds from the original flow.
425
442
  console.error('prepared ok:', err.succeededOps);
426
443
  }
427
444
  }
428
445
  ```
429
446
 
430
- See [`docs/sdk-interface.md`](docs/sdk-interface.md#per-op-preparation-failures) for the full error surface.
447
+ **Version mismatch.** `compile` and `route` fail with `kind: 'sdk_outdated'` when this SDK is older than the oldest version the backend serves. They fail with `kind: 'server_outdated'` when the SDK is newer than the backend in any version component, which happens between an npm release and the backend rollout. Both come back as `VALIDATION_ERROR`. `isComposeSdkOutdatedError(err)` exposes `err.sdkOutdated.minimumSdkVersion`; `isComposeServerOutdatedError(err)` exposes `err.serverOutdated.serverVersion`. On `server_outdated`, pin both packages to the server's version or wait for the rollout.
431
448
 
432
449
  ## Examples
433
450
 
434
- The `src/examples/` directory contains complete working examples:
435
-
436
- - **lifiSwap** — Single token swap (WETH to USDC)
437
- - **lifiZap** — Swap into a DeFi position
438
- - **lifiZapAsync** — Zap into a position whose output settles after the initiating transaction (future availability)
439
- - **swapAndZap** — Multi-step: swap then deposit
440
- - **splitAndZap** — Split a resource and zap each portion into a different vault
441
- - **splitWithArithmetic** — Split then verify with add/subtract/assertEqual assertions
442
- - **dustSweep** — Split and partially use tokens, sweep leftover dust back to sender
443
- - **depositFromProxy** — Read tokens already on the proxy via `balanceOf`, with a precondition guard
444
- - **approveAndDeposit** — Approve a vault, deposit, and graduate shares via `asResource`
445
- - **consolidateToUsdc** — Consolidate multiple tokens into USDC
446
- - **consolidateToEth** — Consolidate multiple tokens into ETH
447
- - **swapToRecipient** — Swap and send to a different address
448
- - **swapWithBalanceCheck** — Swap with balance precondition
449
- - **swapWithOutputValidation** — Swap with computed slippage bounds using bpsDown/bpsUp/assertInRange
450
- - **rawCallWithArithmetic** — Query a contract with pre-encoded calldata, then scale with multiply/divide
451
- - **readContractState** — Compare peek (compile-time), staticCall (execution-time), and balanceOf (resource)
452
- - **swapWithAllowRevert** — Swap with `simulationPolicy: 'allow-revert'` and handle the `ComposeCompileResult` discriminated union
453
- - **swapWithFee** — Swap while collecting an integrator fee via `integratorFeeBps` (requires an integration-scoped `apiKey`)
454
- - **transferTokens** — Transfer ERC-20 tokens from the proxy to an arbitrary recipient
455
- - **callContract** — Call an arbitrary contract (ERC-4626 redeem; reward claim) without a dedicated typed op
456
- - **aaveRepay** — Repay an Aave v3 variable-rate debt, sweeping the unspent residual back to the sender
457
- - **aaveRepayWithATokens** — Repay Aave v3 debt by burning aToken collateral already held by the proxy
458
- - **aaveClaimRewards** — Claim accrued Aave rewards and forward the claimed amount to a recipient
459
- - **aaveSetEMode** — Switch the proxy's Aave v3 eMode category
460
- - **untypedOpWithTypedRef** — Insert an untyped operation node via `untypedOp`, then bridge its output into typed operations using `raw.ref<T>()`
461
- - **routeTokenPair** — Compile a from/to token pair via `sdk.route(...)`, with both the `exact` and `all` amount variants
462
- - **supportedChains** — Cache `sdk.getSupportedChains()`, then preflight a chain and label it from the response's `name`, so an unsupported one fails locally with a message naming the alternatives
451
+ Each file in `src/examples/` is a complete flow:
452
+
453
+ - **lifiSwap**: single token swap (WETH to USDC)
454
+ - **lifiZap**: swap into a DeFi position
455
+ - **lifiZapAsync**: zap into a position whose output settles after the initiating transaction
456
+ - **swapAndZap**: swap, then deposit
457
+ - **splitAndZap**: split a resource and zap each part into a different vault
458
+ - **splitWithArithmetic**: split, then check with add/subtract/assertEqual
459
+ - **dustSweep**: split, use part of the tokens, sweep the rest back to the sender
460
+ - **depositFromProxy**: spend tokens already on the proxy via `balanceOf`, with a precondition
461
+ - **approveAndDeposit**: approve a vault, deposit, and turn the shares into a resource via `asResource`
462
+ - **consolidateToUsdc** / **consolidateToEth**: consolidate several tokens into one
463
+ - **swapToRecipient**: swap and send to another address
464
+ - **swapWithBalanceCheck**: swap with a balance precondition
465
+ - **swapWithOutputValidation**: swap with computed bounds via bpsDown/bpsUp/assertInRange
466
+ - **invariantChecks**: swap, then assert the output with `invariant.gte` and an allowance with `invariant.allowanceAtLeast`
467
+ - **rawCallWithArithmetic**: query a contract with pre-encoded calldata, then scale the result
468
+ - **readContractState**: compare `peek` (compile time), `staticCall` (execution time), and `balanceOf` (resource)
469
+ - **emitCustomEvent**: emit a custom event with `core.emitEvent2`; `bytes32` ports accept any static 32-byte handle
470
+ - **swapWithAllowRevert**: handle both branches of `ComposeCompileResult` under `'allow-revert'`
471
+ - **swapWithFee**: collect an integrator fee via `integratorFeeBps` (requires an integration-scoped `apiKey`)
472
+ - **transferTokens**: transfer ERC-20 tokens from the proxy to any recipient
473
+ - **callContract**: call an arbitrary contract (ERC-4626 redeem, reward claim) without a typed op
474
+ - **flySwap**: on Base, exit a Pendle PT via `lifi.zap`, then swap all proceeds to WETH with `fly.swap`
475
+ - **aaveRepay**: repay Aave v3 variable debt and sweep the unspent residual to the sender
476
+ - **aaveRepayWithATokens**: repay Aave v3 debt by burning aTokens held by the proxy
477
+ - **aaveClaimRewards**: claim Aave rewards and forward them to a recipient
478
+ - **aaveSetEMode**: switch the proxy's Aave v3 eMode category
479
+ - **untypedOpWithTypedRef**: add a node with `untypedOp`, then use its output in typed ops via `raw.ref<T>()`
480
+ - **routeTokenPair**: `sdk.route(...)` with both the `exact` and `all` amount variants
481
+ - **supportedChains**: cache `sdk.getSupportedChains()`, then fail an unsupported chain locally with a message naming the alternatives
482
+ - **simulateRawTransaction** / **simulateCompiledSwap**: raw and compiled simulation
463
483
 
464
484
  ## Staging channel
465
485
 
466
- Some operations exist on the Compose backend but are deliberately held back from the default SDK — for example an op whose required contract changes are not yet live on production. These **staged** operations are published on a separate npm dist-tag, `staging`:
486
+ Some operations run on the backend but are held back from the default SDK, for example because their contract changes are not live in production yet. These **staged** operations are published on the `staging` npm dist-tag:
467
487
 
468
488
  ```bash
469
489
  npm install @lifi/composer-sdk@staging @lifi/compose-spec@staging
470
490
  ```
471
491
 
472
- The `staging` build includes the not-yet-public operations in its typed surface (e.g. `lifi.flashloanRepay`), so you can author flows against them with full type-checking. The default install (`@lifi/composer-sdk`, the `latest` dist-tag) never exposes them.
473
-
474
- A staged operation only runs if the backend you point at actually has it enabled. The SDK has no default backend — you supply one per instance:
492
+ The `staging` build types the staged operations (e.g. `lifi.flashloanRepay`). The default install never exposes them. A staged operation only runs on a backend that has it enabled, so point `baseUrl` at one:
475
493
 
476
494
  ```ts
477
495
  const sdk = createComposeSdk({
478
- baseUrl: '<staging-backend-url>', // a backend that has the staged ops enabled
496
+ baseUrl: '<staging-backend-url>',
479
497
  apiKey: process.env.LIFI_API_KEY!,
480
498
  });
481
499
  ```
482
500
 
483
- Calling a staged operation against a backend that does not have it enabled fails at runtime as a service-level error; the typed surface being present does not guarantee backend availability.
484
-
485
- The channel and the backend URL are independent:
486
-
487
- - The `staging` **channel** is durable — it is a permanent property of `main`, and which operations it carries rotates over time as ops graduate to `latest`.
488
- - The `baseUrl` is **per-environment and disposable** — it is a runtime argument, not a build-time property, so the same staging build can target whichever backend currently has the ops enabled.
501
+ Against a backend without the operation, the call fails at runtime; having the types does not mean the backend supports the operation. The channel is permanent, and its contents rotate as ops graduate to `latest`. The backend URL is a runtime argument, so one staging build can target whichever backend has the ops enabled.
489
502
 
490
503
  ## License
491
504
 
package/dist/client.cjs CHANGED
@@ -25,7 +25,7 @@ module.exports = __toCommonJS(client_exports);
25
25
  var import_compose_spec = require("@lifi/compose-spec");
26
26
  var import_errors = require("./errors.js");
27
27
  var import_responseSchemas = require("./responseSchemas.js");
28
- const SDK_VERSION = true ? "0.6.2" : "dev";
28
+ const SDK_VERSION = true ? "0.6.3" : "dev";
29
29
  const SDK_VERSION_TRIPLE = (0, import_compose_spec.parseVersion)(SDK_VERSION);
30
30
  const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
31
31
  const isNonNullObject = (v) => typeof v === "object" && v !== null;
package/dist/client.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  parseCompilePartialEnvelope,
10
10
  parseSimulateResult
11
11
  } from "./responseSchemas.js";
12
- const SDK_VERSION = true ? "0.6.2" : "dev";
12
+ const SDK_VERSION = true ? "0.6.3" : "dev";
13
13
  const SDK_VERSION_TRIPLE = parseVersion(SDK_VERSION);
14
14
  const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
15
15
  const isNonNullObject = (v) => typeof v === "object" && v !== null;
package/dist/sdk.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sdk.ts"],"sourcesContent":["import type {\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeRouteRequest,\n InputSpec,\n SimulateRequest,\n SimulateResult,\n} from '@lifi/compose-spec';\n\nimport {\n createFlowBuilderCore,\n type FlowBuilderCore,\n type FlowOptions,\n type TypedFlow,\n} from './authoring/FlowBuilderCore.js';\nimport { type ComposeClient, createComposeClient } from './client.js';\nimport type { ChainSummary } from './discovery.js';\nimport {\n bindGeneratedOps,\n type GeneratedOps,\n} from './generated/operations.generated.js';\nimport { buildRun, type ComposeRunInput } from './run/inputs.js';\nimport type { InputSchema } from './types.js';\n\nexport type { ComposeRunInput };\n\n/**\n * A flow builder augmented with generated operation methods and a `compile` method\n * that submits the flow to the Compose backend for compilation.\n *\n * Created via {@link ComposeSdk.flow}.\n *\n * @typeParam T - The input schema describing the flow's required inputs.\n */\nexport type FlowBuilder<T extends InputSchema = InputSchema> =\n FlowBuilderCore<T> &\n GeneratedOps & {\n /**\n * Builds the flow document from the current builder state and submits it\n * to the Compose API for compilation.\n *\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns The compiled transaction calldata and metadata.\n * @throws {@link ComposeError} on network, validation, or server errors.\n *\n * @example\n * ```ts\n * const result = await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * console.log(result.calldata);\n * ```\n */\n readonly compile: (\n run: ComposeRunInput<T>,\n ) => Promise<ComposeCompileResult>;\n };\n\n/**\n * Configuration for creating a Compose SDK instance.\n */\nexport interface ComposeSdkOptions {\n /** Base URL of the Compose API (e.g. `\"https://li.quest\"`). */\n readonly baseUrl: string;\n /** Optional custom `fetch` implementation. Defaults to `globalThis.fetch`. */\n readonly fetch?: typeof globalThis.fetch;\n /** LI.FI API key, sent as the `x-lifi-api-key` header on every request. Required — the Compose API rejects unauthenticated requests. */\n readonly apiKey: string;\n}\n\n/**\n * The top-level Compose SDK interface.\n *\n * Provides methods to build flows, compile them into executable calldata, and\n * interact with the Compose API directly.\n *\n * @example\n * ```ts\n * import { createComposeSdk, resources, materialisers } from '@lifi/composer-sdk';\n *\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n *\n * const builder = sdk.flow(1, {\n * inputs: { usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1) },\n * });\n *\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.usdc },\n * config: { resourceOut: resources.native(1) },\n * });\n *\n * const result = await builder.compile({\n * inputs: { usdc: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * ```\n */\nexport interface ComposeSdk {\n /** Low-level HTTP client for the Compose API. */\n readonly client: ComposeClient;\n /**\n * Creates a new flow builder targeting the given chain.\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilder} with generated operation methods and a `compile` method.\n * @throws Error if any resource input's `chainId` doesn't match the flow's `chainId`.\n */\n readonly flow: <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n ) => FlowBuilder<T>;\n /**\n * Fetches the chains this Compose deployment supports, ascending by\n * `chainId`. Thin pass-through to {@link ComposeClient.getChains}; see it\n * for what \"supported\" means and for the caching caveat.\n *\n * Each entry pairs the `chainId` with a display `name`, so one call both\n * gates a chain selector and labels it. Key on `chainId`; `name` is a\n * deployment-maintained label that can change.\n *\n * Use it to fail fast before building a flow for a chain the server would\n * reject.\n *\n * @returns The supported chains, ascending by `chainId`.\n * @throws {@link ComposeError} on network or server errors.\n *\n * @example\n * ```ts\n * const chains = await sdk.getSupportedChains();\n * if (!chains.some((c) => c.chainId === 8453)) {\n * throw new Error('Base is not supported');\n * }\n * for (const { chainId, name } of chains) {\n * console.log(`${name} (${chainId})`);\n * }\n * ```\n */\n readonly getSupportedChains: () => Promise<readonly ChainSummary[]>;\n /**\n * Builds a raw compile request without sending it. Useful for inspecting the\n * request payload or submitting it through a custom transport.\n * @param flow - A built flow document (from `builder.build()`).\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns A {@link ComposeCompileRequest} ready to send to the Compose API.\n */\n readonly request: <T extends InputSchema>(\n flow: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ) => ComposeCompileRequest;\n /**\n * Compiles a token pair on one chain — as of today a single zap step over\n * one routing-catalog edge. Thin pass-through to\n * {@link ComposeClient.route}; see it for the current single-step limit,\n * when to prefer `flow(...).compile(...)`, the `success` / `partial`\n * discrimination, and error behaviour. Build the `amount` with\n * `routeAmount.exact(...)` / `routeAmount.all(...)`.\n *\n * @param request - The from/to token pair, amount, signer, and route options.\n * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly route: (\n request: ComposeRouteRequest,\n ) => Promise<ComposeCompileResult>;\n /**\n * Simulates a raw, pre-encoded transaction. Thin pass-through to\n * {@link ComposeClient.simulate}; see it for the `ok` / `revert` / `error`\n * result discrimination and error behaviour. Pair with `buildSimulateRequest`\n * to assemble the request from a compile result.\n *\n * @param request - The raw transaction plus funding `requirements` and\n * `trackedBalances`.\n * @returns A {@link SimulateResult} (`ok` / `revert` / `error`).\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly simulate: (request: SimulateRequest) => Promise<SimulateResult>;\n}\n\n/**\n * Creates a new Compose SDK instance.\n *\n * @param options - SDK configuration including the API base URL, required API key, and optional fetch implementation.\n * @returns A {@link ComposeSdk} instance.\n *\n * @example\n * ```ts\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n * ```\n */\nexport const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => {\n const composeClient = createComposeClient(options);\n\n const request = <T extends InputSchema>(\n flowDoc: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ): ComposeCompileRequest => ({\n flow: flowDoc,\n run: buildRun({\n inputs: run.inputs as Record<string, InputSpec>,\n preconditions: run.preconditions,\n signer: run.signer,\n assumptions: run.assumptions as Record<string, bigint> | undefined,\n referrer: run.referrer,\n integratorFeeBps: run.integratorFeeBps,\n maxPriceImpactBps: run.maxPriceImpactBps,\n sweepTo: run.sweepTo,\n simulationPolicy: run.simulationPolicy,\n simulateUserProgram: run.simulateUserProgram,\n checkOnChainAllowances: run.checkOnChainAllowances,\n }),\n });\n\n const flow = <T extends InputSchema>(\n chainId: number,\n opts: FlowOptions<T>,\n ): FlowBuilder<T> => {\n const core = createFlowBuilderCore(chainId, opts);\n const builder = Object.assign(core, bindGeneratedOps(core));\n\n const compile = async (\n run: ComposeRunInput<T>,\n ): Promise<ComposeCompileResult> => {\n const flowDoc = builder.build();\n const req = request(flowDoc, run);\n return composeClient.compile(req);\n };\n\n return Object.assign(builder, { compile });\n };\n\n return {\n client: composeClient,\n flow,\n getSupportedChains: composeClient.getChains,\n request,\n route: composeClient.route,\n simulate: composeClient.simulate,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,6BAKO;AACP,oBAAwD;AAExD,kCAGO;AACP,oBAA+C;AAyKxC,MAAM,mBAAmB,CAAC,YAA2C;AAC1E,QAAM,oBAAgB,mCAAoB,OAAO;AAEjD,QAAM,UAAU,CACd,SACA,SAC2B;AAAA,IAC3B,MAAM;AAAA,IACN,SAAK,wBAAS;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,eAAe,IAAI;AAAA,MACnB,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,kBAAkB,IAAI;AAAA,MACtB,mBAAmB,IAAI;AAAA,MACvB,SAAS,IAAI;AAAA,MACb,kBAAkB,IAAI;AAAA,MACtB,qBAAqB,IAAI;AAAA,MACzB,wBAAwB,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,CACX,SACA,SACmB;AACnB,UAAM,WAAO,8CAAsB,SAAS,IAAI;AAChD,UAAM,UAAU,OAAO,OAAO,UAAM,8CAAiB,IAAI,CAAC;AAE1D,UAAM,UAAU,OACd,QACkC;AAClC,YAAM,UAAU,QAAQ,MAAM;AAC9B,YAAM,MAAM,QAAQ,SAAS,GAAG;AAChC,aAAO,cAAc,QAAQ,GAAG;AAAA,IAClC;AAEA,WAAO,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,oBAAoB,cAAc;AAAA,IAClC;AAAA,IACA,OAAO,cAAc;AAAA,IACrB,UAAU,cAAc;AAAA,EAC1B;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/sdk.ts"],"sourcesContent":["import type {\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeRouteRequest,\n InputSpec,\n SimulateRequest,\n SimulateResult,\n} from '@lifi/compose-spec';\n\nimport {\n createFlowBuilderCore,\n type FlowBuilderCore,\n type FlowOptions,\n type TypedFlow,\n} from './authoring/FlowBuilderCore.js';\nimport { type ComposeClient, createComposeClient } from './client.js';\nimport type { ChainSummary } from './discovery.js';\nimport {\n bindGeneratedOps,\n type GeneratedOps,\n} from './generated/operations.generated.js';\nimport { buildRun, type ComposeRunInput } from './run/inputs.js';\nimport type { InputSchema } from './types.js';\n\nexport type { ComposeRunInput };\n\n/**\n * A flow builder augmented with generated operation methods and a `compile` method\n * that submits the flow to the Compose backend for compilation.\n *\n * Created via {@link ComposeSdk.flow}.\n *\n * @typeParam T - The input schema describing the flow's required inputs.\n */\nexport type FlowBuilder<T extends InputSchema = InputSchema> =\n FlowBuilderCore<T> &\n GeneratedOps & {\n /**\n * Builds the flow document from the current builder state and submits it\n * to the Compose API for compilation.\n *\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns The compiled transaction calldata and metadata.\n * @throws {@link ComposeError} on network, validation, or server errors.\n *\n * @example\n * ```ts\n * const result = await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * console.log(result.calldata);\n * ```\n */\n readonly compile: (\n run: ComposeRunInput<T>,\n ) => Promise<ComposeCompileResult>;\n };\n\n/**\n * Configuration for creating a Compose SDK instance.\n */\nexport interface ComposeSdkOptions {\n /** Base URL of the Compose API (e.g. `\"https://li.quest\"`). */\n readonly baseUrl: string;\n /** Optional custom `fetch` implementation. Defaults to `globalThis.fetch`. */\n readonly fetch?: typeof globalThis.fetch;\n /** LI.FI API key, sent as the `x-lifi-api-key` header on every request. Required — the Compose API rejects unauthenticated requests. */\n readonly apiKey: string;\n}\n\n/**\n * The top-level Compose SDK interface.\n *\n * Provides methods to build flows, compile them into executable calldata, and\n * interact with the Compose API directly.\n *\n * @example\n * ```ts\n * import { createComposeSdk, resources, materialisers } from '@lifi/composer-sdk';\n *\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n *\n * const builder = sdk.flow(1, {\n * inputs: { usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1) },\n * });\n *\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.usdc },\n * config: { resourceOut: resources.native(1) },\n * });\n *\n * const result = await builder.compile({\n * inputs: { usdc: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * ```\n */\nexport interface ComposeSdk {\n /** Low-level HTTP client for the Compose API. */\n readonly client: ComposeClient;\n /**\n * Creates a new flow builder targeting the given chain.\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilder} with generated operation methods and a `compile` method.\n * @throws Error if any resource input's `chainId` doesn't match the flow's `chainId`.\n */\n readonly flow: <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n ) => FlowBuilder<T>;\n /**\n * Fetches the chains this Compose deployment supports, ascending by\n * `chainId`. Thin pass-through to {@link ComposeClient.getChains}; see it\n * for what \"supported\" means and for the caching caveat.\n *\n * Each entry pairs the `chainId` with a display `name`, so one call both\n * gates a chain selector and labels it. Key on `chainId`; `name` is a\n * deployment-maintained label that can change.\n *\n * Use it to fail fast before building a flow for a chain the server would\n * reject.\n *\n * @returns The supported chains, ascending by `chainId`.\n * @throws {@link ComposeError} on network or server errors.\n *\n * @example\n * ```ts\n * const chains = await sdk.getSupportedChains();\n * if (!chains.some((c) => c.chainId === 8453)) {\n * throw new Error('Base is not supported');\n * }\n * for (const { chainId, name } of chains) {\n * console.log(`${name} (${chainId})`);\n * }\n * ```\n */\n readonly getSupportedChains: () => Promise<readonly ChainSummary[]>;\n /**\n * Builds a raw compile request without sending it. Useful for inspecting the\n * request payload or submitting it through a custom transport.\n * @param flow - A built flow document (from `builder.build()`).\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns A {@link ComposeCompileRequest} ready to send to the Compose API.\n */\n readonly request: <T extends InputSchema>(\n flow: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ) => ComposeCompileRequest;\n /**\n * Compiles a token pair on one chain into a submit-ready transaction. Thin\n * pass-through to {@link ComposeClient.route}; see it for the short chain of\n * catalog edges the server plans, when to prefer `flow(...).compile(...)`,\n * the `success` / `partial` discrimination, and error behaviour. Build the\n * `amount` with `routeAmount.exact(...)` / `routeAmount.all(...)`.\n *\n * @param request - The from/to token pair, amount, signer, and route options.\n * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly route: (\n request: ComposeRouteRequest,\n ) => Promise<ComposeCompileResult>;\n /**\n * Simulates a raw, pre-encoded transaction. Thin pass-through to\n * {@link ComposeClient.simulate}; see it for the `ok` / `revert` / `error`\n * result discrimination and error behaviour. Pair with `buildSimulateRequest`\n * to assemble the request from a compile result.\n *\n * @param request - The raw transaction plus funding `requirements` and\n * `trackedBalances`.\n * @returns A {@link SimulateResult} (`ok` / `revert` / `error`).\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly simulate: (request: SimulateRequest) => Promise<SimulateResult>;\n}\n\n/**\n * Creates a new Compose SDK instance.\n *\n * @param options - SDK configuration including the API base URL, required API key, and optional fetch implementation.\n * @returns A {@link ComposeSdk} instance.\n *\n * @example\n * ```ts\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n * ```\n */\nexport const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => {\n const composeClient = createComposeClient(options);\n\n const request = <T extends InputSchema>(\n flowDoc: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ): ComposeCompileRequest => ({\n flow: flowDoc,\n run: buildRun({\n inputs: run.inputs as Record<string, InputSpec>,\n preconditions: run.preconditions,\n signer: run.signer,\n assumptions: run.assumptions as Record<string, bigint> | undefined,\n referrer: run.referrer,\n integratorFeeBps: run.integratorFeeBps,\n maxPriceImpactBps: run.maxPriceImpactBps,\n sweepTo: run.sweepTo,\n simulationPolicy: run.simulationPolicy,\n simulateUserProgram: run.simulateUserProgram,\n checkOnChainAllowances: run.checkOnChainAllowances,\n }),\n });\n\n const flow = <T extends InputSchema>(\n chainId: number,\n opts: FlowOptions<T>,\n ): FlowBuilder<T> => {\n const core = createFlowBuilderCore(chainId, opts);\n const builder = Object.assign(core, bindGeneratedOps(core));\n\n const compile = async (\n run: ComposeRunInput<T>,\n ): Promise<ComposeCompileResult> => {\n const flowDoc = builder.build();\n const req = request(flowDoc, run);\n return composeClient.compile(req);\n };\n\n return Object.assign(builder, { compile });\n };\n\n return {\n client: composeClient,\n flow,\n getSupportedChains: composeClient.getChains,\n request,\n route: composeClient.route,\n simulate: composeClient.simulate,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,6BAKO;AACP,oBAAwD;AAExD,kCAGO;AACP,oBAA+C;AAwKxC,MAAM,mBAAmB,CAAC,YAA2C;AAC1E,QAAM,oBAAgB,mCAAoB,OAAO;AAEjD,QAAM,UAAU,CACd,SACA,SAC2B;AAAA,IAC3B,MAAM;AAAA,IACN,SAAK,wBAAS;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,eAAe,IAAI;AAAA,MACnB,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,kBAAkB,IAAI;AAAA,MACtB,mBAAmB,IAAI;AAAA,MACvB,SAAS,IAAI;AAAA,MACb,kBAAkB,IAAI;AAAA,MACtB,qBAAqB,IAAI;AAAA,MACzB,wBAAwB,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,CACX,SACA,SACmB;AACnB,UAAM,WAAO,8CAAsB,SAAS,IAAI;AAChD,UAAM,UAAU,OAAO,OAAO,UAAM,8CAAiB,IAAI,CAAC;AAE1D,UAAM,UAAU,OACd,QACkC;AAClC,YAAM,UAAU,QAAQ,MAAM;AAC9B,YAAM,MAAM,QAAQ,SAAS,GAAG;AAChC,aAAO,cAAc,QAAQ,GAAG;AAAA,IAClC;AAEA,WAAO,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,oBAAoB,cAAc;AAAA,IAClC;AAAA,IACA,OAAO,cAAc;AAAA,IACrB,UAAU,cAAc;AAAA,EAC1B;AACF;","names":[]}
package/dist/sdk.d.cts CHANGED
@@ -123,12 +123,11 @@ interface ComposeSdk {
123
123
  */
124
124
  readonly request: <T extends InputSchema>(flow: TypedFlow<T>, run: ComposeRunInput<T>) => ComposeCompileRequest;
125
125
  /**
126
- * Compiles a token pair on one chain — as of today a single zap step over
127
- * one routing-catalog edge. Thin pass-through to
128
- * {@link ComposeClient.route}; see it for the current single-step limit,
129
- * when to prefer `flow(...).compile(...)`, the `success` / `partial`
130
- * discrimination, and error behaviour. Build the `amount` with
131
- * `routeAmount.exact(...)` / `routeAmount.all(...)`.
126
+ * Compiles a token pair on one chain into a submit-ready transaction. Thin
127
+ * pass-through to {@link ComposeClient.route}; see it for the short chain of
128
+ * catalog edges the server plans, when to prefer `flow(...).compile(...)`,
129
+ * the `success` / `partial` discrimination, and error behaviour. Build the
130
+ * `amount` with `routeAmount.exact(...)` / `routeAmount.all(...)`.
132
131
  *
133
132
  * @param request - The from/to token pair, amount, signer, and route options.
134
133
  * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.
package/dist/sdk.d.ts CHANGED
@@ -123,12 +123,11 @@ interface ComposeSdk {
123
123
  */
124
124
  readonly request: <T extends InputSchema>(flow: TypedFlow<T>, run: ComposeRunInput<T>) => ComposeCompileRequest;
125
125
  /**
126
- * Compiles a token pair on one chain — as of today a single zap step over
127
- * one routing-catalog edge. Thin pass-through to
128
- * {@link ComposeClient.route}; see it for the current single-step limit,
129
- * when to prefer `flow(...).compile(...)`, the `success` / `partial`
130
- * discrimination, and error behaviour. Build the `amount` with
131
- * `routeAmount.exact(...)` / `routeAmount.all(...)`.
126
+ * Compiles a token pair on one chain into a submit-ready transaction. Thin
127
+ * pass-through to {@link ComposeClient.route}; see it for the short chain of
128
+ * catalog edges the server plans, when to prefer `flow(...).compile(...)`,
129
+ * the `success` / `partial` discrimination, and error behaviour. Build the
130
+ * `amount` with `routeAmount.exact(...)` / `routeAmount.all(...)`.
132
131
  *
133
132
  * @param request - The from/to token pair, amount, signer, and route options.
134
133
  * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.
package/dist/sdk.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sdk.ts"],"sourcesContent":["import type {\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeRouteRequest,\n InputSpec,\n SimulateRequest,\n SimulateResult,\n} from '@lifi/compose-spec';\n\nimport {\n createFlowBuilderCore,\n type FlowBuilderCore,\n type FlowOptions,\n type TypedFlow,\n} from './authoring/FlowBuilderCore.js';\nimport { type ComposeClient, createComposeClient } from './client.js';\nimport type { ChainSummary } from './discovery.js';\nimport {\n bindGeneratedOps,\n type GeneratedOps,\n} from './generated/operations.generated.js';\nimport { buildRun, type ComposeRunInput } from './run/inputs.js';\nimport type { InputSchema } from './types.js';\n\nexport type { ComposeRunInput };\n\n/**\n * A flow builder augmented with generated operation methods and a `compile` method\n * that submits the flow to the Compose backend for compilation.\n *\n * Created via {@link ComposeSdk.flow}.\n *\n * @typeParam T - The input schema describing the flow's required inputs.\n */\nexport type FlowBuilder<T extends InputSchema = InputSchema> =\n FlowBuilderCore<T> &\n GeneratedOps & {\n /**\n * Builds the flow document from the current builder state and submits it\n * to the Compose API for compilation.\n *\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns The compiled transaction calldata and metadata.\n * @throws {@link ComposeError} on network, validation, or server errors.\n *\n * @example\n * ```ts\n * const result = await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * console.log(result.calldata);\n * ```\n */\n readonly compile: (\n run: ComposeRunInput<T>,\n ) => Promise<ComposeCompileResult>;\n };\n\n/**\n * Configuration for creating a Compose SDK instance.\n */\nexport interface ComposeSdkOptions {\n /** Base URL of the Compose API (e.g. `\"https://li.quest\"`). */\n readonly baseUrl: string;\n /** Optional custom `fetch` implementation. Defaults to `globalThis.fetch`. */\n readonly fetch?: typeof globalThis.fetch;\n /** LI.FI API key, sent as the `x-lifi-api-key` header on every request. Required — the Compose API rejects unauthenticated requests. */\n readonly apiKey: string;\n}\n\n/**\n * The top-level Compose SDK interface.\n *\n * Provides methods to build flows, compile them into executable calldata, and\n * interact with the Compose API directly.\n *\n * @example\n * ```ts\n * import { createComposeSdk, resources, materialisers } from '@lifi/composer-sdk';\n *\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n *\n * const builder = sdk.flow(1, {\n * inputs: { usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1) },\n * });\n *\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.usdc },\n * config: { resourceOut: resources.native(1) },\n * });\n *\n * const result = await builder.compile({\n * inputs: { usdc: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * ```\n */\nexport interface ComposeSdk {\n /** Low-level HTTP client for the Compose API. */\n readonly client: ComposeClient;\n /**\n * Creates a new flow builder targeting the given chain.\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilder} with generated operation methods and a `compile` method.\n * @throws Error if any resource input's `chainId` doesn't match the flow's `chainId`.\n */\n readonly flow: <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n ) => FlowBuilder<T>;\n /**\n * Fetches the chains this Compose deployment supports, ascending by\n * `chainId`. Thin pass-through to {@link ComposeClient.getChains}; see it\n * for what \"supported\" means and for the caching caveat.\n *\n * Each entry pairs the `chainId` with a display `name`, so one call both\n * gates a chain selector and labels it. Key on `chainId`; `name` is a\n * deployment-maintained label that can change.\n *\n * Use it to fail fast before building a flow for a chain the server would\n * reject.\n *\n * @returns The supported chains, ascending by `chainId`.\n * @throws {@link ComposeError} on network or server errors.\n *\n * @example\n * ```ts\n * const chains = await sdk.getSupportedChains();\n * if (!chains.some((c) => c.chainId === 8453)) {\n * throw new Error('Base is not supported');\n * }\n * for (const { chainId, name } of chains) {\n * console.log(`${name} (${chainId})`);\n * }\n * ```\n */\n readonly getSupportedChains: () => Promise<readonly ChainSummary[]>;\n /**\n * Builds a raw compile request without sending it. Useful for inspecting the\n * request payload or submitting it through a custom transport.\n * @param flow - A built flow document (from `builder.build()`).\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns A {@link ComposeCompileRequest} ready to send to the Compose API.\n */\n readonly request: <T extends InputSchema>(\n flow: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ) => ComposeCompileRequest;\n /**\n * Compiles a token pair on one chain — as of today a single zap step over\n * one routing-catalog edge. Thin pass-through to\n * {@link ComposeClient.route}; see it for the current single-step limit,\n * when to prefer `flow(...).compile(...)`, the `success` / `partial`\n * discrimination, and error behaviour. Build the `amount` with\n * `routeAmount.exact(...)` / `routeAmount.all(...)`.\n *\n * @param request - The from/to token pair, amount, signer, and route options.\n * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly route: (\n request: ComposeRouteRequest,\n ) => Promise<ComposeCompileResult>;\n /**\n * Simulates a raw, pre-encoded transaction. Thin pass-through to\n * {@link ComposeClient.simulate}; see it for the `ok` / `revert` / `error`\n * result discrimination and error behaviour. Pair with `buildSimulateRequest`\n * to assemble the request from a compile result.\n *\n * @param request - The raw transaction plus funding `requirements` and\n * `trackedBalances`.\n * @returns A {@link SimulateResult} (`ok` / `revert` / `error`).\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly simulate: (request: SimulateRequest) => Promise<SimulateResult>;\n}\n\n/**\n * Creates a new Compose SDK instance.\n *\n * @param options - SDK configuration including the API base URL, required API key, and optional fetch implementation.\n * @returns A {@link ComposeSdk} instance.\n *\n * @example\n * ```ts\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n * ```\n */\nexport const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => {\n const composeClient = createComposeClient(options);\n\n const request = <T extends InputSchema>(\n flowDoc: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ): ComposeCompileRequest => ({\n flow: flowDoc,\n run: buildRun({\n inputs: run.inputs as Record<string, InputSpec>,\n preconditions: run.preconditions,\n signer: run.signer,\n assumptions: run.assumptions as Record<string, bigint> | undefined,\n referrer: run.referrer,\n integratorFeeBps: run.integratorFeeBps,\n maxPriceImpactBps: run.maxPriceImpactBps,\n sweepTo: run.sweepTo,\n simulationPolicy: run.simulationPolicy,\n simulateUserProgram: run.simulateUserProgram,\n checkOnChainAllowances: run.checkOnChainAllowances,\n }),\n });\n\n const flow = <T extends InputSchema>(\n chainId: number,\n opts: FlowOptions<T>,\n ): FlowBuilder<T> => {\n const core = createFlowBuilderCore(chainId, opts);\n const builder = Object.assign(core, bindGeneratedOps(core));\n\n const compile = async (\n run: ComposeRunInput<T>,\n ): Promise<ComposeCompileResult> => {\n const flowDoc = builder.build();\n const req = request(flowDoc, run);\n return composeClient.compile(req);\n };\n\n return Object.assign(builder, { compile });\n };\n\n return {\n client: composeClient,\n flow,\n getSupportedChains: composeClient.getChains,\n request,\n route: composeClient.route,\n simulate: composeClient.simulate,\n };\n};\n"],"mappings":"AASA;AAAA,EACE;AAAA,OAIK;AACP,SAA6B,2BAA2B;AAExD;AAAA,EACE;AAAA,OAEK;AACP,SAAS,gBAAsC;AAyKxC,MAAM,mBAAmB,CAAC,YAA2C;AAC1E,QAAM,gBAAgB,oBAAoB,OAAO;AAEjD,QAAM,UAAU,CACd,SACA,SAC2B;AAAA,IAC3B,MAAM;AAAA,IACN,KAAK,SAAS;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,eAAe,IAAI;AAAA,MACnB,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,kBAAkB,IAAI;AAAA,MACtB,mBAAmB,IAAI;AAAA,MACvB,SAAS,IAAI;AAAA,MACb,kBAAkB,IAAI;AAAA,MACtB,qBAAqB,IAAI;AAAA,MACzB,wBAAwB,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,CACX,SACA,SACmB;AACnB,UAAM,OAAO,sBAAsB,SAAS,IAAI;AAChD,UAAM,UAAU,OAAO,OAAO,MAAM,iBAAiB,IAAI,CAAC;AAE1D,UAAM,UAAU,OACd,QACkC;AAClC,YAAM,UAAU,QAAQ,MAAM;AAC9B,YAAM,MAAM,QAAQ,SAAS,GAAG;AAChC,aAAO,cAAc,QAAQ,GAAG;AAAA,IAClC;AAEA,WAAO,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,oBAAoB,cAAc;AAAA,IAClC;AAAA,IACA,OAAO,cAAc;AAAA,IACrB,UAAU,cAAc;AAAA,EAC1B;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/sdk.ts"],"sourcesContent":["import type {\n ComposeCompileRequest,\n ComposeCompileResult,\n ComposeRouteRequest,\n InputSpec,\n SimulateRequest,\n SimulateResult,\n} from '@lifi/compose-spec';\n\nimport {\n createFlowBuilderCore,\n type FlowBuilderCore,\n type FlowOptions,\n type TypedFlow,\n} from './authoring/FlowBuilderCore.js';\nimport { type ComposeClient, createComposeClient } from './client.js';\nimport type { ChainSummary } from './discovery.js';\nimport {\n bindGeneratedOps,\n type GeneratedOps,\n} from './generated/operations.generated.js';\nimport { buildRun, type ComposeRunInput } from './run/inputs.js';\nimport type { InputSchema } from './types.js';\n\nexport type { ComposeRunInput };\n\n/**\n * A flow builder augmented with generated operation methods and a `compile` method\n * that submits the flow to the Compose backend for compilation.\n *\n * Created via {@link ComposeSdk.flow}.\n *\n * @typeParam T - The input schema describing the flow's required inputs.\n */\nexport type FlowBuilder<T extends InputSchema = InputSchema> =\n FlowBuilderCore<T> &\n GeneratedOps & {\n /**\n * Builds the flow document from the current builder state and submits it\n * to the Compose API for compilation.\n *\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns The compiled transaction calldata and metadata.\n * @throws {@link ComposeError} on network, validation, or server errors.\n *\n * @example\n * ```ts\n * const result = await builder.compile({\n * inputs: { token: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * console.log(result.calldata);\n * ```\n */\n readonly compile: (\n run: ComposeRunInput<T>,\n ) => Promise<ComposeCompileResult>;\n };\n\n/**\n * Configuration for creating a Compose SDK instance.\n */\nexport interface ComposeSdkOptions {\n /** Base URL of the Compose API (e.g. `\"https://li.quest\"`). */\n readonly baseUrl: string;\n /** Optional custom `fetch` implementation. Defaults to `globalThis.fetch`. */\n readonly fetch?: typeof globalThis.fetch;\n /** LI.FI API key, sent as the `x-lifi-api-key` header on every request. Required — the Compose API rejects unauthenticated requests. */\n readonly apiKey: string;\n}\n\n/**\n * The top-level Compose SDK interface.\n *\n * Provides methods to build flows, compile them into executable calldata, and\n * interact with the Compose API directly.\n *\n * @example\n * ```ts\n * import { createComposeSdk, resources, materialisers } from '@lifi/composer-sdk';\n *\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n *\n * const builder = sdk.flow(1, {\n * inputs: { usdc: resources.erc20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 1) },\n * });\n *\n * builder.lifi.swap('swap1', {\n * bind: { amountIn: builder.inputs.usdc },\n * config: { resourceOut: resources.native(1) },\n * });\n *\n * const result = await builder.compile({\n * inputs: { usdc: materialisers.balanceOf({}) },\n * signer: '0xYourAddress...',\n * });\n * ```\n */\nexport interface ComposeSdk {\n /** Low-level HTTP client for the Compose API. */\n readonly client: ComposeClient;\n /**\n * Creates a new flow builder targeting the given chain.\n * @param chainId - The EVM chain ID (e.g. `1` for Ethereum mainnet).\n * @param options - Flow configuration including input declarations.\n * @returns A {@link FlowBuilder} with generated operation methods and a `compile` method.\n * @throws Error if any resource input's `chainId` doesn't match the flow's `chainId`.\n */\n readonly flow: <T extends InputSchema>(\n chainId: number,\n options: FlowOptions<T>,\n ) => FlowBuilder<T>;\n /**\n * Fetches the chains this Compose deployment supports, ascending by\n * `chainId`. Thin pass-through to {@link ComposeClient.getChains}; see it\n * for what \"supported\" means and for the caching caveat.\n *\n * Each entry pairs the `chainId` with a display `name`, so one call both\n * gates a chain selector and labels it. Key on `chainId`; `name` is a\n * deployment-maintained label that can change.\n *\n * Use it to fail fast before building a flow for a chain the server would\n * reject.\n *\n * @returns The supported chains, ascending by `chainId`.\n * @throws {@link ComposeError} on network or server errors.\n *\n * @example\n * ```ts\n * const chains = await sdk.getSupportedChains();\n * if (!chains.some((c) => c.chainId === 8453)) {\n * throw new Error('Base is not supported');\n * }\n * for (const { chainId, name } of chains) {\n * console.log(`${name} (${chainId})`);\n * }\n * ```\n */\n readonly getSupportedChains: () => Promise<readonly ChainSummary[]>;\n /**\n * Builds a raw compile request without sending it. Useful for inspecting the\n * request payload or submitting it through a custom transport.\n * @param flow - A built flow document (from `builder.build()`).\n * @param run - Runtime inputs, preconditions, and signer address.\n * @returns A {@link ComposeCompileRequest} ready to send to the Compose API.\n */\n readonly request: <T extends InputSchema>(\n flow: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ) => ComposeCompileRequest;\n /**\n * Compiles a token pair on one chain into a submit-ready transaction. Thin\n * pass-through to {@link ComposeClient.route}; see it for the short chain of\n * catalog edges the server plans, when to prefer `flow(...).compile(...)`,\n * the `success` / `partial` discrimination, and error behaviour. Build the\n * `amount` with `routeAmount.exact(...)` / `routeAmount.all(...)`.\n *\n * @param request - The from/to token pair, amount, signer, and route options.\n * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly route: (\n request: ComposeRouteRequest,\n ) => Promise<ComposeCompileResult>;\n /**\n * Simulates a raw, pre-encoded transaction. Thin pass-through to\n * {@link ComposeClient.simulate}; see it for the `ok` / `revert` / `error`\n * result discrimination and error behaviour. Pair with `buildSimulateRequest`\n * to assemble the request from a compile result.\n *\n * @param request - The raw transaction plus funding `requirements` and\n * `trackedBalances`.\n * @returns A {@link SimulateResult} (`ok` / `revert` / `error`).\n * @throws {@link ComposeError} on network, validation, or server errors.\n */\n readonly simulate: (request: SimulateRequest) => Promise<SimulateResult>;\n}\n\n/**\n * Creates a new Compose SDK instance.\n *\n * @param options - SDK configuration including the API base URL, required API key, and optional fetch implementation.\n * @returns A {@link ComposeSdk} instance.\n *\n * @example\n * ```ts\n * const sdk = createComposeSdk({ baseUrl: 'https://li.quest', apiKey: 'YOUR_API_KEY' });\n * ```\n */\nexport const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => {\n const composeClient = createComposeClient(options);\n\n const request = <T extends InputSchema>(\n flowDoc: TypedFlow<T>,\n run: ComposeRunInput<T>,\n ): ComposeCompileRequest => ({\n flow: flowDoc,\n run: buildRun({\n inputs: run.inputs as Record<string, InputSpec>,\n preconditions: run.preconditions,\n signer: run.signer,\n assumptions: run.assumptions as Record<string, bigint> | undefined,\n referrer: run.referrer,\n integratorFeeBps: run.integratorFeeBps,\n maxPriceImpactBps: run.maxPriceImpactBps,\n sweepTo: run.sweepTo,\n simulationPolicy: run.simulationPolicy,\n simulateUserProgram: run.simulateUserProgram,\n checkOnChainAllowances: run.checkOnChainAllowances,\n }),\n });\n\n const flow = <T extends InputSchema>(\n chainId: number,\n opts: FlowOptions<T>,\n ): FlowBuilder<T> => {\n const core = createFlowBuilderCore(chainId, opts);\n const builder = Object.assign(core, bindGeneratedOps(core));\n\n const compile = async (\n run: ComposeRunInput<T>,\n ): Promise<ComposeCompileResult> => {\n const flowDoc = builder.build();\n const req = request(flowDoc, run);\n return composeClient.compile(req);\n };\n\n return Object.assign(builder, { compile });\n };\n\n return {\n client: composeClient,\n flow,\n getSupportedChains: composeClient.getChains,\n request,\n route: composeClient.route,\n simulate: composeClient.simulate,\n };\n};\n"],"mappings":"AASA;AAAA,EACE;AAAA,OAIK;AACP,SAA6B,2BAA2B;AAExD;AAAA,EACE;AAAA,OAEK;AACP,SAAS,gBAAsC;AAwKxC,MAAM,mBAAmB,CAAC,YAA2C;AAC1E,QAAM,gBAAgB,oBAAoB,OAAO;AAEjD,QAAM,UAAU,CACd,SACA,SAC2B;AAAA,IAC3B,MAAM;AAAA,IACN,KAAK,SAAS;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,eAAe,IAAI;AAAA,MACnB,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,kBAAkB,IAAI;AAAA,MACtB,mBAAmB,IAAI;AAAA,MACvB,SAAS,IAAI;AAAA,MACb,kBAAkB,IAAI;AAAA,MACtB,qBAAqB,IAAI;AAAA,MACzB,wBAAwB,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,CACX,SACA,SACmB;AACnB,UAAM,OAAO,sBAAsB,SAAS,IAAI;AAChD,UAAM,UAAU,OAAO,OAAO,MAAM,iBAAiB,IAAI,CAAC;AAE1D,UAAM,UAAU,OACd,QACkC;AAClC,YAAM,UAAU,QAAQ,MAAM;AAC9B,YAAM,MAAM,QAAQ,SAAS,GAAG;AAChC,aAAO,cAAc,QAAQ,GAAG;AAAA,IAClC;AAEA,WAAO,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,oBAAoB,cAAc;AAAA,IAClC;AAAA,IACA,OAAO,cAAc;AAAA,IACrB,UAAU,cAAc;AAAA,EAC1B;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/composer-sdk",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Public Composer SDK for building and submitting flows",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -47,7 +47,7 @@
47
47
  "example:staged": "tsx src/examples/run.ts --staged"
48
48
  },
49
49
  "peerDependencies": {
50
- "@lifi/compose-spec": "0.6.2"
50
+ "@lifi/compose-spec": "0.6.3"
51
51
  },
52
52
  "dependencies": {
53
53
  "abitype": "^1.2.3",
@@ -57,6 +57,7 @@
57
57
  "devDependencies": {
58
58
  "@eslint/compat": "^1.4.0",
59
59
  "@lifi/compose-spec": "workspace:^",
60
+ "@lifi/vitest-config": "workspace:^",
60
61
  "@types/node": "^25.6.0",
61
62
  "@typescript-eslint/eslint-plugin": "^8.68.0",
62
63
  "@typescript-eslint/parser": "^8.68.0",
package/src/sdk.ts CHANGED
@@ -149,12 +149,11 @@ export interface ComposeSdk {
149
149
  run: ComposeRunInput<T>,
150
150
  ) => ComposeCompileRequest;
151
151
  /**
152
- * Compiles a token pair on one chain — as of today a single zap step over
153
- * one routing-catalog edge. Thin pass-through to
154
- * {@link ComposeClient.route}; see it for the current single-step limit,
155
- * when to prefer `flow(...).compile(...)`, the `success` / `partial`
156
- * discrimination, and error behaviour. Build the `amount` with
157
- * `routeAmount.exact(...)` / `routeAmount.all(...)`.
152
+ * Compiles a token pair on one chain into a submit-ready transaction. Thin
153
+ * pass-through to {@link ComposeClient.route}; see it for the short chain of
154
+ * catalog edges the server plans, when to prefer `flow(...).compile(...)`,
155
+ * the `success` / `partial` discrimination, and error behaviour. Build the
156
+ * `amount` with `routeAmount.exact(...)` / `routeAmount.all(...)`.
158
157
  *
159
158
  * @param request - The from/to token pair, amount, signer, and route options.
160
159
  * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.