@lifi/composer-sdk 0.5.0 → 0.6.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 (72) hide show
  1. package/README.md +13 -3
  2. package/dist/authoring/FlowBuilderCore.cjs.map +1 -1
  3. package/dist/authoring/FlowBuilderCore.d.cts +11 -4
  4. package/dist/authoring/FlowBuilderCore.d.ts +11 -4
  5. package/dist/authoring/FlowBuilderCore.js.map +1 -1
  6. package/dist/authoring/handles.cjs.map +1 -1
  7. package/dist/authoring/handles.d.cts +2 -2
  8. package/dist/authoring/handles.d.ts +2 -2
  9. package/dist/authoring/handles.js.map +1 -1
  10. package/dist/authoring/raw.cjs +5 -3
  11. package/dist/authoring/raw.cjs.map +1 -1
  12. package/dist/authoring/raw.js +5 -3
  13. package/dist/authoring/raw.js.map +1 -1
  14. package/dist/client.cjs +94 -60
  15. package/dist/client.cjs.map +1 -1
  16. package/dist/client.d.cts +12 -4
  17. package/dist/client.d.ts +12 -4
  18. package/dist/client.js +98 -60
  19. package/dist/client.js.map +1 -1
  20. package/dist/errors.cjs +42 -3
  21. package/dist/errors.cjs.map +1 -1
  22. package/dist/errors.d.cts +67 -4
  23. package/dist/errors.d.ts +67 -4
  24. package/dist/errors.js +29 -2
  25. package/dist/errors.js.map +1 -1
  26. package/dist/generated/config.generated.cjs.map +1 -1
  27. package/dist/generated/config.generated.d.cts +46 -5
  28. package/dist/generated/config.generated.d.ts +46 -5
  29. package/dist/generated/operations.generated.cjs +4 -0
  30. package/dist/generated/operations.generated.cjs.map +1 -1
  31. package/dist/generated/operations.generated.d.cts +16 -2
  32. package/dist/generated/operations.generated.d.ts +16 -2
  33. package/dist/generated/operations.generated.js +4 -0
  34. package/dist/generated/operations.generated.js.map +1 -1
  35. package/dist/index.cjs +8 -1
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +4 -3
  38. package/dist/index.d.ts +4 -3
  39. package/dist/index.js +11 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/outputs.cjs +33 -0
  42. package/dist/outputs.cjs.map +1 -0
  43. package/dist/outputs.d.cts +10 -0
  44. package/dist/outputs.d.ts +10 -0
  45. package/dist/outputs.js +9 -0
  46. package/dist/outputs.js.map +1 -0
  47. package/dist/responseSchemas.cjs +3 -0
  48. package/dist/responseSchemas.cjs.map +1 -1
  49. package/dist/responseSchemas.d.cts +7 -4
  50. package/dist/responseSchemas.d.ts +7 -4
  51. package/dist/responseSchemas.js +3 -0
  52. package/dist/responseSchemas.js.map +1 -1
  53. package/dist/types.cjs.map +1 -1
  54. package/dist/types.d.cts +16 -5
  55. package/dist/types.d.ts +16 -5
  56. package/package.json +4 -3
  57. package/src/authoring/FlowBuilderCore.ts +12 -3
  58. package/src/authoring/handles.ts +2 -1
  59. package/src/authoring/raw.ts +4 -3
  60. package/src/client.ts +177 -79
  61. package/src/errors.ts +116 -3
  62. package/src/examples/flySwap.ts +3 -3
  63. package/src/examples/lifiZapAsync.ts +8 -7
  64. package/src/examples/run.ts +6 -9
  65. package/src/examples/swapAndZap.ts +7 -0
  66. package/src/examples/swapWithAllowRevert.ts +13 -3
  67. package/src/generated/config.generated.ts +49 -4
  68. package/src/generated/operations.generated.ts +25 -0
  69. package/src/index.ts +21 -3
  70. package/src/outputs.ts +16 -0
  71. package/src/responseSchemas.ts +10 -4
  72. package/src/types.ts +16 -4
@@ -91,18 +91,28 @@ export const handleCompileResult = async (): Promise<void> => {
91
91
 
92
92
  if (result.status === 'success') {
93
93
  // Full success — transactionRequest includes gasLimit.
94
- const { transactionRequest, userProxy, producedResources } = result;
94
+ const { transactionRequest, userProxy, outputs } = result;
95
95
  void transactionRequest;
96
96
  void userProxy;
97
- void producedResources;
97
+ // Every linear resource output port appears in `outputs`, keyed
98
+ // `<nodeId>.<port>`. The swap's terminal output carries the amount.
99
+ const swapOut = outputs['swap.amountOut'];
100
+ void swapOut?.amount.estimate?.value; // simulated estimate on a 200
101
+ void swapOut?.amount.minimum?.value; // enforced floor, absent if none known
98
102
  return;
99
103
  }
100
104
 
101
105
  // Partial result — simulation reverted but a transaction is still available.
102
106
  // transactionRequest omits gasLimit; the caller must estimate gas themselves.
103
- const { transactionRequest, simulationRevert, error } = result;
107
+ const { transactionRequest, outputs, simulationRevert, error } = result;
104
108
  void transactionRequest;
105
109
 
110
+ // Nothing was measured, so the estimate is a quote and the minimum is
111
+ // op-sourced (or absent) rather than a guard floor.
112
+ const swapOut = outputs['swap.amountOut'];
113
+ void swapOut?.amount.estimate?.value; // quoted on a 206
114
+ void swapOut?.amount.minimum?.value; // op/declared floor, absent if none
115
+
106
116
  // Inspect the revert reason.
107
117
  void simulationRevert.code;
108
118
  void simulationRevert.rawErrorBytes;
@@ -60,6 +60,55 @@ export interface LifiSwapConfig {
60
60
  };
61
61
  }
62
62
 
63
+ /**
64
+ * a string starting with "0x"
65
+ */
66
+ export type StartsWith0X = string;
67
+ /**
68
+ * a number between 0 and 1
69
+ */
70
+ export type Between01 = number;
71
+
72
+ export interface LifiBridgeConfig {
73
+ resourceOut:
74
+ | {
75
+ kind: 'native';
76
+ chainId: Positive;
77
+ }
78
+ | {
79
+ kind: 'erc20';
80
+ token: Address;
81
+ chainId: Positive;
82
+ };
83
+ destination: StartsWith0X;
84
+ slippage?: Between01;
85
+ timing?: {
86
+ swapStepTimingStrategies?: {
87
+ strategy: 'minWaitTime';
88
+ minWaitTimeMs: number;
89
+ startingExpectedResults: number;
90
+ reduceEveryMs: number;
91
+ }[];
92
+ routeTimingStrategies?: {
93
+ strategy: 'minWaitTime';
94
+ minWaitTimeMs: number;
95
+ startingExpectedResults: number;
96
+ reduceEveryMs: number;
97
+ }[];
98
+ };
99
+ bridges?: {
100
+ allow?: MaxItems50;
101
+ deny?: MaxItems50;
102
+ prefer?: MaxItems50;
103
+ };
104
+ exchanges?: {
105
+ allow?: MaxItems50;
106
+ deny?: MaxItems50;
107
+ prefer?: MaxItems50;
108
+ };
109
+ order?: 'FASTEST' | 'CHEAPEST';
110
+ }
111
+
63
112
  export interface InvariantNumericConfig {
64
113
  op: 'gte' | 'gt' | 'lte' | 'lt' | 'eq' | 'neq';
65
114
  threshold: IntegerStringInput;
@@ -671,10 +720,6 @@ export type ContinuationAssertReadyConfig =
671
720
  comparator: 'gte' | 'eq' | 'lte';
672
721
  value: string;
673
722
  };
674
- /**
675
- * a string starting with "0x"
676
- */
677
- export type StartsWith0X = string;
678
723
 
679
724
  export interface LifiZapConfig {
680
725
  resourceOut:
@@ -44,6 +44,7 @@ import type {
44
44
  InvariantNeqConfig,
45
45
  InvariantNumericConfig,
46
46
  InvariantNumericInRangeConfig,
47
+ LifiBridgeConfig,
47
48
  LifiSwapConfig,
48
49
  LifiZapAsyncConfig,
49
50
  LifiZapConfig,
@@ -79,6 +80,15 @@ export interface LifiSwapArgs {
79
80
  readonly guards?: readonly TypedGuard<'unspentIn'>[];
80
81
  }
81
82
 
83
+ /** Bridge tokens to another chain via the LI.FI aggregator API */
84
+ export interface LifiBridgeArgs {
85
+ readonly bind: {
86
+ readonly amountIn: Bindable<'resource'>;
87
+ };
88
+ readonly config: LifiBridgeConfig;
89
+ readonly guards?: readonly TypedGuard<'unspentIn'>[];
90
+ }
91
+
82
92
  /** Assert that an account holds at least a minimum token balance */
83
93
  export interface InvariantBalanceAtLeastArgs {
84
94
  readonly bind: {
@@ -574,6 +584,11 @@ export interface LifiSwapOutputs {
574
584
  readonly unspentIn: OutputHandle<'resource'>;
575
585
  }
576
586
 
587
+ export interface LifiBridgeOutputs {
588
+ readonly amountOut: OutputHandle<'resource'>;
589
+ readonly unspentIn: OutputHandle<'resource'>;
590
+ }
591
+
577
592
  export interface CoreAddOutputs {
578
593
  readonly result: OutputHandle<'uint256'>;
579
594
  }
@@ -767,6 +782,11 @@ export type GeneratedOps = {
767
782
  readonly lifi: {
768
783
  /** Swap tokens via the LI.FI aggregator API — original id: "lifi.swap" */
769
784
  readonly swap: (nodeId: string, args: LifiSwapArgs) => LifiSwapOutputs;
785
+ /** Bridge tokens to another chain via the LI.FI aggregator API — original id: "lifi.bridge" */
786
+ readonly bridge: (
787
+ nodeId: string,
788
+ args: LifiBridgeArgs,
789
+ ) => LifiBridgeOutputs;
770
790
  /** Route tokens into a DeFi protocol position via the edge catalog — original id: "lifi.zap" */
771
791
  readonly zap: (nodeId: string, args: LifiZapArgs) => LifiZapOutputs;
772
792
  /** Route tokens into a DeFi protocol position whose output becomes available after the initiating transaction settles — original id: "lifi.zapAsync" */
@@ -993,6 +1013,11 @@ export const bindGeneratedOps = (
993
1013
  amountOut: 'resource',
994
1014
  unspentIn: 'resource',
995
1015
  } as const),
1016
+ bridge: (nodeId: string, args: LifiBridgeArgs) =>
1017
+ builder.call(nodeId, 'lifi.bridge', args, {
1018
+ amountOut: 'resource',
1019
+ unspentIn: 'resource',
1020
+ } as const),
996
1021
  zap: (nodeId: string, args: LifiZapArgs) =>
997
1022
  builder.call(nodeId, 'lifi.zap', args, {
998
1023
  amountOut: 'resource',
package/src/index.ts CHANGED
@@ -31,6 +31,8 @@ export type {
31
31
  export type {
32
32
  ComposeError,
33
33
  ComposeErrorCode,
34
+ ComposeSdkOutdated,
35
+ ComposeServerOutdated,
34
36
  ComposePreparationOps,
35
37
  } from './errors.js';
36
38
  export type {
@@ -53,16 +55,25 @@ export type {
53
55
  InputSpec,
54
56
  MaterialiserConfigOf,
55
57
  MaterialiserInput,
58
+ OutputAmount,
59
+ Amount,
60
+ ConsumedOutput,
61
+ Delivery,
62
+ Estimate,
63
+ EstimateBasis,
64
+ Minimum,
65
+ MinimumBasis,
66
+ Output,
67
+ Outputs,
68
+ TerminalOutput,
56
69
  Precondition,
57
70
  PreconditionConfigOf,
58
71
  PriceImpact,
59
- ProducedResource,
60
72
  Ref,
61
73
  Resource,
62
74
  RouteAmount,
63
75
  RouteAmountAll,
64
76
  RouteAmountExact,
65
- SimulatedAmounts,
66
77
  SimulationPolicy,
67
78
  SimulationRevert,
68
79
  SolType,
@@ -90,7 +101,12 @@ export {
90
101
  SIMULATE_MAX_TRACKED_BALANCES,
91
102
  SIMULATE_MAX_REQUIREMENTS,
92
103
  } from '@lifi/compose-spec';
93
- export { isComposeError, isComposePreparationError } from './errors.js';
104
+ export {
105
+ isComposeError,
106
+ isComposePreparationError,
107
+ isComposeSdkOutdatedError,
108
+ isComposeServerOutdatedError,
109
+ } from './errors.js';
94
110
 
95
111
  export type {
96
112
  Bindable,
@@ -113,6 +129,8 @@ export { materialiser } from './run/inputs.js';
113
129
  export { buildSimulateRequest } from './run/simulate.js';
114
130
  export type { BuildSimulateRequestInput } from './run/simulate.js';
115
131
 
132
+ export { terminalOutputs } from './outputs.js';
133
+
116
134
  export { createComposeSdk } from './sdk.js';
117
135
  export type { ComposeSdk, ComposeSdkOptions, FlowBuilder } from './sdk.js';
118
136
 
package/src/outputs.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { Outputs, TerminalOutput } from '@lifi/compose-spec';
2
+
3
+ /**
4
+ * The entries of `outputs` the caller actually receives: every port no later
5
+ * node spends, keyed `<nodeId>.<portName>` as on the wire. Consumed ports
6
+ * (intermediate amounts a later op takes as input) are dropped.
7
+ */
8
+ export const terminalOutputs = (
9
+ outputs: Outputs,
10
+ ): Record<string, TerminalOutput> =>
11
+ Object.fromEntries(
12
+ Object.entries(outputs).filter(
13
+ (entry): entry is [string, TerminalOutput] =>
14
+ entry[1].role === 'terminal',
15
+ ),
16
+ );
@@ -90,12 +90,12 @@ export const parseSimulateResult = (body: unknown): SimulateResult | null => {
90
90
 
91
91
  /**
92
92
  * The validated members of an HTTP 206 partial compile envelope. `data` is
93
- * verified to be an object but kept untyped — compose-spec owns its full shape
94
- * (`ComposeCompilePartialData`) as a hand-authored type, so the caller narrows
95
- * it. `error` is fully validated.
93
+ * verified to be an object at runtime but left `unknown` here — compose-spec
94
+ * owns its full shape (`ComposeCompilePartialData`) as a hand-authored type, so
95
+ * the caller narrows it in one step. `error` is fully validated.
96
96
  */
97
97
  export interface CompilePartialEnvelope {
98
- readonly data: Record<string, unknown>;
98
+ readonly data: unknown;
99
99
  readonly error: { readonly kind: string; readonly message: string };
100
100
  }
101
101
 
@@ -131,6 +131,9 @@ export interface ServerErrorBody {
131
131
  readonly kind?: string;
132
132
  readonly message?: string;
133
133
  readonly path?: string;
134
+ readonly sdkVersion?: string;
135
+ readonly serverVersion?: string;
136
+ readonly minimumSdkVersion?: string;
134
137
  readonly details?: SimulationRevert;
135
138
  readonly failedOps?: readonly ServerFailedPreparedOp[];
136
139
  readonly succeededOps?: readonly string[];
@@ -158,6 +161,9 @@ const serverErrorBodySchema = z.object({
158
161
  kind: z.string().optional(),
159
162
  message: z.string().optional(),
160
163
  path: z.string().optional(),
164
+ sdkVersion: z.string().optional(),
165
+ serverVersion: z.string().optional(),
166
+ minimumSdkVersion: z.string().optional(),
161
167
  details: z.custom<SimulationRevert>().optional(),
162
168
  failedOps: z.array(failedPreparedOpSchema).readonly().optional(),
163
169
  succeededOps: z.array(z.string()).readonly().optional(),
package/src/types.ts CHANGED
@@ -48,14 +48,26 @@ export type AbiTypeToOutputKind<T extends string> = T extends SolType
48
48
  ? T
49
49
  : SolType;
50
50
 
51
+ /**
52
+ * Phantom brand key for {@link TypedRef}.
53
+ *
54
+ * Declared, never assigned: it exists only so the type checker can tell a
55
+ * typed ref from an arbitrary `{ $ref }` object. Keying it on a `unique
56
+ * symbol` means no caller can produce one by writing an object literal.
57
+ */
58
+ export declare const typedRefKind: unique symbol;
59
+
51
60
  /**
52
61
  * A typed `$ref` pointer carrying a phantom type parameter.
53
- * Extends `Ref` so it is accepted anywhere a plain `Ref` is.
54
- * The `__outputKind` field is never set at runtime — it exists only
55
- * for TypeScript's structural type checker to distinguish typed refs.
62
+ *
63
+ * On the wire, and at run time, this is exactly `{ $ref: string }` — the
64
+ * brand is erased. The brand is *required* in the type so a plain `Ref` does
65
+ * not satisfy `TypedRef` structurally, which is the contract `RefBindable`
66
+ * advertises: an untyped `{ $ref }` never enters a typed bind slot. Build one
67
+ * with `raw.ref<T>()`.
56
68
  */
57
69
  export interface TypedRef<T extends OutputKind = OutputKind> extends Ref {
58
- readonly __outputKind?: T;
70
+ readonly [typedRefKind]: T;
59
71
  }
60
72
 
61
73
  /**