@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 +202 -189
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/sdk.cjs.map +1 -1
- package/dist/sdk.d.cts +5 -6
- package/dist/sdk.d.ts +5 -6
- package/dist/sdk.js.map +1 -1
- package/package.json +3 -2
- package/src/sdk.ts +5 -6
package/README.md
CHANGED
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
# @lifi/composer-sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for building
|
|
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
|
|
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
|
-
|
|
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
|
|
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!,
|
|
36
|
+
apiKey: process.env.LIFI_API_KEY!,
|
|
50
37
|
});
|
|
51
38
|
|
|
52
|
-
//
|
|
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:
|
|
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:
|
|
70
|
-
//
|
|
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.
|
|
91
|
-
// throws a ComposeError
|
|
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
|
-
//
|
|
84
|
+
// transactionRequest includes gasLimit.
|
|
100
85
|
console.log(result.transactionRequest);
|
|
101
86
|
|
|
102
|
-
//
|
|
103
|
-
// terminal
|
|
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
|
-
//
|
|
111
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
110
|
+
**Runtime inputs (materialisers)**: a materialiser tells the backend how to fund a flow input when the transaction executes.
|
|
125
111
|
|
|
126
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
|
|
121
|
+
**Outputs**: a compile result carries `outputs`, keyed `'<nodeId>.<portName>'`, with one entry per resource output. An entry is either:
|
|
133
122
|
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
|
|
128
|
+
## API surface
|
|
139
129
|
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
- `
|
|
151
|
-
- `
|
|
152
|
-
- `
|
|
153
|
-
- `
|
|
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)
|
|
158
|
-
- `guards
|
|
159
|
-
- `materialisers
|
|
160
|
-
- `preconditions
|
|
161
|
-
- `raw.ref<T>(path)`
|
|
162
|
-
- `
|
|
163
|
-
- `
|
|
164
|
-
- `
|
|
165
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
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
|
-
|
|
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'
|
|
222
|
-
- **`availability: 'future'
|
|
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
|
-
|
|
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
|
-
|
|
241
|
+
### Amounts
|
|
227
242
|
|
|
228
|
-
- **`routeAmount.exact(amount)
|
|
229
|
-
- **`routeAmount.all(simAmount)
|
|
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.
|
|
248
|
+
Both accept a `bigint` or a decimal string.
|
|
232
249
|
|
|
233
250
|
### Options
|
|
234
251
|
|
|
235
|
-
|
|
252
|
+
The SDK sends only the fields you set. The server applies the defaults.
|
|
236
253
|
|
|
237
|
-
- **`slippageBps
|
|
238
|
-
- **`maxPriceImpactBps
|
|
239
|
-
- **`integratorFeeBps
|
|
240
|
-
- **`referrer
|
|
241
|
-
- **`sweepTo
|
|
242
|
-
- **`checkOnChainAllowances
|
|
243
|
-
- **`simulationPolicy
|
|
244
|
-
- **`simulateUserProgram
|
|
245
|
-
- **`allowAsyncRoutes
|
|
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
|
-
|
|
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
|
|
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'
|
|
256
|
-
- **`VALIDATION_ERROR
|
|
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
|
-
|
|
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
|
-
|
|
271
|
-
const tx = result.transactionRequest;
|
|
272
|
-
console.log(tx.gasLimit); // string
|
|
296
|
+
console.log(result.transactionRequest?.gasLimit); // string
|
|
273
297
|
} else {
|
|
274
|
-
//
|
|
275
|
-
//
|
|
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);
|
|
301
|
+
console.log(result.error.message);
|
|
278
302
|
|
|
279
|
-
// Revert diagnostics
|
|
280
303
|
const revert = result.simulationRevert;
|
|
281
|
-
console.log(revert.code);
|
|
282
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
//
|
|
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`
|
|
367
|
-
|
|
368
|
-
|
|
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(...)`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
424
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
- **lifiSwap
|
|
437
|
-
- **lifiZap
|
|
438
|
-
- **lifiZapAsync
|
|
439
|
-
- **swapAndZap
|
|
440
|
-
- **splitAndZap
|
|
441
|
-
- **splitWithArithmetic
|
|
442
|
-
- **dustSweep
|
|
443
|
-
- **depositFromProxy
|
|
444
|
-
- **approveAndDeposit
|
|
445
|
-
- **consolidateToUsdc**
|
|
446
|
-
- **
|
|
447
|
-
- **
|
|
448
|
-
- **
|
|
449
|
-
- **
|
|
450
|
-
- **rawCallWithArithmetic
|
|
451
|
-
- **readContractState
|
|
452
|
-
- **
|
|
453
|
-
- **
|
|
454
|
-
- **
|
|
455
|
-
- **
|
|
456
|
-
- **
|
|
457
|
-
- **
|
|
458
|
-
- **
|
|
459
|
-
- **
|
|
460
|
-
- **
|
|
461
|
-
- **
|
|
462
|
-
- **
|
|
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
|
|
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
|
|
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>',
|
|
496
|
+
baseUrl: '<staging-backend-url>',
|
|
479
497
|
apiKey: process.env.LIFI_API_KEY!,
|
|
480
498
|
});
|
|
481
499
|
```
|
|
482
500
|
|
|
483
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
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
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
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'`.
|