@lifi/composer-sdk 0.4.0 → 0.5.0

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.
Files changed (106) hide show
  1. package/README.md +98 -5
  2. package/dist/authoring/FlowBuilderCore.cjs +1 -5
  3. package/dist/authoring/FlowBuilderCore.cjs.map +1 -1
  4. package/dist/authoring/FlowBuilderCore.d.cts +9 -7
  5. package/dist/authoring/FlowBuilderCore.d.ts +9 -7
  6. package/dist/authoring/FlowBuilderCore.js +2 -6
  7. package/dist/authoring/FlowBuilderCore.js.map +1 -1
  8. package/dist/authoring/handles.cjs +5 -2
  9. package/dist/authoring/handles.cjs.map +1 -1
  10. package/dist/authoring/handles.d.cts +25 -6
  11. package/dist/authoring/handles.d.ts +25 -6
  12. package/dist/authoring/handles.js +3 -1
  13. package/dist/authoring/handles.js.map +1 -1
  14. package/dist/authoring/raw.cjs +6 -1
  15. package/dist/authoring/raw.cjs.map +1 -1
  16. package/dist/authoring/raw.d.cts +33 -3
  17. package/dist/authoring/raw.d.ts +33 -3
  18. package/dist/authoring/raw.js +5 -1
  19. package/dist/authoring/raw.js.map +1 -1
  20. package/dist/authoring/signatureArgs.cjs.map +1 -1
  21. package/dist/authoring/signatureArgs.d.cts +16 -12
  22. package/dist/authoring/signatureArgs.d.ts +16 -12
  23. package/dist/authoring/signatureArgs.js.map +1 -1
  24. package/dist/client.cjs +94 -58
  25. package/dist/client.cjs.map +1 -1
  26. package/dist/client.d.cts +61 -2
  27. package/dist/client.d.ts +61 -2
  28. package/dist/client.js +94 -58
  29. package/dist/client.js.map +1 -1
  30. package/dist/discovery.cjs.map +1 -1
  31. package/dist/discovery.d.cts +31 -2
  32. package/dist/discovery.d.ts +31 -2
  33. package/dist/generated/config.generated.cjs.map +1 -1
  34. package/dist/generated/config.generated.d.cts +35 -3
  35. package/dist/generated/config.generated.d.ts +35 -3
  36. package/dist/generated/operations.generated.cjs +9 -0
  37. package/dist/generated/operations.generated.cjs.map +1 -1
  38. package/dist/generated/operations.generated.d.cts +33 -2
  39. package/dist/generated/operations.generated.d.ts +33 -2
  40. package/dist/generated/operations.generated.js +9 -0
  41. package/dist/generated/operations.generated.js.map +1 -1
  42. package/dist/index.cjs +5 -1
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +26 -7
  45. package/dist/index.d.ts +26 -7
  46. package/dist/index.js +4 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/{raw-IxCF_bSS.d.ts → raw-BvRghN3r.d.ts} +3 -2
  49. package/dist/{raw-Dj_2rBSQ.d.cts → raw-s1rer8X3.d.cts} +3 -2
  50. package/dist/raw.cjs +2 -0
  51. package/dist/raw.cjs.map +1 -1
  52. package/dist/raw.d.cts +1 -1
  53. package/dist/raw.d.ts +1 -1
  54. package/dist/raw.js +2 -1
  55. package/dist/raw.js.map +1 -1
  56. package/dist/routeAmount-DdbJYJyM.d.cts +55 -0
  57. package/dist/routeAmount-DdbJYJyM.d.ts +55 -0
  58. package/dist/routeAmount.cjs +38 -0
  59. package/dist/routeAmount.cjs.map +1 -0
  60. package/dist/routeAmount.d.cts +2 -0
  61. package/dist/routeAmount.d.ts +2 -0
  62. package/dist/routeAmount.js +13 -0
  63. package/dist/routeAmount.js.map +1 -0
  64. package/dist/run/inputs.cjs +14 -15
  65. package/dist/run/inputs.cjs.map +1 -1
  66. package/dist/run/inputs.d.cts +8 -0
  67. package/dist/run/inputs.d.ts +8 -0
  68. package/dist/run/inputs.js +14 -15
  69. package/dist/run/inputs.js.map +1 -1
  70. package/dist/run/simulate.cjs +5 -0
  71. package/dist/run/simulate.cjs.map +1 -1
  72. package/dist/run/simulate.d.cts +5 -2
  73. package/dist/run/simulate.d.ts +5 -2
  74. package/dist/run/simulate.js +5 -0
  75. package/dist/run/simulate.js.map +1 -1
  76. package/dist/sdk.cjs +3 -0
  77. package/dist/sdk.cjs.map +1 -1
  78. package/dist/sdk.d.cts +42 -2
  79. package/dist/sdk.d.ts +42 -2
  80. package/dist/sdk.js +3 -0
  81. package/dist/sdk.js.map +1 -1
  82. package/dist/types.cjs.map +1 -1
  83. package/dist/types.d.cts +18 -2
  84. package/dist/types.d.ts +18 -2
  85. package/package.json +6 -13
  86. package/src/authoring/FlowBuilderCore.ts +13 -15
  87. package/src/authoring/handles.ts +41 -5
  88. package/src/authoring/raw.ts +39 -4
  89. package/src/authoring/signatureArgs.ts +17 -13
  90. package/src/client.ts +204 -58
  91. package/src/discovery.ts +31 -1
  92. package/src/examples/flySwap.ts +167 -0
  93. package/src/examples/lifiZap.ts +4 -0
  94. package/src/examples/lifiZapAsync.ts +88 -0
  95. package/src/examples/routeTokenPair.ts +58 -0
  96. package/src/examples/run.ts +56 -6
  97. package/src/examples/supportedChains.ts +122 -0
  98. package/src/generated/config.generated.ts +45 -3
  99. package/src/generated/operations.generated.ts +56 -8
  100. package/src/index.ts +33 -1
  101. package/src/raw.ts +1 -1
  102. package/src/routeAmount.ts +61 -0
  103. package/src/run/inputs.ts +30 -11
  104. package/src/run/simulate.ts +14 -2
  105. package/src/sdk.ts +47 -0
  106. package/src/types.ts +20 -1
@@ -1,10 +1,7 @@
1
1
  // AUTO-GENERATED from manifest.json — do not edit by hand.
2
2
  // Regenerate with: yarn workspace @lifi/composer-sdk generate
3
3
 
4
- import type {
5
- AnyBindable,
6
- FlowBuilderInternal,
7
- } from '../authoring/FlowBuilderCore.js';
4
+ import type { FlowBuilderInternal } from '../authoring/FlowBuilderCore.js';
8
5
  import type { Bindable, OutputHandle } from '../authoring/handles.js';
9
6
  import type {
10
7
  CoreCallResult,
@@ -38,6 +35,7 @@ import type {
38
35
  CoreSplitConfig,
39
36
  CoreStaticCallConfig,
40
37
  CoreTransferConfig,
38
+ FlySwapConfig,
41
39
  InvariantEqConfig,
42
40
  InvariantGtConfig,
43
41
  InvariantGteConfig,
@@ -47,6 +45,7 @@ import type {
47
45
  InvariantNumericConfig,
48
46
  InvariantNumericInRangeConfig,
49
47
  LifiSwapConfig,
48
+ LifiZapAsyncConfig,
50
49
  LifiZapConfig,
51
50
  MorphoBlueSupplyCollateralConfig,
52
51
  MorphoBlueWithdrawCollateralConfig,
@@ -520,11 +519,22 @@ export interface MorphoBlueWithdrawCollateralArgs {
520
519
  export interface LifiZapArgs {
521
520
  readonly bind: {
522
521
  readonly amountIn: Bindable<'resource'>;
522
+ readonly recipient?: Bindable<'address'>;
523
523
  };
524
524
  readonly config: LifiZapConfig;
525
525
  readonly guards?: readonly TypedGuard<'amountOut'>[];
526
526
  }
527
527
 
528
+ /** Route tokens into a DeFi protocol position whose output becomes available after the initiating transaction settles */
529
+ export interface LifiZapAsyncArgs {
530
+ readonly bind: {
531
+ readonly amountIn: Bindable<'resource'>;
532
+ readonly recipient: Bindable<'address'>;
533
+ };
534
+ readonly config: LifiZapAsyncConfig;
535
+ readonly guards?: readonly TypedGuard<never>[];
536
+ }
537
+
528
538
  /** Exact-output (BUY) swap via the Paraswap/Velora Market API: produce exactly `exactAmountOut` of the destination token, pulling only the source needed and exposing the genuinely-unspent source as `unspentIn`. */
529
539
  export interface ParaswapBuyArgs {
530
540
  readonly bind: {
@@ -541,6 +551,15 @@ export interface CorePeekArgs {
541
551
  readonly guards?: readonly TypedGuard<'result'>[];
542
552
  }
543
553
 
554
+ /** Swap via Fly's DexAggregator, sized at runtime: the router calldata leaves `amountIn` unset, so an upstream op's output can feed the swap without being known at plan time. Either leg may be the chain-native coin. */
555
+ export interface FlySwapArgs {
556
+ readonly bind: {
557
+ readonly amountIn: Bindable<'resource'>;
558
+ };
559
+ readonly config: FlySwapConfig;
560
+ readonly guards?: readonly TypedGuard<'unspentIn'>[];
561
+ }
562
+
544
563
  export interface CoreSplitOutputs {
545
564
  readonly a: OutputHandle<'resource'>;
546
565
  readonly b: OutputHandle<'resource'>;
@@ -629,6 +648,10 @@ export interface LifiZapOutputs {
629
648
  readonly amountOut: OutputHandle<'resource'>;
630
649
  }
631
650
 
651
+ export interface LifiZapAsyncOutputs {
652
+ readonly amountOut: OutputHandle<'resource'>;
653
+ }
654
+
632
655
  export interface ParaswapBuyOutputs {
633
656
  readonly amountOut: OutputHandle<'resource'>;
634
657
  readonly unspentIn: OutputHandle<'resource'>;
@@ -638,6 +661,11 @@ export interface CorePeekOutputs {
638
661
  readonly result: OutputHandle<'uint256'>;
639
662
  }
640
663
 
664
+ export interface FlySwapOutputs {
665
+ readonly amountOut: OutputHandle<'resource'>;
666
+ readonly unspentIn: OutputHandle<'resource'>;
667
+ }
668
+
641
669
  export type GeneratedOps = {
642
670
  readonly core: {
643
671
  /** Split a resource into two outputs by basis-point ratio — original id: "core.split" */
@@ -741,6 +769,11 @@ export type GeneratedOps = {
741
769
  readonly swap: (nodeId: string, args: LifiSwapArgs) => LifiSwapOutputs;
742
770
  /** Route tokens into a DeFi protocol position via the edge catalog — original id: "lifi.zap" */
743
771
  readonly zap: (nodeId: string, args: LifiZapArgs) => LifiZapOutputs;
772
+ /** Route tokens into a DeFi protocol position whose output becomes available after the initiating transaction settles — original id: "lifi.zapAsync" */
773
+ readonly zapAsync: (
774
+ nodeId: string,
775
+ args: LifiZapAsyncArgs,
776
+ ) => LifiZapAsyncOutputs;
744
777
  };
745
778
  readonly invariant: {
746
779
  /** Assert that an account holds at least a minimum token balance — original id: "invariant.balanceAtLeast" */
@@ -840,6 +873,10 @@ export type GeneratedOps = {
840
873
  /** Exact-output (BUY) swap via the Paraswap/Velora Market API: produce exactly `exactAmountOut` of the destination token, pulling only the source needed and exposing the genuinely-unspent source as `unspentIn`. — original id: "paraswap.buy" */
841
874
  readonly buy: (nodeId: string, args: ParaswapBuyArgs) => ParaswapBuyOutputs;
842
875
  };
876
+ readonly fly: {
877
+ /** Swap via Fly's DexAggregator, sized at runtime: the router calldata leaves `amountIn` unset, so an upstream op's output can feed the swap without being known at plan time. Either leg may be the chain-native coin. — original id: "fly.swap" */
878
+ readonly swap: (nodeId: string, args: FlySwapArgs) => FlySwapOutputs;
879
+ };
843
880
  };
844
881
 
845
882
  export const bindGeneratedOps = (
@@ -917,8 +954,8 @@ export const bindGeneratedOps = (
917
954
  call: <TSig extends string>(nodeId: string, args: CoreCallArgs<TSig>) => {
918
955
  const wire = buildCallWireFormat({
919
956
  resource: args.resource,
920
- bind: args.bind as Record<string, AnyBindable>,
921
- config: args.config as Record<string, unknown>,
957
+ bind: args.bind,
958
+ config: args.config,
922
959
  guards: args.guards,
923
960
  });
924
961
  return builder.call(nodeId, wire.op, wire, {
@@ -933,8 +970,8 @@ export const bindGeneratedOps = (
933
970
  nodeId,
934
971
  'core.staticCall',
935
972
  buildStaticCallWireFormat({
936
- bind: args.bind as Record<string, AnyBindable>,
937
- config: args.config as Record<string, unknown>,
973
+ bind: args.bind,
974
+ config: args.config,
938
975
  guards: args.guards,
939
976
  }),
940
977
  { result: 'uint256' } as const,
@@ -960,6 +997,10 @@ export const bindGeneratedOps = (
960
997
  builder.call(nodeId, 'lifi.zap', args, {
961
998
  amountOut: 'resource',
962
999
  } as const),
1000
+ zapAsync: (nodeId: string, args: LifiZapAsyncArgs) =>
1001
+ builder.call(nodeId, 'lifi.zapAsync', args, {
1002
+ amountOut: 'resource',
1003
+ } as const),
963
1004
  },
964
1005
  invariant: {
965
1006
  balanceAtLeast: (nodeId: string, args: InvariantBalanceAtLeastArgs) =>
@@ -1078,4 +1119,11 @@ export const bindGeneratedOps = (
1078
1119
  unspentIn: 'resource',
1079
1120
  } as const),
1080
1121
  },
1122
+ fly: {
1123
+ swap: (nodeId: string, args: FlySwapArgs) =>
1124
+ builder.call(nodeId, 'fly.swap', args, {
1125
+ amountOut: 'resource',
1126
+ unspentIn: 'resource',
1127
+ } as const),
1128
+ },
1081
1129
  });
package/src/index.ts CHANGED
@@ -19,9 +19,11 @@ export type {
19
19
  OutputKind,
20
20
  TypedGuard,
21
21
  TypedRef,
22
+ TypedLiteral,
22
23
  } from './types.js';
23
24
 
24
25
  export type {
26
+ ChainSummary,
25
27
  GetZapPacksOptions,
26
28
  ZapPackEdge,
27
29
  ZapPackOverview,
@@ -36,11 +38,15 @@ export type {
36
38
  ApprovalEntry,
37
39
  Call,
38
40
  ComposeCompilePartialData,
41
+ ComposeAction,
42
+ ComposeFill,
43
+ ComposeFee,
44
+ ComposeUserProgramSimulation,
39
45
  ComposeCompileRequest,
40
46
  ComposeCompileResult,
41
47
  ComposeCompileSuccessData,
42
- ComposeContinuation,
43
48
  ComposeErrorKind,
49
+ ComposeRouteRequest,
44
50
  ComposeTransactionRequest,
45
51
  FailedPreparedOp,
46
52
  Flow,
@@ -50,8 +56,13 @@ export type {
50
56
  Precondition,
51
57
  PreconditionConfigOf,
52
58
  PriceImpact,
59
+ ProducedResource,
53
60
  Ref,
54
61
  Resource,
62
+ RouteAmount,
63
+ RouteAmountAll,
64
+ RouteAmountExact,
65
+ SimulatedAmounts,
55
66
  SimulationPolicy,
56
67
  SimulationRevert,
57
68
  SolType,
@@ -83,6 +94,8 @@ export { isComposeError, isComposePreparationError } from './errors.js';
83
94
 
84
95
  export type {
85
96
  Bindable,
97
+ RefBindable,
98
+ AnyRefBindable,
86
99
  InputHandle,
87
100
  ResourceInputHandle,
88
101
  OutputHandle,
@@ -108,6 +121,7 @@ import * as materialisersMod from './generated/materialisers.generated.js';
108
121
  import * as preconditionsMod from './generated/preconditions.generated.js';
109
122
  import * as rawMod from './raw.js';
110
123
  import * as resourcesMod from './resources.js';
124
+ import * as routeAmountMod from './routeAmount.js';
111
125
 
112
126
  /**
113
127
  * Guard factories for protecting operation outputs (e.g. slippage tolerance).
@@ -168,6 +182,24 @@ export const preconditions: typeof preconditionsMod = preconditionsMod;
168
182
  * ```
169
183
  */
170
184
  export const resources: typeof resourcesMod = resourcesMod;
185
+ /**
186
+ * Amount constructors for {@link ComposeSdk.route}. Use
187
+ * {@link routeAmount.exact} to spend a fixed amount of the `fromToken`, and
188
+ * {@link routeAmount.all} to spend the signer's whole balance.
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * // USDC into the Aave v3 aEthUSDC position — one routing-catalog edge.
193
+ * const result = await sdk.route({
194
+ * chainId: 1,
195
+ * fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
196
+ * toToken: '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c',
197
+ * amount: routeAmount.exact(1_000_000n),
198
+ * signer: '0xYourAddress...',
199
+ * });
200
+ * ```
201
+ */
202
+ export const routeAmount: typeof routeAmountMod = routeAmountMod;
171
203
  export * from './generated/config.generated.js';
172
204
 
173
205
  /**
package/src/raw.ts CHANGED
@@ -6,5 +6,5 @@
6
6
  * For most use cases, prefer the generated operation methods on {@link FlowBuilder}
7
7
  * (e.g. `builder.lifi.swap(...)`) and the typed guard/materialiser helpers.
8
8
  */
9
- export { rawGuard as guard, ref } from './authoring/raw.js';
9
+ export { rawGuard as guard, literal, ref } from './authoring/raw.js';
10
10
  export { materialiser } from './run/inputs.js';
@@ -0,0 +1,61 @@
1
+ import type { RouteAmountAll, RouteAmountExact } from '@lifi/compose-spec';
2
+
3
+ // Constructors for the `amount` member of a `ComposeRouteRequest`. The wire
4
+ // union is discriminated by `type` and — deliberately, because the two variants
5
+ // mean different things — names its payload field differently in each variant:
6
+ // `amount` is the sum actually moved, `simAmount` is only a quoting stand-in for
7
+ // a balance resolved on-chain. These helpers keep call sites from having to
8
+ // remember which name goes with which discriminant.
9
+
10
+ /**
11
+ * Spend an exact amount of the route's `fromToken`.
12
+ *
13
+ * @param amount - Amount in the token's smallest unit. `bigint` is serialised
14
+ * to a decimal string when the request is sent.
15
+ * @returns The `EXACT` member of the route amount union.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const result = await sdk.route({
20
+ * chainId: 1,
21
+ * fromToken: NATIVE_ETH,
22
+ * toToken: WETH,
23
+ * amount: routeAmount.exact(10n ** 17n), // 0.1 ETH, wrapped
24
+ * signer: OWNER,
25
+ * });
26
+ * ```
27
+ */
28
+ export const exact = (amount: bigint | string): RouteAmountExact => ({
29
+ type: 'EXACT',
30
+ amount,
31
+ });
32
+
33
+ /**
34
+ * Spend the signer's entire `fromToken` balance, resolved on-chain at execution
35
+ * time.
36
+ *
37
+ * Not supported when `fromToken` is the chain's native sentinel — the server
38
+ * rejects it. A gas coin is sent via `msg.value`, so there is no on-chain
39
+ * balance to sweep; use {@link exact} for native inputs.
40
+ *
41
+ * @param simAmount - A realistic estimate of that balance, in the token's
42
+ * smallest unit. The server quotes and simulates against this value, so it
43
+ * shapes the returned quote and preconditions — but the amount actually moved
44
+ * is whatever the balance turns out to be.
45
+ * @returns The `ALL` member of the route amount union.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const result = await sdk.route({
50
+ * chainId: 1,
51
+ * fromToken: USDC,
52
+ * toToken: A_ETH_USDC,
53
+ * amount: routeAmount.all(1_000_000n), // estimate: ~1 USDC on hand
54
+ * signer: OWNER,
55
+ * });
56
+ * ```
57
+ */
58
+ export const all = (simAmount: bigint | string): RouteAmountAll => ({
59
+ type: 'ALL',
60
+ simAmount,
61
+ });
package/src/run/inputs.ts CHANGED
@@ -54,6 +54,13 @@ export interface ComposeRunInput<T extends InputSchema = InputSchema> {
54
54
  * result with the transaction and revert diagnostics.
55
55
  */
56
56
  readonly simulationPolicy?: SimulationPolicy;
57
+ /**
58
+ * When `true`, the server also simulates the final user-facing transaction
59
+ * after the structured simulation succeeds; reverts honour
60
+ * `simulationPolicy`. On success the result is echoed back on the response as
61
+ * `userProgramSimulation`. Defaults to `false`.
62
+ */
63
+ readonly simulateUserProgram?: boolean;
57
64
  /**
58
65
  * When `true`, the server checks on-chain ERC-20 allowances and omits
59
66
  * approvals that are already sufficient. Defaults to `false`.
@@ -89,6 +96,21 @@ export const materialiser = (
89
96
  ...config,
90
97
  });
91
98
 
99
+ /**
100
+ * Drops the keys whose value is `undefined`, so optional fields are omitted
101
+ * from the wire payload rather than serialized as `null`.
102
+ *
103
+ * A local copy of `pickDefined` from `@lifi/utils`: this package is published
104
+ * to npm and `@lifi/utils` is a private workspace package, so it cannot be a
105
+ * runtime dependency here.
106
+ */
107
+ const pickDefined = <S extends Record<string, unknown>>(
108
+ obj: S,
109
+ ): Partial<{ [K in keyof S]: Exclude<S[K], undefined> }> =>
110
+ Object.fromEntries(
111
+ Object.entries(obj).filter(([, value]) => value !== undefined),
112
+ ) as Partial<{ [K in keyof S]: Exclude<S[K], undefined> }>;
113
+
92
114
  /**
93
115
  * Assembles a {@link ComposeRun} object from individual parameters.
94
116
  * Used internally by the SDK; prefer {@link ComposeRunInput} for external use.
@@ -105,24 +127,21 @@ export const buildRun = (run: {
105
127
  maxPriceImpactBps?: number;
106
128
  sweepTo?: SweepTo;
107
129
  simulationPolicy?: SimulationPolicy;
130
+ simulateUserProgram?: boolean;
108
131
  checkOnChainAllowances?: boolean;
109
132
  }): ComposeRun => ({
110
133
  inputs: run.inputs,
111
- ...(run.preconditions?.length && { preconditions: run.preconditions }),
112
134
  signer: run.signer,
113
- ...(run.assumptions && { assumptions: run.assumptions }),
114
- ...(run.referrer !== undefined && { referrer: run.referrer }),
115
- ...(run.integratorFeeBps !== undefined && {
135
+ ...pickDefined({
136
+ // An empty precondition list is the same as none, so drop it too.
137
+ preconditions: run.preconditions?.length ? run.preconditions : undefined,
138
+ assumptions: run.assumptions,
139
+ referrer: run.referrer,
116
140
  integratorFeeBps: run.integratorFeeBps,
117
- }),
118
- ...(run.maxPriceImpactBps !== undefined && {
119
141
  maxPriceImpactBps: run.maxPriceImpactBps,
120
- }),
121
- ...(run.sweepTo !== undefined && { sweepTo: run.sweepTo }),
122
- ...(run.simulationPolicy !== undefined && {
142
+ sweepTo: run.sweepTo,
123
143
  simulationPolicy: run.simulationPolicy,
124
- }),
125
- ...(run.checkOnChainAllowances !== undefined && {
144
+ simulateUserProgram: run.simulateUserProgram,
126
145
  checkOnChainAllowances: run.checkOnChainAllowances,
127
146
  }),
128
147
  });
@@ -12,8 +12,11 @@ import type {
12
12
  export interface BuildSimulateRequestInput {
13
13
  /**
14
14
  * A result from `builder.compile(...)` / `client.compile(...)`. Both
15
- * `success` and `partial` results carry a `transactionRequest`, so either is
16
- * valid input — the helper does not discriminate on `status`.
15
+ * `success` and `partial` results are valid input — the helper does not
16
+ * discriminate on `status`. A success result for an empty compiled program
17
+ * (a settle-only `funding: 'external'` flow) carries no
18
+ * `transactionRequest`; there is nothing to simulate and this helper
19
+ * throws — submit the result's `actions` entries directly instead.
17
20
  */
18
21
  readonly result: ComposeCompileResult;
19
22
  /** EVM chain id. A compile result does not carry the target chain. */
@@ -65,6 +68,15 @@ export const buildSimulateRequest = (
65
68
  ): SimulateRequest => {
66
69
  const { result, chainId, signer, trackedBalances, requirements, block } =
67
70
  input;
71
+ // A settle-only flow authored with `funding: 'external'` compiles to an
72
+ // empty program: the success response carries no `transactionRequest`, so
73
+ // there is nothing to simulate — the required submissions are the
74
+ // `actions` entries, submitted directly.
75
+ if (result.transactionRequest === undefined) {
76
+ throw new Error(
77
+ 'buildSimulateRequest: the compile result has no transactionRequest (empty compiled program — a settle-only external-funding flow); submit the `actions` entries instead of simulating',
78
+ );
79
+ }
68
80
  return {
69
81
  chainId,
70
82
  from: signer,
package/src/sdk.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  ComposeCompileRequest,
3
3
  ComposeCompileResult,
4
+ ComposeRouteRequest,
4
5
  InputSpec,
5
6
  SimulateRequest,
6
7
  SimulateResult,
@@ -13,6 +14,7 @@ import {
13
14
  type TypedFlow,
14
15
  } from './authoring/FlowBuilderCore.js';
15
16
  import { type ComposeClient, createComposeClient } from './client.js';
17
+ import type { ChainSummary } from './discovery.js';
16
18
  import {
17
19
  bindGeneratedOps,
18
20
  type GeneratedOps,
@@ -108,6 +110,33 @@ export interface ComposeSdk {
108
110
  chainId: number,
109
111
  options: FlowOptions<T>,
110
112
  ) => FlowBuilder<T>;
113
+ /**
114
+ * Fetches the chains this Compose deployment supports, ascending by
115
+ * `chainId`. Thin pass-through to {@link ComposeClient.getChains}; see it
116
+ * for what "supported" means and for the caching caveat.
117
+ *
118
+ * Each entry pairs the `chainId` with a display `name`, so one call both
119
+ * gates a chain selector and labels it. Key on `chainId`; `name` is a
120
+ * deployment-maintained label that can change.
121
+ *
122
+ * Use it to fail fast before building a flow for a chain the server would
123
+ * reject.
124
+ *
125
+ * @returns The supported chains, ascending by `chainId`.
126
+ * @throws {@link ComposeError} on network or server errors.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const chains = await sdk.getSupportedChains();
131
+ * if (!chains.some((c) => c.chainId === 8453)) {
132
+ * throw new Error('Base is not supported');
133
+ * }
134
+ * for (const { chainId, name } of chains) {
135
+ * console.log(`${name} (${chainId})`);
136
+ * }
137
+ * ```
138
+ */
139
+ readonly getSupportedChains: () => Promise<readonly ChainSummary[]>;
111
140
  /**
112
141
  * Builds a raw compile request without sending it. Useful for inspecting the
113
142
  * request payload or submitting it through a custom transport.
@@ -119,6 +148,21 @@ export interface ComposeSdk {
119
148
  flow: TypedFlow<T>,
120
149
  run: ComposeRunInput<T>,
121
150
  ) => ComposeCompileRequest;
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(...)`.
158
+ *
159
+ * @param request - The from/to token pair, amount, signer, and route options.
160
+ * @returns A discriminated result: `status: 'success'` or `status: 'partial'`.
161
+ * @throws {@link ComposeError} on network, validation, or server errors.
162
+ */
163
+ readonly route: (
164
+ request: ComposeRouteRequest,
165
+ ) => Promise<ComposeCompileResult>;
122
166
  /**
123
167
  * Simulates a raw, pre-encoded transaction. Thin pass-through to
124
168
  * {@link ComposeClient.simulate}; see it for the `ok` / `revert` / `error`
@@ -162,6 +206,7 @@ export const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => {
162
206
  maxPriceImpactBps: run.maxPriceImpactBps,
163
207
  sweepTo: run.sweepTo,
164
208
  simulationPolicy: run.simulationPolicy,
209
+ simulateUserProgram: run.simulateUserProgram,
165
210
  checkOnChainAllowances: run.checkOnChainAllowances,
166
211
  }),
167
212
  });
@@ -187,7 +232,9 @@ export const createComposeSdk = (options: ComposeSdkOptions): ComposeSdk => {
187
232
  return {
188
233
  client: composeClient,
189
234
  flow,
235
+ getSupportedChains: composeClient.getChains,
190
236
  request,
237
+ route: composeClient.route,
191
238
  simulate: composeClient.simulate,
192
239
  };
193
240
  };
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Ref } from '@lifi/compose-spec';
1
+ import type { LiteralBinding, Ref } from '@lifi/compose-spec';
2
2
  import {
3
3
  InputSpec,
4
4
  MaterialiserInput,
@@ -58,6 +58,25 @@ export interface TypedRef<T extends OutputKind = OutputKind> extends Ref {
58
58
  readonly __outputKind?: T;
59
59
  }
60
60
 
61
+ /**
62
+ * A literal bind value carrying a phantom type parameter.
63
+ *
64
+ * The wire format accepts a constant in a bind slot as
65
+ * `{ kind: <solType>, value: <string> }`, alongside `$ref` pointers. Use it
66
+ * for a value that must be baked into the flow document rather than supplied
67
+ * at run time — the destination of a flow the backend stores and re-runs
68
+ * later (a `continuation.settle` node's `continuationFlow`) has no run to
69
+ * take an input from.
70
+ *
71
+ * @typeParam T - The Solidity type of the literal (e.g. `'address'`).
72
+ */
73
+ export interface TypedLiteral<
74
+ T extends SolType = SolType,
75
+ > extends LiteralBinding {
76
+ readonly kind: T;
77
+ readonly __outputKind?: T;
78
+ }
79
+
61
80
  /**
62
81
  * An input declaration in a flow schema.
63
82
  * `Resource` values become resource inputs (token amounts);