@lifi/composer-sdk 0.2.0 → 0.4.0-staging.1

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 (93) hide show
  1. package/README.md +44 -3
  2. package/dist/client.cjs +9 -3
  3. package/dist/client.cjs.map +1 -1
  4. package/dist/client.d.cts +2 -2
  5. package/dist/client.d.ts +2 -2
  6. package/dist/client.js +9 -3
  7. package/dist/client.js.map +1 -1
  8. package/dist/errors.cjs +26 -3
  9. package/dist/errors.cjs.map +1 -1
  10. package/dist/errors.d.cts +46 -2
  11. package/dist/errors.d.ts +46 -2
  12. package/dist/errors.js +24 -2
  13. package/dist/errors.js.map +1 -1
  14. package/dist/generated/config.generated.cjs.map +1 -1
  15. package/dist/generated/config.generated.d.cts +37 -14
  16. package/dist/generated/config.generated.d.ts +37 -14
  17. package/dist/generated/materialisers.generated.cjs +8 -2
  18. package/dist/generated/materialisers.generated.cjs.map +1 -1
  19. package/dist/generated/materialisers.generated.d.cts +1 -1
  20. package/dist/generated/materialisers.generated.d.ts +1 -1
  21. package/dist/generated/materialisers.generated.js +6 -1
  22. package/dist/generated/materialisers.generated.js.map +1 -1
  23. package/dist/generated/operations.generated.cjs +39 -1
  24. package/dist/generated/operations.generated.cjs.map +1 -1
  25. package/dist/generated/operations.generated.d.cts +111 -2
  26. package/dist/generated/operations.generated.d.ts +111 -2
  27. package/dist/generated/operations.generated.js +39 -1
  28. package/dist/generated/operations.generated.js.map +1 -1
  29. package/dist/index.cjs +2 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +4 -4
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.js +2 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/{materialisers.generated-BMkBQgWZ.d.ts → materialisers.generated-BfWJ-g7t.d.ts} +8 -3
  36. package/dist/{materialisers.generated-BWq5oePO.d.cts → materialisers.generated-DaJRwiRe.d.cts} +8 -3
  37. package/dist/responseSchemas.cjs +10 -1
  38. package/dist/responseSchemas.cjs.map +1 -1
  39. package/dist/responseSchemas.d.cts +15 -1
  40. package/dist/responseSchemas.d.ts +15 -1
  41. package/dist/responseSchemas.js +10 -1
  42. package/dist/responseSchemas.js.map +1 -1
  43. package/dist/sdk.cjs.map +1 -1
  44. package/dist/sdk.d.cts +5 -5
  45. package/dist/sdk.d.ts +5 -5
  46. package/dist/sdk.js.map +1 -1
  47. package/package.json +4 -5
  48. package/src/client.ts +10 -5
  49. package/src/errors.ts +67 -1
  50. package/src/examples/aaveClaimRewards.ts +2 -2
  51. package/src/examples/aaveRepay.ts +2 -2
  52. package/src/examples/aaveRepayWithATokens.ts +2 -2
  53. package/src/examples/aaveSetEMode.ts +2 -2
  54. package/src/examples/approveAndDeposit.ts +2 -2
  55. package/src/examples/callContract.ts +4 -4
  56. package/src/examples/config.ts +4 -1
  57. package/src/examples/consolidateToEth.ts +2 -2
  58. package/src/examples/consolidateToUsdc.ts +2 -2
  59. package/src/examples/depositFromProxy.ts +2 -2
  60. package/src/examples/dustSweep.ts +2 -2
  61. package/src/examples/emitCustomEvent.ts +2 -2
  62. package/src/examples/invariantChecks.ts +2 -2
  63. package/src/examples/lifiSwap.ts +2 -2
  64. package/src/examples/lifiZap.ts +2 -2
  65. package/src/examples/rawCallWithArithmetic.ts +2 -2
  66. package/src/examples/readContractState.ts +2 -2
  67. package/src/examples/splitAndZap.ts +2 -2
  68. package/src/examples/splitWithArithmetic.ts +2 -2
  69. package/src/examples/staged/aaveBorrow.ts +128 -0
  70. package/src/examples/staged/aaveDebtRebalanceExactOut.ts +301 -0
  71. package/src/examples/staged/aaveHealthFactorExplode.ts +70 -0
  72. package/src/examples/staged/aaveToMorphoDebtMigration.ts +200 -0
  73. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwap.ts +231 -0
  74. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwapExactOut.ts +307 -0
  75. package/src/examples/staged/flashloanRepay.ts +142 -0
  76. package/src/examples/staged/manualRelayContinuation.ts +161 -0
  77. package/src/examples/staged/morphoBlueBorrow.ts +93 -0
  78. package/src/examples/staged/morphoBlueRepay.ts +132 -0
  79. package/src/examples/staged/registry.ts +89 -0
  80. package/src/examples/swapAndZap.ts +2 -2
  81. package/src/examples/swapToRecipient.ts +2 -2
  82. package/src/examples/swapWithAllowRevert.ts +3 -3
  83. package/src/examples/swapWithBalanceCheck.ts +2 -2
  84. package/src/examples/swapWithFee.ts +2 -2
  85. package/src/examples/swapWithOutputValidation.ts +2 -2
  86. package/src/examples/transferTokens.ts +3 -3
  87. package/src/examples/untypedOpWithTypedRef.ts +2 -2
  88. package/src/generated/config.generated.ts +42 -14
  89. package/src/generated/materialisers.generated.ts +12 -0
  90. package/src/generated/operations.generated.ts +214 -0
  91. package/src/index.ts +13 -2
  92. package/src/responseSchemas.ts +29 -0
  93. package/src/sdk.ts +5 -5
package/src/errors.ts CHANGED
@@ -1,4 +1,9 @@
1
- import type { ComposeErrorKind, SimulationRevert } from '@lifi/compose-spec';
1
+ import type {
2
+ ComposeErrorKind,
3
+ FailedPreparedOp,
4
+ GenericComposeErrorKind,
5
+ SimulationRevert,
6
+ } from '@lifi/compose-spec';
2
7
 
3
8
  import {
4
9
  parseServerErrorBody,
@@ -62,6 +67,17 @@ export class ComposeError extends Error {
62
67
  * backend can parse the revert reason.
63
68
  */
64
69
  readonly details?: SimulationRevert;
70
+ /**
71
+ * The prepared ops that failed, attached to `preparation_error` errors.
72
+ * Each entry carries the `callId` of the failing node so callers can drop
73
+ * the unroutable legs and resubmit a smaller flow.
74
+ */
75
+ readonly failedOps?: readonly FailedPreparedOp[];
76
+ /**
77
+ * The `callId`s of the prepared ops that succeeded, attached to
78
+ * `preparation_error` errors alongside {@link ComposeError.failedOps}.
79
+ */
80
+ readonly succeededOps?: readonly string[];
65
81
 
66
82
  constructor(
67
83
  code: ComposeErrorCode,
@@ -73,6 +89,8 @@ export class ComposeError extends Error {
73
89
  kind?: ComposeErrorKind;
74
90
  path?: string;
75
91
  details?: SimulationRevert;
92
+ failedOps?: readonly FailedPreparedOp[];
93
+ succeededOps?: readonly string[];
76
94
  },
77
95
  ) {
78
96
  super(message, { cause: options?.cause });
@@ -82,9 +100,21 @@ export class ComposeError extends Error {
82
100
  this.kind = options?.kind;
83
101
  this.path = options?.path;
84
102
  this.details = options?.details;
103
+ this.failedOps = options?.failedOps;
104
+ this.succeededOps = options?.succeededOps;
85
105
  }
86
106
  }
87
107
 
108
+ /**
109
+ * The per-op preparation diagnostics carried by `preparation_error` responses,
110
+ * with both arrays known to be present. Produced by narrowing with
111
+ * {@link isComposePreparationError}.
112
+ */
113
+ export interface ComposePreparationOps {
114
+ readonly failedOps: readonly FailedPreparedOp[];
115
+ readonly succeededOps: readonly string[];
116
+ }
117
+
88
118
  /**
89
119
  * Type guard that narrows an unknown error to {@link ComposeError}.
90
120
  * @param e - The value to check.
@@ -94,6 +124,35 @@ export const isComposeError = (e: unknown): e is ComposeError =>
94
124
  e instanceof ComposeError ||
95
125
  (e instanceof Error && e.name === 'ComposeError' && 'code' in e);
96
126
 
127
+ /**
128
+ * Type guard for `preparation_error` failures (HTTP 422): a mixed basket where
129
+ * some prepared ops failed and others succeeded. Narrows both
130
+ * {@link ComposeError.failedOps} and {@link ComposeError.succeededOps} to
131
+ * present, so callers can read them without a cast or a truthiness check.
132
+ *
133
+ * @param e - The value to check.
134
+ * @returns `true` if `e` is a `ComposeError` carrying per-op preparation diagnostics.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * try {
139
+ * await builder.compile(run);
140
+ * } catch (e) {
141
+ * if (isComposePreparationError(e)) {
142
+ * console.error('Failed ops:', e.failedOps);
143
+ * console.error('Succeeded ops:', e.succeededOps);
144
+ * }
145
+ * }
146
+ * ```
147
+ */
148
+ export const isComposePreparationError = (
149
+ e: unknown,
150
+ ): e is ComposeError & ComposePreparationOps =>
151
+ isComposeError(e) &&
152
+ e.kind === 'preparation_error' &&
153
+ Array.isArray(e.failedOps) &&
154
+ Array.isArray(e.succeededOps);
155
+
97
156
  const STATUS_TO_CODE: ReadonlyMap<number, ComposeErrorCode> = new Map<
98
157
  number,
99
158
  ComposeErrorCode
@@ -143,6 +202,13 @@ export const errorFromHttpResponse = (
143
202
  kind: serverError?.kind as ComposeErrorKind | undefined,
144
203
  path: serverError?.path,
145
204
  details: serverError?.details,
205
+ // The wire schema types `kind` as a bare string; the server only ever
206
+ // reports generic kinds for individual ops.
207
+ failedOps: serverError?.failedOps?.map((op) => ({
208
+ ...op,
209
+ kind: op.kind as GenericComposeErrorKind,
210
+ })),
211
+ succeededOps: serverError?.succeededOps,
146
212
  },
147
213
  );
148
214
  };
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Aave v3 incentives contracts on Ethereum mainnet.
9
9
  const REWARDS_CONTROLLER = '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb';
@@ -37,7 +37,7 @@ export const buildAaveClaimRewards = ({
37
37
  flow: Flow;
38
38
  request: ComposeCompileRequest;
39
39
  } => {
40
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
40
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
41
41
 
42
42
  const builder = sdk.flow(1, {
43
43
  name: 'aave-claim-rewards',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Aave v3 contracts on Ethereum mainnet.
9
9
  const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
@@ -49,7 +49,7 @@ export const buildAaveRepay = ({
49
49
  flow: Flow;
50
50
  request: ComposeCompileRequest;
51
51
  } => {
52
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
52
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
53
53
 
54
54
  const builder = sdk.flow(1, {
55
55
  name: 'aave-repay-weth',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Aave v3 contracts on Ethereum mainnet.
9
9
  const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
@@ -39,7 +39,7 @@ export const buildAaveRepayWithATokens = ({
39
39
  flow: Flow;
40
40
  request: ComposeCompileRequest;
41
41
  } => {
42
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
42
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
43
43
 
44
44
  const builder = sdk.flow(1, {
45
45
  name: 'aave-repay-with-atokens',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Aave v3 contracts on Ethereum mainnet.
9
9
  const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
@@ -41,7 +41,7 @@ export const buildAaveSetEMode = ({
41
41
  flow: Flow;
42
42
  request: ComposeCompileRequest;
43
43
  } => {
44
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
44
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
45
45
 
46
46
  const builder = sdk.flow(1, {
47
47
  name: 'aave-set-emode',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
9
9
  // Example ERC-4626 vault address (Steakhouse USDC on Ethereum mainnet)
@@ -30,7 +30,7 @@ export const buildApproveAndDeposit = ({
30
30
  flow: Flow;
31
31
  request: ComposeCompileRequest;
32
32
  } => {
33
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
33
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
34
34
 
35
35
  const builder = sdk.flow(1, {
36
36
  name: 'approve-and-deposit',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Example ERC-4626 vault address (Steakhouse USDC on Ethereum mainnet)
9
9
  const VAULT = '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB';
@@ -31,7 +31,7 @@ export const buildRedeemFromVault = ({
31
31
  flow: Flow;
32
32
  request: ComposeCompileRequest;
33
33
  } => {
34
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
34
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
35
35
 
36
36
  const builder = sdk.flow(1, {
37
37
  name: 'redeem-from-vault',
@@ -103,7 +103,7 @@ export const buildClaimRewards = ({
103
103
  const REWARDS_CONTRACT = '0x1111111111111111111111111111111111111111';
104
104
  const REWARD_TOKEN = '0x2222222222222222222222222222222222222222';
105
105
 
106
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
106
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
107
107
 
108
108
  const builder = sdk.flow(1, {
109
109
  name: 'claim-rewards',
@@ -154,7 +154,7 @@ export const buildWrapEth = ({
154
154
  flow: Flow;
155
155
  request: ComposeCompileRequest;
156
156
  } => {
157
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
157
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
158
158
 
159
159
  const builder = sdk.flow(1, {
160
160
  name: 'wrap-eth',
@@ -2,7 +2,10 @@
2
2
  // built-in process.env.BASE_URL (set to "/" from the `base` config option).
3
3
  export const BASE_URL: string =
4
4
  process.env['COMPOSER_BASE_URL'] ?? 'https://composer.li.quest';
5
- export const API_KEY: string | undefined = process.env['LIFI_API_KEY'];
5
+ // `apiKey` is required by `createComposeSdk`/`createComposeClient`; fall back to a
6
+ // placeholder so the examples remain runnable (against a backend that accepts it)
7
+ // without every contributor needing a real LI.FI API key set locally.
8
+ export const API_KEY: string = process.env['LIFI_API_KEY'] ?? 'YOUR_API_KEY';
6
9
 
7
10
  export const OWNER = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
8
11
  export const RECIPIENT = '0x1234567890abcdef1234567890abcdef12345678';
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  export interface ConsolidateToEthInput {
9
9
  readonly owner: Address;
@@ -38,7 +38,7 @@ export const buildConsolidateToEth = ({
38
38
  flow: Flow;
39
39
  request: ComposeCompileRequest;
40
40
  } => {
41
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
41
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
42
42
  const erc20 = (token: Address) => resources.erc20(token, CHAIN_ID);
43
43
 
44
44
  // Declare the flow with one input per ERC-20 token on Ethereum mainnet.
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  export interface ConsolidateStablesToUsdcInput {
9
9
  readonly owner: Address;
@@ -39,7 +39,7 @@ export const buildConsolidateStablesToUsdc = ({
39
39
  flow: Flow;
40
40
  request: ComposeCompileRequest;
41
41
  } => {
42
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
42
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
43
43
  const erc20 = (token: Address) => resources.erc20(token, CHAIN_ID);
44
44
 
45
45
  // Declare the flow with one input per stablecoin on Ethereum mainnet.
@@ -9,7 +9,7 @@ import {
9
9
  } from '../index.js';
10
10
  import type { Address } from '../types.js';
11
11
 
12
- import { BASE_URL } from './config.js';
12
+ import { API_KEY, BASE_URL } from './config.js';
13
13
 
14
14
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
15
15
  // Aave v3 aEthUSDC receipt token on Ethereum mainnet
@@ -45,7 +45,7 @@ export const buildDepositFromProxy = ({
45
45
  flow: Flow;
46
46
  request: ComposeCompileRequest;
47
47
  } => {
48
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
48
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
49
49
 
50
50
  const builder = sdk.flow(1, {
51
51
  name: 'deposit-from-proxy',
@@ -2,7 +2,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
2
 
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
 
5
- import { BASE_URL, OWNER } from './config.js';
5
+ import { API_KEY, BASE_URL, OWNER } from './config.js';
6
6
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
7
7
  const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7';
8
8
 
@@ -23,7 +23,7 @@ export const buildDustSweepExample = (): {
23
23
  flow: Flow;
24
24
  request: ComposeCompileRequest;
25
25
  } => {
26
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
26
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
27
27
 
28
28
  const builder = sdk.flow(1, {
29
29
  name: 'dust-sweep',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Example ERC-4626 vault address (Steakhouse USDC on Ethereum mainnet)
9
9
  const VAULT = '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB';
@@ -33,7 +33,7 @@ export const buildEmitCustomEvent = ({
33
33
  flow: Flow;
34
34
  request: ComposeCompileRequest;
35
35
  } => {
36
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
36
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
37
37
 
38
38
  const builder = sdk.flow(1, {
39
39
  name: 'emit-custom-event',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
9
9
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
@@ -41,7 +41,7 @@ export const buildInvariantChecks = ({
41
41
  flow: Flow;
42
42
  request: ComposeCompileRequest;
43
43
  } => {
44
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
44
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
45
45
 
46
46
  // One WETH resource input plus a scalar spender address.
47
47
  const builder = sdk.flow(1, {
@@ -2,7 +2,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
2
 
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
 
5
- import { BASE_URL, OWNER } from './config.js';
5
+ import { API_KEY, BASE_URL, OWNER } from './config.js';
6
6
 
7
7
  const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
8
8
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
@@ -19,7 +19,7 @@ export const buildLifiSwapExample = (): {
19
19
  flow: Flow;
20
20
  request: ComposeCompileRequest;
21
21
  } => {
22
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
22
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
23
23
 
24
24
  // Declare the flow with a single WETH input on Ethereum mainnet.
25
25
  const builder = sdk.flow(1, {
@@ -7,7 +7,7 @@ import {
7
7
  resources,
8
8
  } from '../index.js';
9
9
 
10
- import { BASE_URL, OWNER } from './config.js';
10
+ import { API_KEY, BASE_URL, OWNER } from './config.js';
11
11
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
12
12
  // Aave aEthUSDC receipt token on Ethereum mainnet
13
13
  const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c';
@@ -24,7 +24,7 @@ export const buildLifiZapExample = (): {
24
24
  flow: Flow;
25
25
  request: ComposeCompileRequest;
26
26
  } => {
27
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
27
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
28
28
 
29
29
  // Declare the flow with a single USDC input on Ethereum mainnet.
30
30
  const builder = sdk.flow(1, {
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  // Example contract address for a non-standard oracle
9
9
  const ORACLE = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419';
@@ -27,7 +27,7 @@ export const buildRawCallWithArithmetic = ({
27
27
  flow: Flow;
28
28
  request: ComposeCompileRequest;
29
29
  } => {
30
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
30
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
31
31
 
32
32
  const builder = sdk.flow(1, {
33
33
  name: 'raw-call-with-arithmetic',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
9
9
  // Example ERC-4626 vault address (Steakhouse USDC on Ethereum mainnet)
@@ -28,7 +28,7 @@ export const buildReadContractState = ({
28
28
  flow: Flow;
29
29
  request: ComposeCompileRequest;
30
30
  } => {
31
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
31
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
32
32
 
33
33
  const builder = sdk.flow(1, {
34
34
  name: 'read-contract-state',
@@ -7,7 +7,7 @@ import {
7
7
  resources,
8
8
  } from '../index.js';
9
9
 
10
- import { BASE_URL, OWNER } from './config.js';
10
+ import { API_KEY, BASE_URL, OWNER } from './config.js';
11
11
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
12
12
  // Aave v3 aEthUSDC receipt token on Ethereum mainnet
13
13
  const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c';
@@ -26,7 +26,7 @@ export const buildSplitAndZapExample = (): {
26
26
  flow: Flow;
27
27
  request: ComposeCompileRequest;
28
28
  } => {
29
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
29
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
30
30
 
31
31
  const builder = sdk.flow(1, {
32
32
  name: 'split-usdc-to-two-vaults',
@@ -3,7 +3,7 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
3
3
  import { createComposeSdk, materialisers, resources } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
- import { BASE_URL } from './config.js';
6
+ import { API_KEY, BASE_URL } from './config.js';
7
7
 
8
8
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
9
9
 
@@ -29,7 +29,7 @@ export const buildSplitWithArithmetic = ({
29
29
  flow: Flow;
30
30
  request: ComposeCompileRequest;
31
31
  } => {
32
- const sdk = createComposeSdk({ baseUrl: BASE_URL });
32
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
33
33
 
34
34
  const builder = sdk.flow(1, {
35
35
  name: 'split-with-arithmetic',
@@ -0,0 +1,128 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import {
4
+ createComposeSdk,
5
+ guards,
6
+ materialisers,
7
+ resources,
8
+ } from '../../index.js';
9
+ import type { Address } from '../../types.js';
10
+ import { API_KEY, BASE_URL } from '../config.js';
11
+
12
+ // Aave v3 contracts on Ethereum mainnet.
13
+ const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
14
+ const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
15
+ // Aave v3 aEthUSDC receipt token and variable-debt USDC token.
16
+ const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c';
17
+ const VDEBT_USDC = '0x72E95b8931767C79bA4EeE721354d6E99a61D004';
18
+
19
+ // 1.05 WAD (1e18) — minimum acceptable Aave health factor after the borrow.
20
+ // The on-chain VM reverts the run if the post-borrow HF drops below this.
21
+ const MIN_HEALTH_FACTOR_WAD = '1050000000000000000';
22
+
23
+ export interface AaveBorrowInput {
24
+ readonly owner: Address;
25
+ readonly recipient: Address;
26
+ /** USDC supplied as collateral so the proxy can borrow (6 dp). */
27
+ readonly collateralAmount: `${bigint}`;
28
+ /** USDC borrowed against the collateral and sent to the recipient (6 dp). */
29
+ readonly borrowAmount: `${bigint}`;
30
+ }
31
+
32
+ /**
33
+ * Supply USDC to Aave v3, borrow USDC against it, and transfer the borrowed
34
+ * funds to a recipient — with an on-chain health-factor guard that reverts
35
+ * if the post-borrow position is too thin.
36
+ *
37
+ * The in-flow supply makes the example self-contained: the borrow is backed
38
+ * by collateral created in the same transaction, so it simulates cleanly
39
+ * for a fresh proxy. Keep `borrowAmount` comfortably below the collateral's
40
+ * borrowing power or the health-factor guard reverts the run.
41
+ *
42
+ * Demonstrates:
43
+ * - `lifi.zap` to supply USDC into Aave (USDC → aEthUSDC routing edge)
44
+ * - `aave.borrow` with no resource input — the borrowed asset materialises
45
+ * as the `borrowed` resource output.
46
+ * - Chaining the borrow output into a `core.transfer` to forward funds.
47
+ * - `aave.getHealthFactor` + `core.numericInvariant` guard asserts that
48
+ * `healthFactor >= MIN_HEALTH_FACTOR_WAD` after the borrow; the run reverts
49
+ * on-chain if the threshold is violated.
50
+ *
51
+ * The supplied aEthUSDC stays on the proxy: it backs the open debt, and
52
+ * Aave rejects aToken transfers that would break the position's health
53
+ * factor, so it cannot be swept while the borrow is outstanding.
54
+ */
55
+ export const buildAaveBorrow = ({
56
+ owner,
57
+ recipient,
58
+ collateralAmount,
59
+ borrowAmount,
60
+ }: AaveBorrowInput): {
61
+ flow: Flow;
62
+ request: ComposeCompileRequest;
63
+ } => {
64
+ const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
65
+
66
+ const builder = sdk.flow(1, {
67
+ name: 'aave-borrow-usdc',
68
+ inputs: {
69
+ collateralIn: resources.erc20(USDC, 1),
70
+ recipient: 'address',
71
+ },
72
+ });
73
+
74
+ // Supply USDC as collateral (USDC → aEthUSDC) so the proxy can borrow.
75
+ builder.lifi.zap('supply', {
76
+ bind: { amountIn: builder.inputs.collateralIn },
77
+ config: { resourceOut: resources.erc20(A_ETH_USDC, 1) },
78
+ });
79
+
80
+ const borrowOut = builder.aave.borrow('borrow', {
81
+ bind: {},
82
+ config: {
83
+ pool: AAVE_V3_POOL,
84
+ asset: USDC,
85
+ variableDebtToken: VDEBT_USDC,
86
+ amount: borrowAmount,
87
+ },
88
+ });
89
+
90
+ // Read the post-borrow Aave health factor and assert it stays above the
91
+ // minimum threshold. The numericInvariant guard lowers to an on-chain
92
+ // `AssertRawInvariant` IR1 instruction; the run reverts if HF < threshold.
93
+ builder.aave.getHealthFactor('hf', {
94
+ bind: {},
95
+ config: { pool: AAVE_V3_POOL },
96
+ guards: [
97
+ guards.coreNumericInvariant({
98
+ port: 'healthFactor',
99
+ op: 'gte',
100
+ threshold: MIN_HEALTH_FACTOR_WAD,
101
+ }),
102
+ ],
103
+ });
104
+
105
+ // Forward the freshly borrowed USDC to the recipient.
106
+ builder.core.transfer('send-borrowed', {
107
+ bind: {
108
+ amount: borrowOut.borrowed,
109
+ recipient: builder.inputs.recipient,
110
+ },
111
+ config: {},
112
+ });
113
+
114
+ const flow = builder.build();
115
+
116
+ // No sweepTo: the aEthUSDC collateral must stay on the proxy while the
117
+ // debt is open — Aave blocks aToken transfers that would leave the
118
+ // position undercollateralized.
119
+ const request = sdk.request(flow, {
120
+ signer: owner,
121
+ inputs: {
122
+ collateralIn: materialisers.directDeposit({ amount: collateralAmount }),
123
+ recipient,
124
+ },
125
+ });
126
+
127
+ return { flow, request };
128
+ };