@lifi/composer-sdk 0.2.0-staging.4 → 0.2.0-staging.5

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 (38) hide show
  1. package/dist/authoring/handles.cjs.map +1 -1
  2. package/dist/authoring/handles.d.cts +13 -6
  3. package/dist/authoring/handles.d.ts +13 -6
  4. package/dist/authoring/handles.js.map +1 -1
  5. package/dist/client.cjs +1 -1
  6. package/dist/client.js +1 -1
  7. package/dist/generated/config.generated.cjs.map +1 -1
  8. package/dist/generated/config.generated.d.cts +39 -1
  9. package/dist/generated/config.generated.d.ts +39 -1
  10. package/dist/generated/operations.generated.cjs +55 -1
  11. package/dist/generated/operations.generated.cjs.map +1 -1
  12. package/dist/generated/operations.generated.d.cts +204 -2
  13. package/dist/generated/operations.generated.d.ts +204 -2
  14. package/dist/generated/operations.generated.js +55 -1
  15. package/dist/generated/operations.generated.js.map +1 -1
  16. package/dist/index.d.cts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/package.json +2 -2
  19. package/src/authoring/handles.ts +19 -10
  20. package/src/examples/aaveRepay.ts +55 -16
  21. package/src/examples/aaveRepayWithATokens.ts +41 -30
  22. package/src/examples/aaveRounding.ts +42 -0
  23. package/src/examples/aaveSetEMode.ts +0 -1
  24. package/src/examples/config.ts +1 -1
  25. package/src/examples/emitCustomEvent.ts +75 -0
  26. package/src/examples/invariantChecks.ts +96 -0
  27. package/src/examples/run.ts +20 -18
  28. package/src/examples/staged/aaveBorrow.ts +38 -14
  29. package/src/examples/staged/aaveToMorphoDebtMigration.ts +13 -3
  30. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwap.ts +12 -2
  31. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwapExactOut.ts +5 -0
  32. package/src/examples/staged/flashloanRepay.ts +4 -0
  33. package/src/examples/staged/morphoBlueBorrow.ts +4 -0
  34. package/src/examples/staged/morphoBlueRepay.ts +59 -18
  35. package/src/examples/staged/registry.ts +7 -5
  36. package/src/examples/untypedOpWithTypedRef.ts +26 -20
  37. package/src/generated/config.generated.ts +50 -0
  38. package/src/generated/operations.generated.ts +330 -0
@@ -1,12 +1,19 @@
1
1
  import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
2
 
3
- import { createComposeSdk, guards } from '../../index.js';
3
+ import {
4
+ createComposeSdk,
5
+ guards,
6
+ materialisers,
7
+ resources,
8
+ } from '../../index.js';
4
9
  import type { Address } from '../../types.js';
5
10
  import { BASE_URL } from '../config.js';
6
11
 
7
12
  // Aave v3 contracts on Ethereum mainnet.
8
13
  const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
9
14
  const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
15
+ // Aave v3 aEthUSDC receipt token and variable-debt USDC token.
16
+ const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c';
10
17
  const VDEBT_USDC = '0x72E95b8931767C79bA4EeE721354d6E99a61D004';
11
18
 
12
19
  // 1.05 WAD (1e18) — minimum acceptable Aave health factor after the borrow.
@@ -16,53 +23,67 @@ const MIN_HEALTH_FACTOR_WAD = '1050000000000000000';
16
23
  export interface AaveBorrowInput {
17
24
  readonly owner: Address;
18
25
  readonly recipient: Address;
19
- readonly amount: `${bigint}`;
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}`;
20
30
  }
21
31
 
22
32
  /**
23
- * Borrow USDC against existing Aave v3 collateral and transfer it to a recipient,
24
- * with an on-chain health-factor guard that reverts if the post-borrow position
25
- * is too thin.
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.
26
36
  *
27
- * The proxy address must already hold supplied collateral. The borrow op
28
- * exposes the borrowed amount as a tracked resource so downstream nodes can
29
- * spend it.
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.
30
41
  *
31
42
  * Demonstrates:
43
+ * - `lifi.zap` to supply USDC into Aave (USDC → aEthUSDC routing edge)
32
44
  * - `aave.borrow` with no resource input — the borrowed asset materialises
33
45
  * as the `borrowed` resource output.
34
46
  * - Chaining the borrow output into a `core.transfer` to forward funds.
35
- * - The `debtBalance` handle reports the post-borrow variable-debt balance
36
- * for the proxy.
37
47
  * - `aave.getHealthFactor` + `core.numericInvariant` guard asserts that
38
48
  * `healthFactor >= MIN_HEALTH_FACTOR_WAD` after the borrow; the run reverts
39
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.
40
54
  */
41
55
  export const buildAaveBorrow = ({
42
56
  owner,
43
57
  recipient,
44
- amount,
58
+ collateralAmount,
59
+ borrowAmount,
45
60
  }: AaveBorrowInput): {
46
61
  flow: Flow;
47
62
  request: ComposeCompileRequest;
48
63
  } => {
49
64
  const sdk = createComposeSdk({ baseUrl: BASE_URL });
50
65
 
51
- // The flow takes no resource inputs — Aave hands us the borrowed asset.
52
66
  const builder = sdk.flow(1, {
53
67
  name: 'aave-borrow-usdc',
54
68
  inputs: {
69
+ collateralIn: resources.erc20(USDC, 1),
55
70
  recipient: 'address',
56
71
  },
57
72
  });
58
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
+
59
80
  const borrowOut = builder.aave.borrow('borrow', {
60
81
  bind: {},
61
82
  config: {
62
83
  pool: AAVE_V3_POOL,
63
84
  asset: USDC,
64
85
  variableDebtToken: VDEBT_USDC,
65
- amount,
86
+ amount: borrowAmount,
66
87
  },
67
88
  });
68
89
 
@@ -92,12 +113,15 @@ export const buildAaveBorrow = ({
92
113
 
93
114
  const flow = builder.build();
94
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.
95
119
  const request = sdk.request(flow, {
96
120
  signer: owner,
97
121
  inputs: {
122
+ collateralIn: materialisers.directDeposit({ amount: collateralAmount }),
98
123
  recipient,
99
124
  },
100
- sweepTo: builder.context.sender,
101
125
  });
102
126
 
103
127
  return { flow, request };
@@ -2,6 +2,10 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
2
 
3
3
  import { createComposeSdk, materialisers, resources } from '../../index.js';
4
4
  import type { Address } from '../../types.js';
5
+ import {
6
+ padForAaveDebtRounding,
7
+ trimForAaveWithdrawalRounding,
8
+ } from '../aaveRounding.js';
5
9
  import { BASE_URL } from '../config.js';
6
10
 
7
11
  // Base mainnet token addresses.
@@ -80,10 +84,16 @@ export const buildAaveToMorphoDebtMigration = ({
80
84
  } => {
81
85
  const sdk = createComposeSdk({ baseUrl: BASE_URL });
82
86
 
87
+ // Both flashloan legs absorb Aave's 1-wei scaled-balance skew: the USDC
88
+ // leg funds a max-mode repay, the WETH leg is covered by a withdrawal.
89
+ const debtFlashloanAmount = padForAaveDebtRounding(debtAmount);
90
+ const collateralFlashloanAmount =
91
+ trimForAaveWithdrawalRounding(collateralAmount);
92
+
83
93
  // Morpho borrow must equal principal + Aave-V3 flashloan fee so the
84
94
  // `borrowed` resource exactly settles the USDC flashloan leg.
85
95
  const morphoBorrowAmount = (
86
- BigInt(debtAmount) + BigInt(debtFlashloanFee)
96
+ BigInt(debtFlashloanAmount) + BigInt(debtFlashloanFee)
87
97
  ).toString() as `${bigint}`;
88
98
 
89
99
  const builder = sdk.flow(8453, {
@@ -174,11 +184,11 @@ export const buildAaveToMorphoDebtMigration = ({
174
184
  }),
175
185
  debtFlashloan: materialisers.flashloan({
176
186
  providerKind: 'aave-v3',
177
- amount: debtAmount,
187
+ amount: debtFlashloanAmount,
178
188
  }),
179
189
  collateralFlashloan: materialisers.flashloan({
180
190
  providerKind: 'balancer-v2',
181
- amount: collateralAmount,
191
+ amount: collateralFlashloanAmount,
182
192
  }),
183
193
  },
184
194
  // Sweep the Aave borrow proceeds (and any aave.repay residual) to the
@@ -2,6 +2,10 @@ import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
2
 
3
3
  import { createComposeSdk, materialisers, resources } from '../../index.js';
4
4
  import type { Address } from '../../types.js';
5
+ import {
6
+ padForAaveDebtRounding,
7
+ trimForAaveWithdrawalRounding,
8
+ } from '../aaveRounding.js';
5
9
  import { BASE_URL } from '../config.js';
6
10
 
7
11
  // Base mainnet token addresses.
@@ -105,6 +109,12 @@ export const buildAaveToMorphoDebtMigrationWithSwap = ({
105
109
  } => {
106
110
  const sdk = createComposeSdk({ baseUrl: BASE_URL });
107
111
 
112
+ // Both flashloan legs absorb Aave's 1-wei scaled-balance skew: the USDC
113
+ // leg funds a max-mode repay, the WETH leg is covered by a withdrawal.
114
+ const debtFlashloanAmount = padForAaveDebtRounding(debtAmount);
115
+ const collateralFlashloanAmount =
116
+ trimForAaveWithdrawalRounding(collateralAmount);
117
+
108
118
  const builder = sdk.flow(8453, {
109
119
  name: 'aave-to-morpho-debt-migration-with-swap',
110
120
  inputs: {
@@ -205,11 +215,11 @@ export const buildAaveToMorphoDebtMigrationWithSwap = ({
205
215
  }),
206
216
  debtFlashloan: materialisers.flashloan({
207
217
  providerKind: 'aave-v3',
208
- amount: debtAmount,
218
+ amount: debtFlashloanAmount,
209
219
  }),
210
220
  collateralFlashloan: materialisers.flashloan({
211
221
  providerKind: 'balancer-v2',
212
- amount: collateralAmount,
222
+ amount: collateralFlashloanAmount,
213
223
  }),
214
224
  },
215
225
  // Sweep Aave borrow proceeds, aave.repay's residual, and any EURC / USDC
@@ -273,6 +273,10 @@ export const buildAaveToMorphoDebtMigrationWithSwapExactOut = ({
273
273
 
274
274
  const flow = builder.build();
275
275
 
276
+ const assumptions: Record<string, bigint> = {
277
+ 'buy-usdc-exact.amountIn': BigInt(morphoBorrowAmount),
278
+ };
279
+
276
280
  const request = sdk.request(flow, {
277
281
  signer: owner,
278
282
  inputs: {
@@ -296,6 +300,7 @@ export const buildAaveToMorphoDebtMigrationWithSwapExactOut = ({
296
300
  // Sweep Aave borrow proceeds, aave.repay's residual, and any EURC over-repay
297
301
  // residual from the capped Morpho repay back to the signer.
298
302
  sweepTo: builder.context.sender,
303
+ assumptions,
299
304
  });
300
305
 
301
306
  return { flow, request };
@@ -69,6 +69,10 @@ export const buildTrivialFlashloanRepay = ({
69
69
  }),
70
70
  feeTopUp: materialisers.directDeposit({ amount: fee }),
71
71
  },
72
+ // The smoke flow returns the full principal + fee to the adapter and
73
+ // produces no user output, so the guard sees the fee top-up as 100%
74
+ // loss. 0 skips the check; the user's real cost is only the provider fee.
75
+ maxPriceImpactBps: 0,
72
76
  });
73
77
 
74
78
  return { flow, request };
@@ -83,6 +83,10 @@ export const buildMorphoBlueBorrow = ({
83
83
  },
84
84
  // Sweep the borrowed USDC back to the signer.
85
85
  sweepTo: builder.context.sender,
86
+ // The WETH collateral becomes unpriced Morpho position state (no receipt
87
+ // token), so the default 500 bps price-impact check reads any safe borrow
88
+ // (LLTV caps at 86%) as a loss. 0 skips the check.
89
+ maxPriceImpactBps: 0,
86
90
  });
87
91
 
88
92
  return { flow, request };
@@ -21,27 +21,41 @@ const WETH_USDC_MARKET = {
21
21
 
22
22
  export interface MorphoBlueRepayInput {
23
23
  readonly owner: Address;
24
- readonly repayAmount: `${bigint}`;
24
+ /** WETH supplied as collateral for the in-flow borrow (18 dp). */
25
25
  readonly collateralAmount: `${bigint}`;
26
+ /** USDC borrowed against the collateral, then repaid by this flow (6 dp). */
27
+ readonly borrowAmount: `${bigint}`;
28
+ /**
29
+ * USDC deposited to repay the debt (6 dp). Must exceed `borrowAmount`:
30
+ * Morpho's share rounding can leave the debt a wei above the borrowed
31
+ * amount, so the `max`-mode repay needs a little headroom. The leftover
32
+ * surfaces on the `residual` port.
33
+ */
34
+ readonly repayAmount: `${bigint}`;
26
35
  }
27
36
 
28
37
  /**
29
- * Close a Morpho Blue WETH/USDC position on Base:
38
+ * Open and close a Morpho Blue WETH/USDC position on Base in one flow —
39
+ * a self-contained supplyCollateral → borrow → repay → withdrawCollateral
40
+ * round trip that creates the debt it retires.
30
41
  *
31
- * - User direct-deposits USDC (slightly over-funded to exercise the residual
32
- * port).
33
- * - `morphoBlue.repay` with `mode: 'max'` reads the proxy's current
34
- * `borrowShares` via a precursor `Morpho.position` static call and passes
35
- * the share count back into `repay` to retire the debt to the wei. Any
36
- * unused USDC surfaces on the `residual` Resource port.
37
- * - `morphoBlue.withdrawCollateral` pulls the freed WETH back to the signer's
38
- * wallet.
39
- * - The flow sweeps both the freed WETH and the USDC residual to the signer.
42
+ * Demonstrates:
43
+ * - `morphoBlue.supplyCollateral` to post WETH (collateral is internal
44
+ * position state — no receipt token).
45
+ * - `morphoBlue.borrow` to mint USDC debt against it.
46
+ * - `morphoBlue.repay` with `mode: 'max'`, which reads the proxy's current
47
+ * `borrowShares` on-chain and repays in shares mode to retire the debt to
48
+ * the wei. Unused USDC surfaces on the `residual` Resource port.
49
+ * - `morphoBlue.withdrawCollateral` to pull the freed WETH once the debt
50
+ * is cleared.
51
+ * - `sweepTo` to return the borrowed USDC, the repay residual, and the
52
+ * freed WETH to the signer.
40
53
  */
41
54
  export const buildMorphoBlueRepay = ({
42
55
  owner,
43
- repayAmount,
44
56
  collateralAmount,
57
+ borrowAmount,
58
+ repayAmount,
45
59
  }: MorphoBlueRepayInput): {
46
60
  flow: Flow;
47
61
  request: ComposeCompileRequest;
@@ -51,13 +65,38 @@ export const buildMorphoBlueRepay = ({
51
65
  const builder = sdk.flow(8453, {
52
66
  name: 'morpho-blue-repay-usdc-withdraw-weth',
53
67
  inputs: {
54
- assetIn: resources.erc20(BASE_USDC, 8453),
68
+ collateralIn: resources.erc20(BASE_WETH, 8453),
69
+ repayIn: resources.erc20(BASE_USDC, 8453),
70
+ },
71
+ });
72
+
73
+ // Post WETH collateral so the proxy can open the debt it will repay.
74
+ builder.morphoBlue.supplyCollateral('supply-collateral', {
75
+ bind: {
76
+ assetIn: builder.inputs.collateralIn,
77
+ },
78
+ config: {
79
+ marketParams: WETH_USDC_MARKET,
80
+ mode: 'exact',
81
+ },
82
+ });
83
+
84
+ // Borrow USDC against the collateral; the borrowed USDC stays on the
85
+ // proxy as a terminal resource and is swept back to the signer.
86
+ builder.morphoBlue.borrow('borrow', {
87
+ bind: {},
88
+ config: {
89
+ marketParams: WETH_USDC_MARKET,
90
+ amount: borrowAmount,
55
91
  },
56
92
  });
57
93
 
94
+ // Retire the just-opened debt from the deposited USDC. mode 'max' reads
95
+ // the live borrowShares and repays in shares mode, clearing the debt to
96
+ // the wei; the unused USDC surfaces on the `residual` port.
58
97
  builder.morphoBlue.repay('repay', {
59
98
  bind: {
60
- assetIn: builder.inputs.assetIn,
99
+ assetIn: builder.inputs.repayIn,
61
100
  onBehalfOf: builder.context.executionAddress,
62
101
  },
63
102
  config: {
@@ -66,8 +105,8 @@ export const buildMorphoBlueRepay = ({
66
105
  },
67
106
  });
68
107
 
69
- // The withdrawn WETH lands on the proxy; the request-level `sweepTo`
70
- // pushes it (and the USDC `residual` from the repay) to the signer.
108
+ // The debt is cleared, so the full collateral can be withdrawn. The freed
109
+ // WETH lands on the proxy and is swept to the signer.
71
110
  builder.morphoBlue.withdrawCollateral('withdraw-collateral', {
72
111
  bind: {},
73
112
  config: {
@@ -81,9 +120,11 @@ export const buildMorphoBlueRepay = ({
81
120
  const request = sdk.request(flow, {
82
121
  signer: owner,
83
122
  inputs: {
84
- assetIn: materialisers.directDeposit({ amount: repayAmount }),
123
+ collateralIn: materialisers.directDeposit({ amount: collateralAmount }),
124
+ repayIn: materialisers.directDeposit({ amount: repayAmount }),
85
125
  },
86
- // Sweep freed WETH and the USDC residual back to the signer.
126
+ // Sweep the borrowed USDC, the repay residual, and the freed WETH back
127
+ // to the signer.
87
128
  sweepTo: builder.context.sender,
88
129
  });
89
130
 
@@ -25,7 +25,8 @@ export const STAGED_EXAMPLES: Record<string, () => ComposeCompileRequest> = {
25
25
  buildAaveBorrow({
26
26
  owner: OWNER,
27
27
  recipient: RECIPIENT,
28
- amount: '1000000000',
28
+ collateralAmount: '1000000000',
29
+ borrowAmount: '500000000',
29
30
  }).request,
30
31
  'aave-hf-explode': () =>
31
32
  buildAaveHealthFactorExplode({ signer: OWNER }).request,
@@ -33,13 +34,14 @@ export const STAGED_EXAMPLES: Record<string, () => ComposeCompileRequest> = {
33
34
  buildMorphoBlueBorrow({
34
35
  owner: OWNER,
35
36
  collateralAmount: '1000000000000000000',
36
- borrowAmount: '1500000000',
37
+ borrowAmount: '1200000000',
37
38
  }).request,
38
39
  'morpho-repay': () =>
39
40
  buildMorphoBlueRepay({
40
41
  owner: OWNER,
41
- repayAmount: '1505000000',
42
42
  collateralAmount: '1000000000000000000',
43
+ borrowAmount: '500000000',
44
+ repayAmount: '501000000',
43
45
  }).request,
44
46
  'debt-rebalance': () =>
45
47
  buildAaveDebtRebalanceExactOut({
@@ -52,8 +54,8 @@ export const STAGED_EXAMPLES: Record<string, () => ComposeCompileRequest> = {
52
54
  buildAaveToMorphoDebtMigrationWithSwapExactOut({
53
55
  owner: OWNER,
54
56
  collateralAmount: '1000000000000000000',
55
- debtAmount: '2000000000',
56
- morphoBorrowAmount: '1950000000',
57
+ debtAmount: '1000000000',
58
+ morphoBorrowAmount: '975000000',
57
59
  }).request,
58
60
  'flashloan-repay': () =>
59
61
  buildTrivialFlashloanRepay({
@@ -1,25 +1,32 @@
1
1
  import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
2
2
 
3
- import { createComposeSdk, materialisers, raw, resources } from '../index.js';
3
+ import { createComposeSdk, raw } from '../index.js';
4
4
  import type { Address } from '../types.js';
5
5
 
6
6
  import { BASE_URL } from './config.js';
7
7
 
8
- const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
9
- // Example custom vault that the typed builder doesn't cover yet
10
- const CUSTOM_VAULT = '0x1111111111111111111111111111111111111111';
8
+ // Real ERC-4626 vault (Steakhouse USDC on Ethereum mainnet) queried through
9
+ // the untyped escape hatch.
10
+ const VAULT = '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB';
11
11
 
12
12
  export interface UntypedOpWithTypedRefInput {
13
13
  readonly owner: Address;
14
14
  }
15
15
 
16
16
  /**
17
- * Insert an untyped operation node for an unsupported operation, then feed
18
- * its output into typed builder methods using `raw.ref<T>()`.
17
+ * Insert an untyped operation node, then feed its output into typed builder
18
+ * methods using `raw.ref<T>()`.
19
+ *
20
+ * `untypedOp` is the escape hatch for ops the installed SDK's typed surface
21
+ * doesn't cover — typically backend ops newer than your SDK version. The op
22
+ * id is passed as a plain string and its bind/config shapes are not
23
+ * type-checked, so the backend validates them at compile time instead.
24
+ *
25
+ * This example routes a real op (`core.staticCall`) through the untyped path
26
+ * so the flow compiles end-to-end; the mechanism is identical for any op id.
19
27
  *
20
28
  * Demonstrates:
21
- * - `builder.untypedOp()` to add a node the typed API doesn't cover
22
- * (e.g. a custom vault query).
29
+ * - `builder.untypedOp()` to add a node the typed API doesn't cover.
23
30
  * - `raw.ref<'uint256'>()` to reference the untyped node's output in a typed
24
31
  * `Bindable<'uint256'>` slot — the explicit type parameter is required
25
32
  * so the compiler verifies the ref is used in a compatible slot.
@@ -35,16 +42,17 @@ export const buildUntypedOpWithTypedRef = ({
35
42
 
36
43
  const builder = sdk.flow(1, {
37
44
  name: 'untyped-op-with-typed-ref',
38
- inputs: {
39
- amountIn: resources.erc20(USDC, 1),
40
- },
45
+ inputs: {},
41
46
  });
42
47
 
43
- // Insert an untyped node for a custom vault query. untypedOp returns void —
48
+ // Insert an untyped node querying the vault. untypedOp returns void —
44
49
  // 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 },
50
+ builder.untypedOp('vault-query', 'core.staticCall', {
51
+ bind: {},
52
+ config: {
53
+ target: VAULT,
54
+ functionSignature: 'function totalAssets() view returns (uint256)',
55
+ },
48
56
  });
49
57
 
50
58
  // Use raw.ref to bridge the untyped node's output into a typed operation.
@@ -60,17 +68,15 @@ export const buildUntypedOpWithTypedRef = ({
60
68
 
61
69
  // Continue with fully typed operations downstream.
62
70
  builder.core.assertGte('check-min', {
63
- bind: { a: scaled.result, b: builder.inputs.amountIn },
71
+ bind: { a: scaled.result, b: vaultResult },
64
72
  });
65
73
 
66
74
  const flow = builder.build();
67
75
 
76
+ // No sweepTo: the flow reads state and asserts — it produces no resources.
68
77
  const request = sdk.request(flow, {
69
78
  signer: owner,
70
- inputs: {
71
- amountIn: materialisers.directDeposit({ amount: '1000000' }),
72
- },
73
- sweepTo: builder.context.sender,
79
+ inputs: {},
74
80
  });
75
81
 
76
82
  return { flow, request };
@@ -55,6 +55,40 @@ export interface LifiSwapConfig {
55
55
  };
56
56
  }
57
57
 
58
+ export interface InvariantNumericConfig {
59
+ op: 'gte' | 'gt' | 'lte' | 'lt' | 'eq' | 'neq';
60
+ threshold: IntegerStringInput;
61
+ }
62
+
63
+ export interface InvariantNumericInRangeConfig {
64
+ min: IntegerStringInput;
65
+ max: IntegerStringInput;
66
+ }
67
+
68
+ export interface InvariantGteConfig {
69
+ threshold: IntegerStringInput;
70
+ }
71
+
72
+ export interface InvariantGtConfig {
73
+ threshold: IntegerStringInput;
74
+ }
75
+
76
+ export interface InvariantLteConfig {
77
+ threshold: IntegerStringInput;
78
+ }
79
+
80
+ export interface InvariantLtConfig {
81
+ threshold: IntegerStringInput;
82
+ }
83
+
84
+ export interface InvariantEqConfig {
85
+ threshold: IntegerStringInput;
86
+ }
87
+
88
+ export interface InvariantNeqConfig {
89
+ threshold: IntegerStringInput;
90
+ }
91
+
58
92
  export interface CoreBpsDownConfig {
59
93
  bps: number;
60
94
  }
@@ -76,6 +110,22 @@ export interface CoreTransferConfig {
76
110
  amount?: IntegerStringInput;
77
111
  }
78
112
 
113
+ export interface CoreEmitEvent1Config {
114
+ signature: string;
115
+ }
116
+
117
+ export interface CoreEmitEvent2Config {
118
+ signature: string;
119
+ }
120
+
121
+ export interface CoreEmitEvent3Config {
122
+ signature: string;
123
+ }
124
+
125
+ export interface CoreEmitEvent4Config {
126
+ signature: string;
127
+ }
128
+
79
129
  export interface CoreCallConfig {
80
130
  target: Address;
81
131
  functionSignature: string;