@lifi/composer-sdk 0.0.1-alpha → 0.2.0-staging.4

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 (76) hide show
  1. package/README.md +37 -1
  2. package/dist/authoring/signatureArgs.cjs +2 -2
  3. package/dist/authoring/signatureArgs.cjs.map +1 -1
  4. package/dist/authoring/signatureArgs.js +2 -2
  5. package/dist/authoring/signatureArgs.js.map +1 -1
  6. package/dist/client.cjs +2 -2
  7. package/dist/client.cjs.map +1 -1
  8. package/dist/client.js +2 -2
  9. package/dist/client.js.map +1 -1
  10. package/dist/generated/config.generated.cjs.map +1 -1
  11. package/dist/generated/config.generated.d.cts +307 -16
  12. package/dist/generated/config.generated.d.ts +307 -16
  13. package/dist/generated/guards.generated.cjs +6 -0
  14. package/dist/generated/guards.generated.cjs.map +1 -1
  15. package/dist/generated/guards.generated.d.cts +1 -1
  16. package/dist/generated/guards.generated.d.ts +1 -1
  17. package/dist/generated/guards.generated.js +5 -0
  18. package/dist/generated/guards.generated.js.map +1 -1
  19. package/dist/generated/materialisers.generated.cjs +8 -2
  20. package/dist/generated/materialisers.generated.cjs.map +1 -1
  21. package/dist/generated/materialisers.generated.d.cts +1 -1
  22. package/dist/generated/materialisers.generated.d.ts +1 -1
  23. package/dist/generated/materialisers.generated.js +6 -1
  24. package/dist/generated/materialisers.generated.js.map +1 -1
  25. package/dist/generated/operations.generated.cjs +84 -14
  26. package/dist/generated/operations.generated.cjs.map +1 -1
  27. package/dist/generated/operations.generated.d.cts +208 -13
  28. package/dist/generated/operations.generated.d.ts +208 -13
  29. package/dist/generated/operations.generated.js +84 -14
  30. package/dist/generated/operations.generated.js.map +1 -1
  31. package/dist/guards.generated-BPLFwe7y.d.ts +19 -0
  32. package/dist/guards.generated-M3VAIevI.d.cts +19 -0
  33. package/dist/index.d.cts +3 -3
  34. package/dist/index.d.ts +3 -3
  35. package/dist/materialisers.generated-BfWJ-g7t.d.ts +29 -0
  36. package/dist/materialisers.generated-DaJRwiRe.d.cts +29 -0
  37. package/dist/run/inputs.cjs +8 -5
  38. package/dist/run/inputs.cjs.map +1 -1
  39. package/dist/run/inputs.d.cts +8 -0
  40. package/dist/run/inputs.d.ts +8 -0
  41. package/dist/run/inputs.js +8 -5
  42. package/dist/run/inputs.js.map +1 -1
  43. package/dist/sdk.cjs +1 -0
  44. package/dist/sdk.cjs.map +1 -1
  45. package/dist/sdk.js +1 -0
  46. package/dist/sdk.js.map +1 -1
  47. package/package.json +14 -6
  48. package/src/authoring/signatureArgs.ts +2 -2
  49. package/src/client.ts +1 -1
  50. package/src/examples/aaveClaimRewards.ts +80 -0
  51. package/src/examples/aaveRepay.ts +69 -0
  52. package/src/examples/aaveRepayWithATokens.ts +81 -0
  53. package/src/examples/aaveSetEMode.ts +68 -0
  54. package/src/examples/run.ts +41 -4
  55. package/src/examples/staged/aaveBorrow.ts +104 -0
  56. package/src/examples/staged/aaveDebtRebalanceExactOut.ts +301 -0
  57. package/src/examples/staged/aaveHealthFactorExplode.ts +70 -0
  58. package/src/examples/staged/aaveToMorphoDebtMigration.ts +190 -0
  59. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwap.ts +221 -0
  60. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwapExactOut.ts +302 -0
  61. package/src/examples/staged/flashloanRepay.ts +138 -0
  62. package/src/examples/staged/morphoBlueBorrow.ts +89 -0
  63. package/src/examples/staged/morphoBlueRepay.ts +91 -0
  64. package/src/examples/staged/registry.ts +78 -0
  65. package/src/examples/swapToRecipient.ts +0 -19
  66. package/src/examples/swapWithFee.ts +59 -0
  67. package/src/generated/config.generated.ts +364 -18
  68. package/src/generated/guards.generated.ts +12 -1
  69. package/src/generated/materialisers.generated.ts +24 -12
  70. package/src/generated/operations.generated.ts +373 -13
  71. package/src/run/inputs.ts +16 -5
  72. package/src/sdk.ts +1 -0
  73. package/dist/guards.generated-DKKDKREA.d.ts +0 -14
  74. package/dist/guards.generated-hrQ1KPNv.d.cts +0 -14
  75. package/dist/materialisers.generated-BsA1uDW8.d.ts +0 -24
  76. package/dist/materialisers.generated-nMxTAaVi.d.cts +0 -24
@@ -0,0 +1,70 @@
1
+ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
+
3
+ import { createComposeSdk, guards } from '../../index.js';
4
+ import type { Address } from '../../types.js';
5
+ import { BASE_URL } from '../config.js';
6
+
7
+ // Base mainnet Aave v3 Pool.
8
+ const AAVE_V3_POOL = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5';
9
+
10
+ // 1.0 WAD (1e18) — minimum acceptable health factor.
11
+ const MIN_HEALTH_FACTOR_WAD = '1000000000000000000';
12
+
13
+ export interface AaveHealthFactorExplodeInput {
14
+ readonly signer: Address;
15
+ }
16
+
17
+ /**
18
+ * EXPLODE / DecomposeValue showcase.
19
+ *
20
+ * NOTE: currently disabled end-to-end. `aave.getHealthFactor` is in the
21
+ * default `DISABLED_COMPOSE_OPS` set on zap-backend because the EXPLODE
22
+ * opcode is not yet supported on-chain. The example still builds a valid
23
+ * Flow JSON (and its spec passes), but submitting the request to a backend
24
+ * with default config will fail at op resolution. Re-enable by unsetting
25
+ * `aave.getHealthFactor` in the backend's `DISABLED_COMPOSE_OPS` once
26
+ * contracts ship EXPLODE.
27
+ *
28
+ * Builds a minimal compose flow that:
29
+ * - StaticCalls `IPool.getUserAccountData(user)` on Aave v3 (Base)
30
+ * - Decomposes the returned 6-tuple via the EXPLODE opcode
31
+ * - Asserts `healthFactor >= 1.0 WAD` via `core.numericInvariant`
32
+ *
33
+ * The signer must hold an Aave v3 position on the target chain so HF > 0.
34
+ * Uses `allow-revert` so the compiled calldata is returned even if the
35
+ * assertion would fail — handy for tracing on a fork.
36
+ */
37
+ export const buildAaveHealthFactorExplode = ({
38
+ signer,
39
+ }: AaveHealthFactorExplodeInput): {
40
+ flow: Flow;
41
+ request: ComposeCompileRequest;
42
+ } => {
43
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
44
+
45
+ const builder = sdk.flow(8453 /* Base */, {
46
+ name: 'aave-hf-explode-showcase',
47
+ inputs: {},
48
+ });
49
+
50
+ builder.aave.getHealthFactor('checkHf', {
51
+ bind: {},
52
+ config: { pool: AAVE_V3_POOL, user: signer },
53
+ guards: [
54
+ guards.coreNumericInvariant({
55
+ port: 'healthFactor',
56
+ op: 'gte',
57
+ threshold: MIN_HEALTH_FACTOR_WAD,
58
+ }),
59
+ ],
60
+ });
61
+
62
+ const flow = builder.build();
63
+ const request = sdk.request(flow, {
64
+ signer,
65
+ inputs: {},
66
+ simulationPolicy: 'allow-revert',
67
+ });
68
+
69
+ return { flow, request };
70
+ };
@@ -0,0 +1,190 @@
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
+ import { BASE_URL } from '../config.js';
6
+
7
+ // Base mainnet token addresses.
8
+ const BASE_WETH = '0x4200000000000000000000000000000000000006';
9
+ const BASE_USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
10
+
11
+ // Aave V3 on Base (pool + aWETH receipt + variable-debt USDC).
12
+ const AAVE_V3_POOL_BASE = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5';
13
+ const A_BAS_WETH = '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7';
14
+ const VDEBT_BAS_USDC = '0x59dca05b6c26dbd64b5381374aAaC5CD05644C28';
15
+
16
+ // Morpho Blue WETH/USDC market on Base (same parameters as the
17
+ // `morphoBlueBorrow` / `morphoBlueRepay` examples).
18
+ const ADAPTIVE_CURVE_IRM_BASE = '0x46415998764C29aB2a25CbeA6254146D50D22687';
19
+ const WETH_USDC_ORACLE = '0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4';
20
+ const WETH_USDC_LLTV = '860000000000000000';
21
+
22
+ const MORPHO_WETH_USDC_MARKET = {
23
+ loanToken: BASE_USDC,
24
+ collateralToken: BASE_WETH,
25
+ oracle: WETH_USDC_ORACLE,
26
+ irm: ADAPTIVE_CURVE_IRM_BASE,
27
+ lltv: WETH_USDC_LLTV,
28
+ };
29
+
30
+ export interface AaveToMorphoDebtMigrationInput {
31
+ readonly owner: Address;
32
+ /** WETH collateral the user posts (in smallest units). */
33
+ readonly collateralAmount: `${bigint}`;
34
+ /** USDC principal borrowed from Aave (in smallest units). */
35
+ readonly debtAmount: `${bigint}`;
36
+ /**
37
+ * Aave-V3 flashloan fee on `debtAmount` — equals
38
+ * `computeDefaultFee('aave-v3', debtAmount)` (5 bps of the principal).
39
+ */
40
+ readonly debtFlashloanFee: `${bigint}`;
41
+ }
42
+
43
+ /**
44
+ * Atomically provision an Aave V3 borrow position on Base and migrate it to
45
+ * a Morpho Blue WETH/USDC market — all in a single compose flow driven by
46
+ * two concurrent flashloans.
47
+ *
48
+ * Phase 1 — provision the source position on Aave V3:
49
+ * - User direct-deposits WETH.
50
+ * - `lifi.zap` supplies the WETH to Aave (WETH → aBasWETH routing edge).
51
+ * - `aave.borrow` mints USDC debt; the borrowed USDC stays on the proxy
52
+ * and is swept back to the signer as the user's loan proceeds.
53
+ *
54
+ * Phase 2 — migrate the position to Morpho using two concurrent flashloans:
55
+ * - Flashloan #1 (Aave V3, USDC) bridges the Aave debt payoff before the
56
+ * Morpho borrow exists.
57
+ * - Flashloan #2 (Balancer V2, WETH) bridges the Morpho collateral supply
58
+ * before the Aave collateral is freed.
59
+ * - `aave.repay` clears the Aave USDC debt with flashloan #1.
60
+ * - `lifi.zap` withdraws the freed WETH from Aave (aBasWETH → WETH).
61
+ * - `morphoBlue.supplyCollateral` deposits flashloan #2's WETH into Morpho.
62
+ * - `morphoBlue.borrow` mints exactly `debtAmount + debtFlashloanFee` USDC
63
+ * so the `borrowed` resource settles flashloan #1 with zero residual.
64
+ * - Two `lifi.flashloanRepay` ops settle each leg: the USDC leg from the
65
+ * Morpho borrow, the WETH leg from the Aave withdrawal (Balancer V2
66
+ * charges no fee, so the principal alone covers it).
67
+ *
68
+ * After execution the signer holds `debtAmount` USDC and an open Morpho
69
+ * Blue position (`collateralAmount` WETH collateral, `debtAmount +
70
+ * debtFlashloanFee` USDC debt). The Aave position is closed.
71
+ */
72
+ export const buildAaveToMorphoDebtMigration = ({
73
+ owner,
74
+ collateralAmount,
75
+ debtAmount,
76
+ debtFlashloanFee,
77
+ }: AaveToMorphoDebtMigrationInput): {
78
+ flow: Flow;
79
+ request: ComposeCompileRequest;
80
+ } => {
81
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
82
+
83
+ // Morpho borrow must equal principal + Aave-V3 flashloan fee so the
84
+ // `borrowed` resource exactly settles the USDC flashloan leg.
85
+ const morphoBorrowAmount = (
86
+ BigInt(debtAmount) + BigInt(debtFlashloanFee)
87
+ ).toString() as `${bigint}`;
88
+
89
+ const builder = sdk.flow(8453, {
90
+ name: 'aave-to-morpho-debt-migration',
91
+ inputs: {
92
+ initialCollateral: resources.erc20(BASE_WETH, 8453),
93
+ debtFlashloan: resources.erc20(BASE_USDC, 8453),
94
+ collateralFlashloan: resources.erc20(BASE_WETH, 8453),
95
+ },
96
+ });
97
+
98
+ // --- Phase 1: provision the Aave V3 borrow position ----------------------
99
+
100
+ // WETH → aBasWETH via the aave routing edge family (Aave V3 supply).
101
+ const supplyToAave = builder.lifi.zap('supply-to-aave', {
102
+ bind: { amountIn: builder.inputs.initialCollateral },
103
+ config: { resourceOut: resources.erc20(A_BAS_WETH, 8453) },
104
+ });
105
+
106
+ // Borrow USDC against the freshly supplied collateral. The `borrowed`
107
+ // resource is left unbound; request-level `sweepTo` forwards it to the
108
+ // signer as the user's loan proceeds.
109
+ builder.aave.borrow('borrow-from-aave', {
110
+ bind: {},
111
+ config: {
112
+ pool: AAVE_V3_POOL_BASE,
113
+ asset: BASE_USDC,
114
+ variableDebtToken: VDEBT_BAS_USDC,
115
+ amount: debtAmount,
116
+ },
117
+ });
118
+
119
+ // --- Phase 2: migrate to Morpho with two concurrent flashloans -----------
120
+
121
+ // Settle the Aave USDC debt with flashloan #1. mode: 'max' makes Aave's
122
+ // Pool clamp paybackAmount to userVariableDebt exactly, so any unspent
123
+ // flashloan principal flows through the op's residual port (swept by the
124
+ // request-level sweepTo).
125
+ builder.aave.repay('repay-aave', {
126
+ bind: {
127
+ assetIn: builder.inputs.debtFlashloan,
128
+ onBehalfOf: builder.context.executionAddress,
129
+ },
130
+ config: { pool: AAVE_V3_POOL_BASE, mode: 'max' },
131
+ });
132
+
133
+ // Withdraw the freed WETH from Aave (aBasWETH → WETH) via routing.
134
+ const withdrawFromAave = builder.lifi.zap('withdraw-from-aave', {
135
+ bind: { amountIn: supplyToAave.amountOut },
136
+ config: { resourceOut: resources.erc20(BASE_WETH, 8453) },
137
+ });
138
+
139
+ // Open the Morpho Blue position with flashloan #2's WETH.
140
+ builder.morphoBlue.supplyCollateral('supply-to-morpho', {
141
+ bind: { assetIn: builder.inputs.collateralFlashloan },
142
+ config: { marketParams: MORPHO_WETH_USDC_MARKET, mode: 'exact' },
143
+ });
144
+
145
+ // Borrow exactly principal + fee from Morpho to settle the USDC leg.
146
+ const morphoBorrow = builder.morphoBlue.borrow('borrow-from-morpho', {
147
+ bind: {},
148
+ config: {
149
+ marketParams: MORPHO_WETH_USDC_MARKET,
150
+ amount: morphoBorrowAmount,
151
+ },
152
+ });
153
+
154
+ // Settle the USDC flashloan leg with the Morpho borrow.
155
+ builder.lifi.flashloanRepay('repay-debt-flashloan', {
156
+ bind: { funds: morphoBorrow.borrowed },
157
+ config: { leg: 'debtFlashloan' },
158
+ });
159
+
160
+ // Settle the WETH flashloan leg with the Aave withdrawal. Balancer V2
161
+ // charges no fee, so the withdrawn principal alone covers the leg.
162
+ builder.lifi.flashloanRepay('repay-collateral-flashloan', {
163
+ bind: { funds: withdrawFromAave.amountOut },
164
+ config: { leg: 'collateralFlashloan' },
165
+ });
166
+
167
+ const flow = builder.build();
168
+
169
+ const request = sdk.request(flow, {
170
+ signer: owner,
171
+ inputs: {
172
+ initialCollateral: materialisers.directDeposit({
173
+ amount: collateralAmount,
174
+ }),
175
+ debtFlashloan: materialisers.flashloan({
176
+ providerKind: 'aave-v3',
177
+ amount: debtAmount,
178
+ }),
179
+ collateralFlashloan: materialisers.flashloan({
180
+ providerKind: 'balancer-v2',
181
+ amount: collateralAmount,
182
+ }),
183
+ },
184
+ // Sweep the Aave borrow proceeds (and any aave.repay residual) to the
185
+ // signer.
186
+ sweepTo: builder.context.sender,
187
+ });
188
+
189
+ return { flow, request };
190
+ };
@@ -0,0 +1,221 @@
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
+ import { BASE_URL } from '../config.js';
6
+
7
+ // Base mainnet token addresses.
8
+ const BASE_WETH = '0x4200000000000000000000000000000000000006';
9
+ const BASE_USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
10
+ // Circle EURC on Base (6 decimals).
11
+ const BASE_EURC = '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42';
12
+
13
+ // Aave V3 on Base (pool + aWETH receipt + variable-debt USDC).
14
+ const AAVE_V3_POOL_BASE = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5';
15
+ const A_BAS_WETH = '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7';
16
+ const VDEBT_BAS_USDC = '0x59dca05b6c26dbd64b5381374aAaC5CD05644C28';
17
+
18
+ // Morpho Blue WETH/EURC market on Base. Real listed market — borrow APY
19
+ // ~1.02% at the time of writing vs ~5.30% on the WETH/USDC market, which is
20
+ // what makes this migration economically interesting. Market ID:
21
+ // 0xa9b5142fa687a24c275faf731f13b52faa9873252bb4e1cb6077aa1f412edb0b
22
+ // (= keccak256(MarketParams) of the values below).
23
+ const ADAPTIVE_CURVE_IRM_BASE = '0x46415998764C29aB2a25CbeA6254146D50D22687';
24
+ const WETH_EURC_ORACLE = '0xE1bb8E5b4930eC9FeC7f7943FCF6227649F14B37';
25
+ const WETH_EURC_LLTV = '860000000000000000';
26
+
27
+ const MORPHO_WETH_EURC_MARKET = {
28
+ loanToken: BASE_EURC,
29
+ collateralToken: BASE_WETH,
30
+ oracle: WETH_EURC_ORACLE,
31
+ irm: ADAPTIVE_CURVE_IRM_BASE,
32
+ lltv: WETH_EURC_LLTV,
33
+ };
34
+
35
+ // Slippage budget on the EURC → USDC swap. The caller must size
36
+ // `morphoBorrowAmount` such that the swap's slippage-adjusted minimum covers
37
+ // `debtAmount + computeDefaultFee('aave-v3', debtAmount)`.
38
+ const SWAP_SLIPPAGE = 0.03;
39
+
40
+ export interface AaveToMorphoDebtMigrationWithSwapInput {
41
+ readonly owner: Address;
42
+ /** WETH collateral the user posts (in smallest units, 18 decimals). */
43
+ readonly collateralAmount: `${bigint}`;
44
+ /** USDC principal borrowed from Aave (6 decimals). */
45
+ readonly debtAmount: `${bigint}`;
46
+ /**
47
+ * EURC to borrow from Morpho (6 decimals). Must be large enough that, after
48
+ * the EURC → USDC swap at `SWAP_SLIPPAGE`, the swap's minimum output covers
49
+ * `debtAmount + computeDefaultFee('aave-v3', debtAmount)`. Caller sizes this
50
+ * off-chain from a live LI.FI quote, e.g.
51
+ * morphoBorrowAmount =
52
+ * ceil((debtAmount + aaveV3Fee) * eurcPerUsdc / (1 - slippage))
53
+ * Note: EUR/USD is not 1:1 — the example fixture assumes ~1.075 USD/EUR.
54
+ */
55
+ readonly morphoBorrowAmount: `${bigint}`;
56
+ }
57
+
58
+ /**
59
+ * Atomically migrate an Aave V3 USDC borrow on Base into the Morpho Blue
60
+ * WETH/EURC market — same WETH collateral, different stablecoin debt. At the
61
+ * time of writing Morpho's WETH/EURC borrow APY is ~1.02% vs ~5.30% on
62
+ * USDC-denominated WETH-collateral markets, so a long-running borrow position
63
+ * can be made materially cheaper by migrating the debt asset.
64
+ *
65
+ * Topology mirrors `aaveToMorphoDebtMigration`, with one extra step: after
66
+ * Morpho mints EURC debt, the borrow proceeds are swapped to USDC via
67
+ * `lifi.swap`, and the swap's `amountOut` (which carries the slippage-adjusted
68
+ * minimum) settles the USDC flashloan leg.
69
+ *
70
+ * Phase 1 — provision the source position on Aave V3:
71
+ * - User direct-deposits WETH.
72
+ * - `lifi.zap` supplies the WETH to Aave (WETH → aBasWETH routing edge).
73
+ * - `aave.borrow` mints USDC debt; the borrowed USDC is swept back to the
74
+ * signer as the user's loan proceeds.
75
+ *
76
+ * Phase 2 — migrate to Morpho using two concurrent flashloans:
77
+ * - Flashloan #1 (Aave V3, USDC) bridges the Aave debt payoff before the
78
+ * Morpho borrow + swap exists.
79
+ * - Flashloan #2 (Balancer V2, WETH) bridges the Morpho collateral supply
80
+ * before the Aave collateral is freed.
81
+ * - `aave.repay` clears the Aave USDC debt with flashloan #1.
82
+ * - `lifi.zap` withdraws the freed WETH from Aave (aBasWETH → WETH).
83
+ * - `morphoBlue.supplyCollateral` deposits flashloan #2's WETH into Morpho.
84
+ * - `morphoBlue.borrow` mints `morphoBorrowAmount` EURC.
85
+ * - `lifi.swap` converts the borrowed EURC into USDC. `amountOut` is the
86
+ * slippage-adjusted minimum that settles the USDC flashloan leg.
87
+ * - Two `lifi.flashloanRepay` ops settle each leg: the USDC leg from the swap
88
+ * output, the WETH leg from the Aave withdrawal (Balancer V2 charges no
89
+ * fee, so the withdrawn principal alone covers it).
90
+ *
91
+ * After execution the signer holds a Morpho Blue position with
92
+ * `collateralAmount` WETH collateral and `morphoBorrowAmount` EURC debt; the
93
+ * Aave position is closed. Any EURC dust (Morpho residual above what the swap
94
+ * consumed) and USDC residual from `aave.repay`'s `mode: 'max'` clamp flow
95
+ * back to the signer via `sweepTo`.
96
+ */
97
+ export const buildAaveToMorphoDebtMigrationWithSwap = ({
98
+ owner,
99
+ collateralAmount,
100
+ debtAmount,
101
+ morphoBorrowAmount,
102
+ }: AaveToMorphoDebtMigrationWithSwapInput): {
103
+ flow: Flow;
104
+ request: ComposeCompileRequest;
105
+ } => {
106
+ const sdk = createComposeSdk({ baseUrl: BASE_URL });
107
+
108
+ const builder = sdk.flow(8453, {
109
+ name: 'aave-to-morpho-debt-migration-with-swap',
110
+ inputs: {
111
+ initialCollateral: resources.erc20(BASE_WETH, 8453),
112
+ debtFlashloan: resources.erc20(BASE_USDC, 8453),
113
+ collateralFlashloan: resources.erc20(BASE_WETH, 8453),
114
+ },
115
+ });
116
+
117
+ // --- Phase 1: provision the Aave V3 borrow position ----------------------
118
+
119
+ // WETH → aBasWETH via the aave routing edge family (Aave V3 supply).
120
+ const supplyToAave = builder.lifi.zap('supply-to-aave', {
121
+ bind: { amountIn: builder.inputs.initialCollateral },
122
+ config: { resourceOut: resources.erc20(A_BAS_WETH, 8453) },
123
+ });
124
+
125
+ // Borrow USDC against the freshly supplied collateral. The `borrowed`
126
+ // resource is left unbound; request-level `sweepTo` forwards it to the
127
+ // signer as the user's loan proceeds.
128
+ builder.aave.borrow('borrow-from-aave', {
129
+ bind: {},
130
+ config: {
131
+ pool: AAVE_V3_POOL_BASE,
132
+ asset: BASE_USDC,
133
+ variableDebtToken: VDEBT_BAS_USDC,
134
+ amount: debtAmount,
135
+ },
136
+ });
137
+
138
+ // --- Phase 2: migrate to Morpho with two concurrent flashloans -----------
139
+
140
+ // Settle the Aave USDC debt with flashloan #1. mode: 'max' makes Aave's
141
+ // Pool clamp paybackAmount to userVariableDebt exactly, so any unspent
142
+ // flashloan principal flows through the op's residual port (swept by
143
+ // request-level sweepTo).
144
+ builder.aave.repay('repay-aave', {
145
+ bind: {
146
+ assetIn: builder.inputs.debtFlashloan,
147
+ onBehalfOf: builder.context.executionAddress,
148
+ },
149
+ config: { pool: AAVE_V3_POOL_BASE, mode: 'max' },
150
+ });
151
+
152
+ // Withdraw the freed WETH from Aave (aBasWETH → WETH) via routing.
153
+ const withdrawFromAave = builder.lifi.zap('withdraw-from-aave', {
154
+ bind: { amountIn: supplyToAave.amountOut },
155
+ config: { resourceOut: resources.erc20(BASE_WETH, 8453) },
156
+ });
157
+
158
+ // Open the Morpho Blue position with flashloan #2's WETH.
159
+ builder.morphoBlue.supplyCollateral('supply-to-morpho', {
160
+ bind: { assetIn: builder.inputs.collateralFlashloan },
161
+ config: { marketParams: MORPHO_WETH_EURC_MARKET, mode: 'exact' },
162
+ });
163
+
164
+ // Borrow EURC at the destination's (cheaper) APR.
165
+ const morphoBorrow = builder.morphoBlue.borrow('borrow-from-morpho', {
166
+ bind: {},
167
+ config: {
168
+ marketParams: MORPHO_WETH_EURC_MARKET,
169
+ amount: morphoBorrowAmount,
170
+ },
171
+ });
172
+
173
+ // Swap EURC → USDC so the borrow proceeds can settle the USDC flashloan
174
+ // leg. `amountOut` carries `providesMinimum: true`; the caller is
175
+ // responsible for sizing `morphoBorrowAmount` so the slippage-adjusted
176
+ // minimum covers `debtAmount + aaveV3Fee(debtAmount)`.
177
+ const swapDebt = builder.lifi.swap('swap-borrow-to-usdc', {
178
+ bind: { amountIn: morphoBorrow.borrowed },
179
+ config: {
180
+ resourceOut: resources.erc20(BASE_USDC, 8453),
181
+ slippage: SWAP_SLIPPAGE,
182
+ },
183
+ });
184
+
185
+ // Settle the USDC flashloan leg with the swap output.
186
+ builder.lifi.flashloanRepay('repay-debt-flashloan', {
187
+ bind: { funds: swapDebt.amountOut },
188
+ config: { leg: 'debtFlashloan' },
189
+ });
190
+
191
+ // Settle the WETH flashloan leg with the Aave withdrawal. Balancer V2
192
+ // charges no fee, so the withdrawn principal alone covers the leg.
193
+ builder.lifi.flashloanRepay('repay-collateral-flashloan', {
194
+ bind: { funds: withdrawFromAave.amountOut },
195
+ config: { leg: 'collateralFlashloan' },
196
+ });
197
+
198
+ const flow = builder.build();
199
+
200
+ const request = sdk.request(flow, {
201
+ signer: owner,
202
+ inputs: {
203
+ initialCollateral: materialisers.directDeposit({
204
+ amount: collateralAmount,
205
+ }),
206
+ debtFlashloan: materialisers.flashloan({
207
+ providerKind: 'aave-v3',
208
+ amount: debtAmount,
209
+ }),
210
+ collateralFlashloan: materialisers.flashloan({
211
+ providerKind: 'balancer-v2',
212
+ amount: collateralAmount,
213
+ }),
214
+ },
215
+ // Sweep Aave borrow proceeds, aave.repay's residual, and any EURC / USDC
216
+ // dust left after the swap back to the signer.
217
+ sweepTo: builder.context.sender,
218
+ });
219
+
220
+ return { flow, request };
221
+ };