@lifi/composer-sdk 0.0.1-alpha

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 (160) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +220 -0
  3. package/dist/authoring/FlowBuilderCore.cjs +115 -0
  4. package/dist/authoring/FlowBuilderCore.cjs.map +1 -0
  5. package/dist/authoring/FlowBuilderCore.d.cts +114 -0
  6. package/dist/authoring/FlowBuilderCore.d.ts +114 -0
  7. package/dist/authoring/FlowBuilderCore.js +91 -0
  8. package/dist/authoring/FlowBuilderCore.js.map +1 -0
  9. package/dist/authoring/handles.cjs +43 -0
  10. package/dist/authoring/handles.cjs.map +1 -0
  11. package/dist/authoring/handles.d.cts +61 -0
  12. package/dist/authoring/handles.d.ts +61 -0
  13. package/dist/authoring/handles.js +17 -0
  14. package/dist/authoring/handles.js.map +1 -0
  15. package/dist/authoring/raw.cjs +41 -0
  16. package/dist/authoring/raw.cjs.map +1 -0
  17. package/dist/authoring/raw.d.cts +47 -0
  18. package/dist/authoring/raw.d.ts +47 -0
  19. package/dist/authoring/raw.js +16 -0
  20. package/dist/authoring/raw.js.map +1 -0
  21. package/dist/authoring/signatureArgs.cjs +114 -0
  22. package/dist/authoring/signatureArgs.cjs.map +1 -0
  23. package/dist/authoring/signatureArgs.d.cts +146 -0
  24. package/dist/authoring/signatureArgs.d.ts +146 -0
  25. package/dist/authoring/signatureArgs.js +87 -0
  26. package/dist/authoring/signatureArgs.js.map +1 -0
  27. package/dist/client.cjs +140 -0
  28. package/dist/client.cjs.map +1 -0
  29. package/dist/client.d.cts +67 -0
  30. package/dist/client.d.ts +67 -0
  31. package/dist/client.js +116 -0
  32. package/dist/client.js.map +1 -0
  33. package/dist/discovery.cjs +17 -0
  34. package/dist/discovery.cjs.map +1 -0
  35. package/dist/discovery.d.cts +56 -0
  36. package/dist/discovery.d.ts +56 -0
  37. package/dist/discovery.js +1 -0
  38. package/dist/discovery.js.map +1 -0
  39. package/dist/errors.cjs +91 -0
  40. package/dist/errors.cjs.map +1 -0
  41. package/dist/errors.d.cts +77 -0
  42. package/dist/errors.d.ts +77 -0
  43. package/dist/errors.js +65 -0
  44. package/dist/errors.js.map +1 -0
  45. package/dist/generated/config.generated.cjs +17 -0
  46. package/dist/generated/config.generated.cjs.map +1 -0
  47. package/dist/generated/config.generated.d.cts +144 -0
  48. package/dist/generated/config.generated.d.ts +144 -0
  49. package/dist/generated/config.generated.js +1 -0
  50. package/dist/generated/config.generated.js.map +1 -0
  51. package/dist/generated/guards.generated.cjs +32 -0
  52. package/dist/generated/guards.generated.cjs.map +1 -0
  53. package/dist/generated/guards.generated.d.cts +4 -0
  54. package/dist/generated/guards.generated.d.ts +4 -0
  55. package/dist/generated/guards.generated.js +8 -0
  56. package/dist/generated/guards.generated.js.map +1 -0
  57. package/dist/generated/materialisers.generated.cjs +44 -0
  58. package/dist/generated/materialisers.generated.cjs.map +1 -0
  59. package/dist/generated/materialisers.generated.d.cts +4 -0
  60. package/dist/generated/materialisers.generated.d.ts +4 -0
  61. package/dist/generated/materialisers.generated.js +18 -0
  62. package/dist/generated/materialisers.generated.js.map +1 -0
  63. package/dist/generated/operations.generated.cjs +110 -0
  64. package/dist/generated/operations.generated.cjs.map +1 -0
  65. package/dist/generated/operations.generated.d.cts +337 -0
  66. package/dist/generated/operations.generated.d.ts +337 -0
  67. package/dist/generated/operations.generated.js +89 -0
  68. package/dist/generated/operations.generated.js.map +1 -0
  69. package/dist/generated/preconditions.generated.cjs +44 -0
  70. package/dist/generated/preconditions.generated.cjs.map +1 -0
  71. package/dist/generated/preconditions.generated.d.cts +4 -0
  72. package/dist/generated/preconditions.generated.d.ts +4 -0
  73. package/dist/generated/preconditions.generated.js +18 -0
  74. package/dist/generated/preconditions.generated.js.map +1 -0
  75. package/dist/guards.generated-DKKDKREA.d.ts +14 -0
  76. package/dist/guards.generated-hrQ1KPNv.d.cts +14 -0
  77. package/dist/index.cjs +76 -0
  78. package/dist/index.cjs.map +1 -0
  79. package/dist/index.d.cts +99 -0
  80. package/dist/index.d.ts +99 -0
  81. package/dist/index.js +30 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/materialisers.generated-BsA1uDW8.d.ts +24 -0
  84. package/dist/materialisers.generated-nMxTAaVi.d.cts +24 -0
  85. package/dist/preconditions.generated-DblS13Wq.d.cts +24 -0
  86. package/dist/preconditions.generated-U7YpVh30.d.ts +24 -0
  87. package/dist/raw-Dj_2rBSQ.d.cts +19 -0
  88. package/dist/raw-IxCF_bSS.d.ts +19 -0
  89. package/dist/raw.cjs +34 -0
  90. package/dist/raw.cjs.map +1 -0
  91. package/dist/raw.d.cts +4 -0
  92. package/dist/raw.d.ts +4 -0
  93. package/dist/raw.js +8 -0
  94. package/dist/raw.js.map +1 -0
  95. package/dist/resources-BKlFxjo8.d.cts +56 -0
  96. package/dist/resources-CYis1_DS.d.ts +56 -0
  97. package/dist/resources.cjs +39 -0
  98. package/dist/resources.cjs.map +1 -0
  99. package/dist/resources.d.cts +3 -0
  100. package/dist/resources.d.ts +3 -0
  101. package/dist/resources.js +14 -0
  102. package/dist/resources.js.map +1 -0
  103. package/dist/run/inputs.cjs +51 -0
  104. package/dist/run/inputs.cjs.map +1 -0
  105. package/dist/run/inputs.d.cts +91 -0
  106. package/dist/run/inputs.d.ts +91 -0
  107. package/dist/run/inputs.js +26 -0
  108. package/dist/run/inputs.js.map +1 -0
  109. package/dist/sdk.cjs +60 -0
  110. package/dist/sdk.cjs.map +1 -0
  111. package/dist/sdk.d.cts +112 -0
  112. package/dist/sdk.d.ts +112 -0
  113. package/dist/sdk.js +40 -0
  114. package/dist/sdk.js.map +1 -0
  115. package/dist/types.cjs +17 -0
  116. package/dist/types.cjs.map +1 -0
  117. package/dist/types.d.cts +74 -0
  118. package/dist/types.d.ts +74 -0
  119. package/dist/types.js +1 -0
  120. package/dist/types.js.map +1 -0
  121. package/package.json +81 -0
  122. package/src/authoring/FlowBuilderCore.ts +270 -0
  123. package/src/authoring/handles.ts +81 -0
  124. package/src/authoring/raw.ts +60 -0
  125. package/src/authoring/signatureArgs.ts +249 -0
  126. package/src/client.ts +230 -0
  127. package/src/discovery.ts +51 -0
  128. package/src/errors.ts +150 -0
  129. package/src/examples/approveAndDeposit.ts +84 -0
  130. package/src/examples/callContract.ts +199 -0
  131. package/src/examples/config.ts +9 -0
  132. package/src/examples/consolidateToEth.ts +94 -0
  133. package/src/examples/consolidateToUsdc.ts +95 -0
  134. package/src/examples/depositFromProxy.ts +87 -0
  135. package/src/examples/dustSweep.ts +66 -0
  136. package/src/examples/lifiSwap.ts +56 -0
  137. package/src/examples/lifiZap.ts +60 -0
  138. package/src/examples/rawCallWithArithmetic.ts +78 -0
  139. package/src/examples/readContractState.ts +77 -0
  140. package/src/examples/run.ts +168 -0
  141. package/src/examples/splitAndZap.ts +73 -0
  142. package/src/examples/splitWithArithmetic.ts +93 -0
  143. package/src/examples/swapAndZap.ts +72 -0
  144. package/src/examples/swapToRecipient.ts +109 -0
  145. package/src/examples/swapWithAllowRevert.ts +119 -0
  146. package/src/examples/swapWithBalanceCheck.ts +73 -0
  147. package/src/examples/swapWithOutputValidation.ts +125 -0
  148. package/src/examples/transferTokens.ts +123 -0
  149. package/src/examples/untypedOpWithTypedRef.ts +77 -0
  150. package/src/generated/config.generated.ts +174 -0
  151. package/src/generated/guards.generated.ts +14 -0
  152. package/src/generated/materialisers.generated.ts +44 -0
  153. package/src/generated/operations.generated.ts +568 -0
  154. package/src/generated/preconditions.generated.ts +43 -0
  155. package/src/index.ts +149 -0
  156. package/src/raw.ts +10 -0
  157. package/src/resources.ts +61 -0
  158. package/src/run/inputs.ts +117 -0
  159. package/src/sdk.ts +173 -0
  160. package/src/types.ts +101 -0
@@ -0,0 +1,119 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import { createComposeSdk, materialisers, resources } from '../index.js';
4
+
5
+ import { BASE_URL, OWNER } from './config.js';
6
+ const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
7
+ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
8
+
9
+ /**
10
+ * Swap WETH to USDC with `simulationPolicy: 'allow-revert'`.
11
+ *
12
+ * Demonstrates:
13
+ * - Opting into partial results when simulation detects a revert
14
+ * - The `ComposeCompileResult` discriminated union (`status: 'success' | 'partial'`)
15
+ * - Inspecting `simulationRevert` diagnostics on a partial result
16
+ * - Using `checkOnChainAllowances` to filter already-sufficient approvals
17
+ *
18
+ * When the compiled transaction reverts in simulation:
19
+ * - Default (`simulationPolicy: 'strict'`): the API returns HTTP 422 and the SDK throws.
20
+ * - With `simulationPolicy: 'allow-revert'`: the API returns HTTP 206 and the SDK returns
21
+ * a partial result containing the transaction (without `gasLimit`) and revert diagnostics.
22
+ * The caller can inspect the revert reason and decide whether to submit anyway.
23
+ */
24
+ export const buildSwapWithAllowRevertExample = (): {
25
+ flow: Flow;
26
+ request: ComposeCompileRequest;
27
+ } => {
28
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
29
+
30
+ const builder = sdk.flow(1, {
31
+ name: 'swap-weth-to-usdc-allow-revert',
32
+ inputs: {
33
+ amountIn: resources.erc20(WETH, 1),
34
+ },
35
+ });
36
+
37
+ builder.lifi.swap('swap', {
38
+ bind: { amountIn: builder.inputs.amountIn },
39
+ config: {
40
+ resourceOut: resources.erc20(USDC, 1),
41
+ slippage: 0.03,
42
+ },
43
+ });
44
+
45
+ const flow = builder.build();
46
+
47
+ // Pass simulationPolicy and checkOnChainAllowances in the run inputs.
48
+ // 'allow-revert' tells the backend to return a partial result instead of
49
+ // a 422 error when the transaction reverts in simulation.
50
+ const request = sdk.request(flow, {
51
+ signer: OWNER,
52
+ inputs: {
53
+ amountIn: materialisers.directDeposit({
54
+ amount: '1000000000000000000',
55
+ }),
56
+ },
57
+ sweepTo: builder.context.sender,
58
+ simulationPolicy: 'allow-revert',
59
+ checkOnChainAllowances: true,
60
+ });
61
+
62
+ return { flow, request };
63
+ };
64
+
65
+ /**
66
+ * Shows how to handle the `ComposeCompileResult` discriminated union
67
+ * returned by `sdk.client.compile()`.
68
+ *
69
+ * This function is illustrative — it cannot run without a live backend,
70
+ * but demonstrates the branching pattern callers should use.
71
+ */
72
+ export const handleCompileResult = async (): Promise<void> => {
73
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
74
+
75
+ const builder = sdk.flow(1, {
76
+ inputs: { amountIn: resources.erc20(WETH, 1) },
77
+ });
78
+
79
+ builder.lifi.swap('swap', {
80
+ bind: { amountIn: builder.inputs.amountIn },
81
+ config: { resourceOut: resources.erc20(USDC, 1), slippage: 0.03 },
82
+ });
83
+
84
+ const result = await builder.compile({
85
+ signer: OWNER,
86
+ inputs: {
87
+ amountIn: materialisers.directDeposit({ amount: '1000000000000000000' }),
88
+ },
89
+ simulationPolicy: 'allow-revert',
90
+ });
91
+
92
+ if (result.status === 'success') {
93
+ // Full success — transactionRequest includes gasLimit.
94
+ const { transactionRequest, userProxy, producedResources } = result;
95
+ void transactionRequest;
96
+ void userProxy;
97
+ void producedResources;
98
+ return;
99
+ }
100
+
101
+ // Partial result — simulation reverted but a transaction is still available.
102
+ // transactionRequest omits gasLimit; the caller must estimate gas themselves.
103
+ const { transactionRequest, simulationRevert, error } = result;
104
+ void transactionRequest;
105
+
106
+ // Inspect the revert reason.
107
+ void simulationRevert.code;
108
+ void simulationRevert.rawErrorBytes;
109
+ if (simulationRevert.decodeResult?.errorCandidates) {
110
+ for (const candidate of simulationRevert.decodeResult.errorCandidates) {
111
+ void candidate.decodedErrorSignature;
112
+ void candidate.decodedParams;
113
+ }
114
+ }
115
+
116
+ // The error envelope from the server.
117
+ void error.kind; // e.g. 'simulation_revert'
118
+ void error.message; // human-readable description
119
+ };
@@ -0,0 +1,73 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import { createComposeSdk, materialisers, resources } from '../index.js';
4
+ import type { Address } from '../types.js';
5
+
6
+ import { BASE_URL } from './config.js';
7
+
8
+ const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
9
+ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
10
+
11
+ export interface SwapWithBalanceCheckInput {
12
+ readonly owner: Address;
13
+ readonly amount: `${bigint}`;
14
+ }
15
+
16
+ /**
17
+ * Swap WETH to USDC via LI.FI, then assert the execution contract holds
18
+ * at least the swapped amount.
19
+ *
20
+ * Demonstrates:
21
+ * - Using builder.context.executionAddress to reference the VM contract
22
+ * - invariant.balanceAtLeast as a post-swap safety check
23
+ * - No hardcoded contract address — the context provides it at compile time
24
+ */
25
+ export const buildSwapWithBalanceCheck = ({
26
+ owner,
27
+ amount,
28
+ }: SwapWithBalanceCheckInput): {
29
+ flow: Flow;
30
+ request: ComposeCompileRequest;
31
+ } => {
32
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
33
+
34
+ // Declare the flow with a single WETH input on Ethereum mainnet.
35
+ const builder = sdk.flow(1, {
36
+ name: 'swap-with-balance-check',
37
+ inputs: {
38
+ amountIn: resources.erc20(WETH, 1),
39
+ },
40
+ });
41
+
42
+ // Swap WETH → USDC via LI.FI.
43
+ const swapOutputs = builder.lifi.swap('swap', {
44
+ bind: { amountIn: builder.inputs.amountIn },
45
+ config: {
46
+ resourceOut: resources.erc20(USDC, 1),
47
+ slippage: 0.03,
48
+ },
49
+ });
50
+
51
+ // Assert the VM execution contract holds at least the swapped USDC amount.
52
+ // builder.context.executionAddress resolves to the contract at compile time.
53
+ builder.invariant.balanceAtLeast('check-usdc', {
54
+ bind: {
55
+ minimumAmount: swapOutputs.amountOut,
56
+ account: builder.context.executionAddress,
57
+ },
58
+ });
59
+
60
+ const flow = builder.build();
61
+
62
+ // Build the compile request.
63
+ // directDeposit transfers a fixed amount of WETH into the VM.
64
+ const request = sdk.request(flow, {
65
+ signer: owner,
66
+ inputs: {
67
+ amountIn: materialisers.directDeposit({ amount }),
68
+ },
69
+ sweepTo: builder.context.sender,
70
+ });
71
+
72
+ return { flow, request };
73
+ };
@@ -0,0 +1,125 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import { createComposeSdk, materialisers, resources } from '../index.js';
4
+ import type { Address } from '../types.js';
5
+
6
+ import { BASE_URL } from './config.js';
7
+
8
+ const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
9
+ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
10
+
11
+ export interface SwapWithOutputValidationInput {
12
+ readonly owner: Address;
13
+ readonly amount: `${bigint}`;
14
+ readonly expectedOut: `${bigint}`;
15
+ }
16
+
17
+ /**
18
+ * Swap WETH to USDC, then validate the output amount against computed
19
+ * slippage bounds.
20
+ *
21
+ * Demonstrates:
22
+ * - core.bpsDown / core.bpsUp to compute lower and upper bounds
23
+ * - core.subtract / core.add for threshold arithmetic
24
+ * - All seven assertion ops for output validation
25
+ */
26
+ export const buildSwapWithOutputValidation = ({
27
+ owner,
28
+ amount,
29
+ expectedOut: _expectedOut,
30
+ }: SwapWithOutputValidationInput): {
31
+ flow: Flow;
32
+ request: ComposeCompileRequest;
33
+ } => {
34
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
35
+
36
+ const builder = sdk.flow(1, {
37
+ name: 'swap-with-output-validation',
38
+ inputs: {
39
+ amountIn: resources.erc20(WETH, 1),
40
+ },
41
+ });
42
+
43
+ // Swap WETH → USDC.
44
+ const swapOutputs = builder.lifi.swap('swap', {
45
+ bind: { amountIn: builder.inputs.amountIn },
46
+ config: {
47
+ resourceOut: resources.erc20(USDC, 1),
48
+ slippage: 0.03,
49
+ },
50
+ });
51
+
52
+ // Compute slippage bounds from the expected output.
53
+ // Lower bound: expectedOut * (10000 - 300) / 10000 (3% slippage down)
54
+ const lowerBound = builder.core.bpsDown('lower-bound', {
55
+ bind: { value: swapOutputs.amountOut },
56
+ config: { bps: 9700 },
57
+ });
58
+
59
+ // Upper bound: expectedOut * (10000 + 100) / 10000 (1% above expected)
60
+ const upperBound = builder.core.bpsUp('upper-bound', {
61
+ bind: { value: swapOutputs.amountOut },
62
+ config: { bps: 10100 },
63
+ });
64
+
65
+ // Derive a margin value: upper - lower
66
+ const margin = builder.core.subtract('margin', {
67
+ bind: { a: upperBound.result, b: lowerBound.result },
68
+ });
69
+
70
+ // Sanity check: lower + margin should reconstruct the gap
71
+ const reconstructed = builder.core.add('reconstructed', {
72
+ bind: { a: lowerBound.result, b: margin.result },
73
+ });
74
+
75
+ // Validate: output >= lower bound
76
+ builder.core.assertGte('check-gte-lower', {
77
+ bind: { a: swapOutputs.amountOut, b: lowerBound.result },
78
+ });
79
+
80
+ // Validate: output <= upper bound
81
+ builder.core.assertLte('check-lte-upper', {
82
+ bind: { a: swapOutputs.amountOut, b: upperBound.result },
83
+ });
84
+
85
+ // Validate: output falls within [lower, upper] range
86
+ builder.core.assertInRange('check-range', {
87
+ bind: {
88
+ value: swapOutputs.amountOut,
89
+ min: lowerBound.result,
90
+ max: upperBound.result,
91
+ },
92
+ });
93
+
94
+ // Validate: upper bound > lower bound
95
+ builder.core.assertGt('check-upper-gt-lower', {
96
+ bind: { a: upperBound.result, b: lowerBound.result },
97
+ });
98
+
99
+ // Validate: lower bound < upper bound (inverse check)
100
+ builder.core.assertLt('check-lower-lt-upper', {
101
+ bind: { a: lowerBound.result, b: upperBound.result },
102
+ });
103
+
104
+ // Validate: reconstructed gap equals upper - lower identity
105
+ builder.core.assertEqual('check-reconstruction', {
106
+ bind: { a: reconstructed.result, b: upperBound.result },
107
+ });
108
+
109
+ // Validate: lower and upper bounds are distinct
110
+ builder.core.assertNotEqual('check-bounds-distinct', {
111
+ bind: { a: lowerBound.result, b: upperBound.result },
112
+ });
113
+
114
+ const flow = builder.build();
115
+
116
+ const request = sdk.request(flow, {
117
+ signer: owner,
118
+ inputs: {
119
+ amountIn: materialisers.directDeposit({ amount }),
120
+ },
121
+ sweepTo: builder.context.sender,
122
+ });
123
+
124
+ return { flow, request };
125
+ };
@@ -0,0 +1,123 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import { createComposeSdk, materialisers, resources } from '../index.js';
4
+ import type { Address } from '../types.js';
5
+
6
+ import { BASE_URL } from './config.js';
7
+
8
+ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
9
+
10
+ export interface TransferTokensInput {
11
+ readonly owner: Address;
12
+ readonly recipient: Address;
13
+ readonly amount: `${bigint}`;
14
+ }
15
+
16
+ /**
17
+ * Transfer ERC-20 tokens from the proxy to an arbitrary recipient.
18
+ *
19
+ * Demonstrates:
20
+ * - `core.transfer` to move tokens to a recipient address
21
+ * - Binding a scalar handle input (`recipient`) alongside a resource input
22
+ * - Optionally specifying a partial transfer amount via `config.amount`
23
+ * (omit to transfer the full balance)
24
+ * - The `transferred` and `remainder` output ports
25
+ */
26
+ export const buildTransferTokens = ({
27
+ owner,
28
+ recipient,
29
+ amount,
30
+ }: TransferTokensInput): {
31
+ flow: Flow;
32
+ request: ComposeCompileRequest;
33
+ } => {
34
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
35
+
36
+ const builder = sdk.flow(1, {
37
+ name: 'transfer-usdc',
38
+ inputs: {
39
+ amountIn: resources.erc20(USDC, 1),
40
+ recipient: 'address',
41
+ },
42
+ });
43
+
44
+ // Transfer the full input amount to the recipient.
45
+ // The `transferred` port tracks what was sent; `remainder` tracks what's left.
46
+ builder.core.transfer('send', {
47
+ bind: {
48
+ amount: builder.inputs.amountIn,
49
+ recipient: builder.inputs.recipient,
50
+ },
51
+ config: {},
52
+ });
53
+
54
+ const flow = builder.build();
55
+
56
+ const request = sdk.request(flow, {
57
+ signer: owner,
58
+ inputs: {
59
+ amountIn: materialisers.directDeposit({ amount }),
60
+ recipient,
61
+ },
62
+ });
63
+
64
+ return { flow, request };
65
+ };
66
+
67
+ /**
68
+ * Transfer a partial amount, keeping the remainder in the proxy.
69
+ *
70
+ * Demonstrates:
71
+ * - `config.amount` to transfer a specific sub-amount
72
+ * - Using the `remainder` output port for subsequent operations
73
+ */
74
+ export const buildPartialTransfer = ({
75
+ owner,
76
+ recipient,
77
+ amount,
78
+ }: TransferTokensInput): {
79
+ flow: Flow;
80
+ request: ComposeCompileRequest;
81
+ } => {
82
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
83
+
84
+ const builder = sdk.flow(1, {
85
+ name: 'partial-transfer-usdc',
86
+ inputs: {
87
+ amountIn: resources.erc20(USDC, 1),
88
+ recipient: 'address',
89
+ },
90
+ });
91
+
92
+ // Transfer a fixed 0.5 USDC (500000 base units at 6 decimals);
93
+ // the remainder stays on the proxy for subsequent operations.
94
+ const out = builder.core.transfer('send-half', {
95
+ bind: {
96
+ amount: builder.inputs.amountIn,
97
+ recipient: builder.inputs.recipient,
98
+ },
99
+ config: { amount: '500000' },
100
+ });
101
+
102
+ // Use the remainder in a swap.
103
+ builder.lifi.swap('swap-remainder', {
104
+ bind: { amountIn: out.remainder },
105
+ config: {
106
+ resourceOut: resources.native(1),
107
+ slippage: 0.03,
108
+ },
109
+ });
110
+
111
+ const flow = builder.build();
112
+
113
+ const request = sdk.request(flow, {
114
+ signer: owner,
115
+ inputs: {
116
+ amountIn: materialisers.directDeposit({ amount }),
117
+ recipient,
118
+ },
119
+ sweepTo: builder.context.sender,
120
+ });
121
+
122
+ return { flow, request };
123
+ };
@@ -0,0 +1,77 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import { createComposeSdk, materialisers, raw, resources } from '../index.js';
4
+ import type { Address } from '../types.js';
5
+
6
+ import { BASE_URL } from './config.js';
7
+
8
+ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
9
+ // Example custom vault that the typed builder doesn't cover yet
10
+ const CUSTOM_VAULT = '0x1111111111111111111111111111111111111111';
11
+
12
+ export interface UntypedOpWithTypedRefInput {
13
+ readonly owner: Address;
14
+ }
15
+
16
+ /**
17
+ * Insert an untyped operation node for an unsupported operation, then feed
18
+ * its output into typed builder methods using `raw.ref<T>()`.
19
+ *
20
+ * Demonstrates:
21
+ * - `builder.untypedOp()` to add a node the typed API doesn't cover
22
+ * (e.g. a custom vault query).
23
+ * - `raw.ref<'uint256'>()` to reference the untyped node's output in a typed
24
+ * `Bindable<'uint256'>` slot — the explicit type parameter is required
25
+ * so the compiler verifies the ref is used in a compatible slot.
26
+ * - Mixing untyped and typed nodes in the same flow.
27
+ */
28
+ export const buildUntypedOpWithTypedRef = ({
29
+ owner,
30
+ }: UntypedOpWithTypedRefInput): {
31
+ flow: Flow;
32
+ request: ComposeCompileRequest;
33
+ } => {
34
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
35
+
36
+ const builder = sdk.flow(1, {
37
+ name: 'untyped-op-with-typed-ref',
38
+ inputs: {
39
+ amountIn: resources.erc20(USDC, 1),
40
+ },
41
+ });
42
+
43
+ // Insert an untyped node for a custom vault query. untypedOp returns void —
44
+ // the typed builder has no knowledge of this node's outputs.
45
+ builder.untypedOp('vault-query', 'custom.vaultQuery', {
46
+ bind: { amount: { $ref: 'input.amountIn' } },
47
+ config: { target: CUSTOM_VAULT },
48
+ });
49
+
50
+ // Use raw.ref to bridge the untyped node's output into a typed operation.
51
+ // The type parameter <'uint256'> tells the compiler this ref produces a
52
+ // uint256, so it's accepted by Bindable<'uint256'> slots.
53
+ const vaultResult = raw.ref<'uint256'>('vault-query.result');
54
+
55
+ // Feed the raw result into typed arithmetic — the compiler checks that
56
+ // vaultResult (typed as uint256) is compatible with the uint256 bind slot.
57
+ const scaled = builder.core.multiply('scale', {
58
+ bind: { a: vaultResult, b: vaultResult },
59
+ });
60
+
61
+ // Continue with fully typed operations downstream.
62
+ builder.core.assertGte('check-min', {
63
+ bind: { a: scaled.result, b: builder.inputs.amountIn },
64
+ });
65
+
66
+ const flow = builder.build();
67
+
68
+ const request = sdk.request(flow, {
69
+ signer: owner,
70
+ inputs: {
71
+ amountIn: materialisers.directDeposit({ amount: '1000000' }),
72
+ },
73
+ sweepTo: builder.context.sender,
74
+ });
75
+
76
+ return { flow, request };
77
+ };
@@ -0,0 +1,174 @@
1
+ // AUTO-GENERATED from manifest.json — do not edit by hand.
2
+ // Regenerate with: yarn workspace @lifi/composer-sdk generate
3
+
4
+ import type { Address, IntegerStringInput } from '../types.js';
5
+
6
+ export interface CoreSplitConfig {
7
+ bps: number;
8
+ }
9
+
10
+ /**
11
+ * a positive number
12
+ */
13
+ export type Positive = number;
14
+
15
+ export interface LifiSwapConfig {
16
+ resourceOut:
17
+ | {
18
+ kind: 'native';
19
+ chainId: Positive;
20
+ }
21
+ | {
22
+ kind: 'erc20';
23
+ token: Address;
24
+ chainId: Positive;
25
+ };
26
+ slippage?: number;
27
+ timing?: {
28
+ swapStepTimingStrategies?: {
29
+ strategy: 'minWaitTime';
30
+ minWaitTimeMs: number;
31
+ startingExpectedResults: number;
32
+ reduceEveryMs: number;
33
+ }[];
34
+ routeTimingStrategies?: {
35
+ strategy: 'minWaitTime';
36
+ minWaitTimeMs: number;
37
+ startingExpectedResults: number;
38
+ reduceEveryMs: number;
39
+ }[];
40
+ };
41
+ }
42
+
43
+ export interface LifiZapConfig {
44
+ resourceOut:
45
+ | {
46
+ kind: 'native';
47
+ chainId: Positive;
48
+ }
49
+ | {
50
+ kind: 'erc20';
51
+ token: Address;
52
+ chainId: Positive;
53
+ };
54
+ }
55
+
56
+ export interface CoreBpsDownConfig {
57
+ bps: number;
58
+ }
59
+
60
+ export interface CoreBpsUpConfig {
61
+ bps: number;
62
+ }
63
+
64
+ export interface CoreApproveConfig {
65
+ spender: Address;
66
+ }
67
+
68
+ export interface CoreBalanceOfConfig {
69
+ token: Address;
70
+ owner?: Address;
71
+ }
72
+
73
+ export interface CoreTransferConfig {
74
+ amount?: IntegerStringInput;
75
+ }
76
+
77
+ export interface CoreCallConfig {
78
+ target: Address;
79
+ functionSignature: string;
80
+ callType?: 'Call' | 'ValueCall';
81
+ args?: Unknown[];
82
+ approvals?: {
83
+ spender: Address;
84
+ }[];
85
+ }
86
+ export interface Unknown {
87
+ [k: string]: unknown;
88
+ }
89
+
90
+ export interface CoreStaticCallConfig {
91
+ target: Address;
92
+ functionSignature: string;
93
+ args?: Unknown[];
94
+ }
95
+
96
+ export interface CoreRawCallConfig {
97
+ target: Address;
98
+ calldata: string;
99
+ callType?: 'Call' | 'StaticCall' | 'ValueCall' | 'DelegateCall';
100
+ resultType?: string;
101
+ }
102
+
103
+ export interface CoreInvokeConfig {
104
+ target: Address;
105
+ functionSignature: string;
106
+ callType?: 'Call' | 'ValueCall';
107
+ args?: Unknown[];
108
+ }
109
+
110
+ export interface CoreAsResourceConfig {
111
+ resource:
112
+ | {
113
+ kind: 'native';
114
+ chainId: Positive;
115
+ }
116
+ | {
117
+ kind: 'erc20';
118
+ token: Address;
119
+ chainId: Positive;
120
+ };
121
+ }
122
+
123
+ export interface CorePeekConfig {
124
+ target: Address;
125
+ functionSignature: string;
126
+ args?: string[];
127
+ }
128
+
129
+ /**
130
+ * a number between 0 and 10000
131
+ */
132
+ export type Between010000 = number;
133
+
134
+ export interface SlippageGuardConfig {
135
+ port: string;
136
+ bps: Between010000;
137
+ }
138
+
139
+ export interface BalanceOfMaterialiserConfig {
140
+ owner: Address;
141
+ }
142
+
143
+ export type DirectDepositMaterialiserConfig =
144
+ | {
145
+ amount: IntegerStringInput;
146
+ allowNonExact?: false;
147
+ }
148
+ | {
149
+ amount?: IntegerStringInput;
150
+ allowNonExact: true;
151
+ };
152
+
153
+ export interface CallMaterialiserConfig {
154
+ target: Address;
155
+ calldata: string;
156
+ }
157
+
158
+ export interface Erc20BalancePreconditionConfig {
159
+ wallet: Address;
160
+ token: Address;
161
+ balance: IntegerStringInput;
162
+ }
163
+
164
+ export interface NativeBalancePreconditionConfig {
165
+ wallet: Address;
166
+ balance: IntegerStringInput;
167
+ }
168
+
169
+ export interface Erc20AllowancePreconditionConfig {
170
+ owner: Address;
171
+ spender: Address;
172
+ token: Address;
173
+ allowance: IntegerStringInput;
174
+ }
@@ -0,0 +1,14 @@
1
+ // AUTO-GENERATED from manifest.json — do not edit by hand.
2
+ // Regenerate with: yarn workspace @lifi/composer-sdk generate
3
+
4
+ import type { TypedGuard } from '../types.js';
5
+
6
+ import type { SlippageGuardConfig } from './config.generated.js';
7
+
8
+ /** Assert that output amounts are within a slippage tolerance in basis points */
9
+ export const slippage = <P extends string>(
10
+ config: Omit<SlippageGuardConfig, 'port'> & { port: P },
11
+ ): TypedGuard<P> => ({
12
+ kind: 'slippage',
13
+ ...config,
14
+ });