@lifi/composer-sdk 0.7.0 → 0.8.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.
- package/README.md +6 -0
- package/dist/authoring/FlowBuilderCore.cjs +1 -1
- package/dist/authoring/FlowBuilderCore.cjs.map +1 -1
- package/dist/authoring/FlowBuilderCore.js +1 -1
- package/dist/authoring/FlowBuilderCore.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/generated/config.generated.cjs.map +1 -1
- package/dist/generated/config.generated.d.cts +77 -35
- package/dist/generated/config.generated.d.ts +77 -35
- package/dist/generated/guards.generated.cjs +6 -0
- package/dist/generated/guards.generated.cjs.map +1 -1
- package/dist/generated/guards.generated.d.cts +1 -1
- package/dist/generated/guards.generated.d.ts +1 -1
- package/dist/generated/guards.generated.js +5 -0
- package/dist/generated/guards.generated.js.map +1 -1
- package/dist/generated/operations.generated.cjs +56 -1
- package/dist/generated/operations.generated.cjs.map +1 -1
- package/dist/generated/operations.generated.d.cts +155 -2
- package/dist/generated/operations.generated.d.ts +155 -2
- package/dist/generated/operations.generated.js +56 -1
- package/dist/generated/operations.generated.js.map +1 -1
- package/dist/{guards.generated-M3VAIevI.d.cts → guards.generated-CDCgYGZo.d.cts} +8 -3
- package/dist/{guards.generated-BPLFwe7y.d.ts → guards.generated-CdYcs4h0.d.ts} +8 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/authoring/FlowBuilderCore.ts +4 -1
- package/src/examples/aaveBorrow.ts +129 -0
- package/src/examples/aaveHealthFactorExplode.ts +63 -0
- package/src/examples/morphoBlueBorrow.ts +94 -0
- package/src/examples/morphoBlueRepay.ts +133 -0
- package/src/examples/run.ts +43 -0
- package/src/examples/uniswapV2AddLiquidity.ts +106 -0
- package/src/examples/uniswapV3Mint.ts +95 -0
- package/src/generated/config.generated.ts +82 -38
- package/src/generated/guards.generated.ts +9 -0
- package/src/generated/operations.generated.ts +292 -0
- package/src/index.ts +0 -3
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
|
|
11
|
+
import { API_KEY, BASE_URL } from './config.js';
|
|
12
|
+
|
|
13
|
+
// Aave v3 contracts on Ethereum mainnet.
|
|
14
|
+
const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
|
|
15
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
16
|
+
// Aave v3 aEthUSDC receipt token and variable-debt USDC token.
|
|
17
|
+
const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c';
|
|
18
|
+
const VDEBT_USDC = '0x72E95b8931767C79bA4EeE721354d6E99a61D004';
|
|
19
|
+
|
|
20
|
+
// 1.05 WAD (1e18) — minimum acceptable Aave health factor after the borrow.
|
|
21
|
+
// The on-chain VM reverts the run if the post-borrow HF drops below this.
|
|
22
|
+
const MIN_HEALTH_FACTOR_WAD = '1050000000000000000';
|
|
23
|
+
|
|
24
|
+
export interface AaveBorrowInput {
|
|
25
|
+
readonly owner: Address;
|
|
26
|
+
readonly recipient: Address;
|
|
27
|
+
/** USDC supplied as collateral so the proxy can borrow (6 dp). */
|
|
28
|
+
readonly collateralAmount: `${bigint}`;
|
|
29
|
+
/** USDC borrowed against the collateral and sent to the recipient (6 dp). */
|
|
30
|
+
readonly borrowAmount: `${bigint}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Supply USDC to Aave v3, borrow USDC against it, and transfer the borrowed
|
|
35
|
+
* funds to a recipient — with an on-chain health-factor guard that reverts
|
|
36
|
+
* if the post-borrow position is too thin.
|
|
37
|
+
*
|
|
38
|
+
* The in-flow supply makes the example self-contained: the borrow is backed
|
|
39
|
+
* by collateral created in the same transaction, so it simulates cleanly
|
|
40
|
+
* for a fresh proxy. Keep `borrowAmount` comfortably below the collateral's
|
|
41
|
+
* borrowing power or the health-factor guard reverts the run.
|
|
42
|
+
*
|
|
43
|
+
* Demonstrates:
|
|
44
|
+
* - `lifi.zap` to supply USDC into Aave (USDC → aEthUSDC routing edge)
|
|
45
|
+
* - `aave.borrow` with no resource input — the borrowed asset materialises
|
|
46
|
+
* as the `borrowed` resource output.
|
|
47
|
+
* - Chaining the borrow output into a `core.transfer` to forward funds.
|
|
48
|
+
* - `aave.getHealthFactor` + `core.numericInvariant` guard asserts that
|
|
49
|
+
* `healthFactor >= MIN_HEALTH_FACTOR_WAD` after the borrow; the run reverts
|
|
50
|
+
* on-chain if the threshold is violated.
|
|
51
|
+
*
|
|
52
|
+
* The supplied aEthUSDC stays on the proxy: it backs the open debt, and
|
|
53
|
+
* Aave rejects aToken transfers that would break the position's health
|
|
54
|
+
* factor, so it cannot be swept while the borrow is outstanding.
|
|
55
|
+
*/
|
|
56
|
+
export const buildAaveBorrow = ({
|
|
57
|
+
owner,
|
|
58
|
+
recipient,
|
|
59
|
+
collateralAmount,
|
|
60
|
+
borrowAmount,
|
|
61
|
+
}: AaveBorrowInput): {
|
|
62
|
+
flow: Flow;
|
|
63
|
+
request: ComposeCompileRequest;
|
|
64
|
+
} => {
|
|
65
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
|
|
66
|
+
|
|
67
|
+
const builder = sdk.flow(1, {
|
|
68
|
+
name: 'aave-borrow-usdc',
|
|
69
|
+
inputs: {
|
|
70
|
+
collateralIn: resources.erc20(USDC, 1),
|
|
71
|
+
recipient: 'address',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Supply USDC as collateral (USDC → aEthUSDC) so the proxy can borrow.
|
|
76
|
+
builder.lifi.zap('supply', {
|
|
77
|
+
bind: { amountIn: builder.inputs.collateralIn },
|
|
78
|
+
config: { resourceOut: resources.erc20(A_ETH_USDC, 1) },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const borrowOut = builder.aave.borrow('borrow', {
|
|
82
|
+
bind: {},
|
|
83
|
+
config: {
|
|
84
|
+
pool: AAVE_V3_POOL,
|
|
85
|
+
asset: USDC,
|
|
86
|
+
variableDebtToken: VDEBT_USDC,
|
|
87
|
+
amount: borrowAmount,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Read the post-borrow Aave health factor and assert it stays above the
|
|
92
|
+
// minimum threshold. The numericInvariant guard lowers to an on-chain
|
|
93
|
+
// `AssertRawInvariant` IR1 instruction; the run reverts if HF < threshold.
|
|
94
|
+
builder.aave.getHealthFactor('hf', {
|
|
95
|
+
bind: {},
|
|
96
|
+
config: { pool: AAVE_V3_POOL },
|
|
97
|
+
guards: [
|
|
98
|
+
guards.coreNumericInvariant({
|
|
99
|
+
port: 'healthFactor',
|
|
100
|
+
op: 'gte',
|
|
101
|
+
threshold: MIN_HEALTH_FACTOR_WAD,
|
|
102
|
+
}),
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Forward the freshly borrowed USDC to the recipient.
|
|
107
|
+
builder.core.transfer('send-borrowed', {
|
|
108
|
+
bind: {
|
|
109
|
+
amount: borrowOut.borrowed,
|
|
110
|
+
recipient: builder.inputs.recipient,
|
|
111
|
+
},
|
|
112
|
+
config: {},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const flow = builder.build();
|
|
116
|
+
|
|
117
|
+
// No sweepTo: the aEthUSDC collateral must stay on the proxy while the
|
|
118
|
+
// debt is open — Aave blocks aToken transfers that would leave the
|
|
119
|
+
// position undercollateralized.
|
|
120
|
+
const request = sdk.request(flow, {
|
|
121
|
+
signer: owner,
|
|
122
|
+
inputs: {
|
|
123
|
+
collateralIn: materialisers.directDeposit({ amount: collateralAmount }),
|
|
124
|
+
recipient,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return { flow, request };
|
|
129
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
|
|
6
|
+
import { API_KEY, BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
// Base mainnet Aave v3 Pool.
|
|
9
|
+
const AAVE_V3_POOL = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5';
|
|
10
|
+
|
|
11
|
+
// 1.0 WAD (1e18) — minimum acceptable health factor.
|
|
12
|
+
const MIN_HEALTH_FACTOR_WAD = '1000000000000000000';
|
|
13
|
+
|
|
14
|
+
export interface AaveHealthFactorExplodeInput {
|
|
15
|
+
readonly signer: Address;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* EXPLODE / DecomposeValue showcase.
|
|
20
|
+
*
|
|
21
|
+
* Builds a minimal compose flow that:
|
|
22
|
+
* - StaticCalls `IPool.getUserAccountData(user)` on Aave v3 (Base)
|
|
23
|
+
* - Decomposes the returned 6-tuple via the EXPLODE opcode
|
|
24
|
+
* - Asserts `healthFactor >= 1.0 WAD` via `core.numericInvariant`
|
|
25
|
+
*
|
|
26
|
+
* The signer must hold an Aave v3 position on the target chain so HF > 0.
|
|
27
|
+
* Uses `allow-revert` so the compiled calldata is returned even if the
|
|
28
|
+
* assertion would fail — handy for tracing on a fork.
|
|
29
|
+
*/
|
|
30
|
+
export const buildAaveHealthFactorExplode = ({
|
|
31
|
+
signer,
|
|
32
|
+
}: AaveHealthFactorExplodeInput): {
|
|
33
|
+
flow: Flow;
|
|
34
|
+
request: ComposeCompileRequest;
|
|
35
|
+
} => {
|
|
36
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
|
|
37
|
+
|
|
38
|
+
const builder = sdk.flow(8453 /* Base */, {
|
|
39
|
+
name: 'aave-hf-explode-showcase',
|
|
40
|
+
inputs: {},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
builder.aave.getHealthFactor('checkHf', {
|
|
44
|
+
bind: {},
|
|
45
|
+
config: { pool: AAVE_V3_POOL, user: signer },
|
|
46
|
+
guards: [
|
|
47
|
+
guards.coreNumericInvariant({
|
|
48
|
+
port: 'healthFactor',
|
|
49
|
+
op: 'gte',
|
|
50
|
+
threshold: MIN_HEALTH_FACTOR_WAD,
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const flow = builder.build();
|
|
56
|
+
const request = sdk.request(flow, {
|
|
57
|
+
signer,
|
|
58
|
+
inputs: {},
|
|
59
|
+
simulationPolicy: 'allow-revert',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return { flow, request };
|
|
63
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
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 { API_KEY, BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
// Morpho Blue WETH/USDC market on Base (chainId 8453).
|
|
9
|
+
// The Morpho singleton address (0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb)
|
|
10
|
+
// is the default `morpho` config field and is read from compose-builtins.
|
|
11
|
+
const ADAPTIVE_CURVE_IRM_BASE = '0x46415998764C29aB2a25CbeA6254146D50D22687';
|
|
12
|
+
const BASE_USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
13
|
+
const BASE_WETH = '0x4200000000000000000000000000000000000006';
|
|
14
|
+
const WETH_USDC_ORACLE = '0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4';
|
|
15
|
+
const WETH_USDC_LLTV = '860000000000000000'; // 0.86e18
|
|
16
|
+
|
|
17
|
+
const WETH_USDC_MARKET = {
|
|
18
|
+
loanToken: BASE_USDC,
|
|
19
|
+
collateralToken: BASE_WETH,
|
|
20
|
+
oracle: WETH_USDC_ORACLE,
|
|
21
|
+
irm: ADAPTIVE_CURVE_IRM_BASE,
|
|
22
|
+
lltv: WETH_USDC_LLTV,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export interface MorphoBlueBorrowInput {
|
|
26
|
+
readonly owner: Address;
|
|
27
|
+
readonly collateralAmount: `${bigint}`;
|
|
28
|
+
readonly borrowAmount: `${bigint}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Open a leveraged-borrow position on the Base Morpho Blue WETH/USDC market.
|
|
33
|
+
*
|
|
34
|
+
* - `morphoBlue.supplyCollateral` debits the proxy's WETH and credits
|
|
35
|
+
* `Morpho.position(WETH_USDC_MARKET_ID, proxy).collateral`. There is no
|
|
36
|
+
* receipt token — collateral is non-transferable accounting state.
|
|
37
|
+
* - `morphoBlue.borrow` mints debt shares against that collateral and
|
|
38
|
+
* transfers USDC to the proxy. The borrowed asset is exposed as the
|
|
39
|
+
* `borrowed` Resource port for downstream nodes.
|
|
40
|
+
* - The flow sweeps the borrowed USDC back to the signer's wallet via
|
|
41
|
+
* `sweepTo` on the request.
|
|
42
|
+
*/
|
|
43
|
+
export const buildMorphoBlueBorrow = ({
|
|
44
|
+
owner,
|
|
45
|
+
collateralAmount,
|
|
46
|
+
borrowAmount,
|
|
47
|
+
}: MorphoBlueBorrowInput): {
|
|
48
|
+
flow: Flow;
|
|
49
|
+
request: ComposeCompileRequest;
|
|
50
|
+
} => {
|
|
51
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
|
|
52
|
+
|
|
53
|
+
const builder = sdk.flow(8453, {
|
|
54
|
+
name: 'morpho-blue-borrow-weth-usdc',
|
|
55
|
+
inputs: {
|
|
56
|
+
assetIn: resources.erc20(BASE_WETH, 8453),
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
builder.morphoBlue.supplyCollateral('supply-collateral', {
|
|
61
|
+
bind: {
|
|
62
|
+
assetIn: builder.inputs.assetIn,
|
|
63
|
+
},
|
|
64
|
+
config: {
|
|
65
|
+
marketParams: WETH_USDC_MARKET,
|
|
66
|
+
mode: 'exact',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
builder.morphoBlue.borrow('borrow', {
|
|
71
|
+
bind: {},
|
|
72
|
+
config: {
|
|
73
|
+
marketParams: WETH_USDC_MARKET,
|
|
74
|
+
amount: borrowAmount,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const flow = builder.build();
|
|
79
|
+
|
|
80
|
+
const request = sdk.request(flow, {
|
|
81
|
+
signer: owner,
|
|
82
|
+
inputs: {
|
|
83
|
+
assetIn: materialisers.directDeposit({ amount: collateralAmount }),
|
|
84
|
+
},
|
|
85
|
+
// Sweep the borrowed USDC back to the signer.
|
|
86
|
+
sweepTo: builder.context.sender,
|
|
87
|
+
// The WETH collateral becomes unpriced Morpho position state (no receipt
|
|
88
|
+
// token), so the default 500 bps price-impact check reads any safe borrow
|
|
89
|
+
// (LLTV caps at 86%) as a loss. 0 skips the check.
|
|
90
|
+
maxPriceImpactBps: 0,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return { flow, request };
|
|
94
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
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 { API_KEY, BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
// Morpho Blue WETH/USDC market on Base (chainId 8453).
|
|
9
|
+
const ADAPTIVE_CURVE_IRM_BASE = '0x46415998764C29aB2a25CbeA6254146D50D22687';
|
|
10
|
+
const BASE_USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
11
|
+
const BASE_WETH = '0x4200000000000000000000000000000000000006';
|
|
12
|
+
const WETH_USDC_ORACLE = '0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4';
|
|
13
|
+
const WETH_USDC_LLTV = '860000000000000000';
|
|
14
|
+
|
|
15
|
+
const WETH_USDC_MARKET = {
|
|
16
|
+
loanToken: BASE_USDC,
|
|
17
|
+
collateralToken: BASE_WETH,
|
|
18
|
+
oracle: WETH_USDC_ORACLE,
|
|
19
|
+
irm: ADAPTIVE_CURVE_IRM_BASE,
|
|
20
|
+
lltv: WETH_USDC_LLTV,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface MorphoBlueRepayInput {
|
|
24
|
+
readonly owner: Address;
|
|
25
|
+
/** WETH supplied as collateral for the in-flow borrow (18 dp). */
|
|
26
|
+
readonly collateralAmount: `${bigint}`;
|
|
27
|
+
/** USDC borrowed against the collateral, then repaid by this flow (6 dp). */
|
|
28
|
+
readonly borrowAmount: `${bigint}`;
|
|
29
|
+
/**
|
|
30
|
+
* USDC deposited to repay the debt (6 dp). Must exceed `borrowAmount`:
|
|
31
|
+
* Morpho's share rounding can leave the debt a wei above the borrowed
|
|
32
|
+
* amount, so the `max`-mode repay needs a little headroom. The leftover
|
|
33
|
+
* surfaces on the `residual` port.
|
|
34
|
+
*/
|
|
35
|
+
readonly repayAmount: `${bigint}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Open and close a Morpho Blue WETH/USDC position on Base in one flow —
|
|
40
|
+
* a self-contained supplyCollateral → borrow → repay → withdrawCollateral
|
|
41
|
+
* round trip that creates the debt it retires.
|
|
42
|
+
*
|
|
43
|
+
* Demonstrates:
|
|
44
|
+
* - `morphoBlue.supplyCollateral` to post WETH (collateral is internal
|
|
45
|
+
* position state — no receipt token).
|
|
46
|
+
* - `morphoBlue.borrow` to mint USDC debt against it.
|
|
47
|
+
* - `morphoBlue.repay` with `mode: 'max'`, which reads the proxy's current
|
|
48
|
+
* `borrowShares` on-chain and repays in shares mode to retire the debt to
|
|
49
|
+
* the wei. Unused USDC surfaces on the `residual` Resource port.
|
|
50
|
+
* - `morphoBlue.withdrawCollateral` to pull the freed WETH once the debt
|
|
51
|
+
* is cleared.
|
|
52
|
+
* - `sweepTo` to return the borrowed USDC, the repay residual, and the
|
|
53
|
+
* freed WETH to the signer.
|
|
54
|
+
*/
|
|
55
|
+
export const buildMorphoBlueRepay = ({
|
|
56
|
+
owner,
|
|
57
|
+
collateralAmount,
|
|
58
|
+
borrowAmount,
|
|
59
|
+
repayAmount,
|
|
60
|
+
}: MorphoBlueRepayInput): {
|
|
61
|
+
flow: Flow;
|
|
62
|
+
request: ComposeCompileRequest;
|
|
63
|
+
} => {
|
|
64
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
|
|
65
|
+
|
|
66
|
+
const builder = sdk.flow(8453, {
|
|
67
|
+
name: 'morpho-blue-repay-usdc-withdraw-weth',
|
|
68
|
+
inputs: {
|
|
69
|
+
collateralIn: resources.erc20(BASE_WETH, 8453),
|
|
70
|
+
repayIn: resources.erc20(BASE_USDC, 8453),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Post WETH collateral so the proxy can open the debt it will repay.
|
|
75
|
+
builder.morphoBlue.supplyCollateral('supply-collateral', {
|
|
76
|
+
bind: {
|
|
77
|
+
assetIn: builder.inputs.collateralIn,
|
|
78
|
+
},
|
|
79
|
+
config: {
|
|
80
|
+
marketParams: WETH_USDC_MARKET,
|
|
81
|
+
mode: 'exact',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Borrow USDC against the collateral; the borrowed USDC stays on the
|
|
86
|
+
// proxy as a terminal resource and is swept back to the signer.
|
|
87
|
+
builder.morphoBlue.borrow('borrow', {
|
|
88
|
+
bind: {},
|
|
89
|
+
config: {
|
|
90
|
+
marketParams: WETH_USDC_MARKET,
|
|
91
|
+
amount: borrowAmount,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Retire the just-opened debt from the deposited USDC. mode 'max' reads
|
|
96
|
+
// the live borrowShares and repays in shares mode, clearing the debt to
|
|
97
|
+
// the wei; the unused USDC surfaces on the `residual` port.
|
|
98
|
+
builder.morphoBlue.repay('repay', {
|
|
99
|
+
bind: {
|
|
100
|
+
assetIn: builder.inputs.repayIn,
|
|
101
|
+
onBehalfOf: builder.context.executionAddress,
|
|
102
|
+
},
|
|
103
|
+
config: {
|
|
104
|
+
marketParams: WETH_USDC_MARKET,
|
|
105
|
+
mode: 'max',
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// The debt is cleared, so the full collateral can be withdrawn. The freed
|
|
110
|
+
// WETH lands on the proxy and is swept to the signer.
|
|
111
|
+
builder.morphoBlue.withdrawCollateral('withdraw-collateral', {
|
|
112
|
+
bind: {},
|
|
113
|
+
config: {
|
|
114
|
+
marketParams: WETH_USDC_MARKET,
|
|
115
|
+
amount: collateralAmount,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const flow = builder.build();
|
|
120
|
+
|
|
121
|
+
const request = sdk.request(flow, {
|
|
122
|
+
signer: owner,
|
|
123
|
+
inputs: {
|
|
124
|
+
collateralIn: materialisers.directDeposit({ amount: collateralAmount }),
|
|
125
|
+
repayIn: materialisers.directDeposit({ amount: repayAmount }),
|
|
126
|
+
},
|
|
127
|
+
// Sweep the borrowed USDC, the repay residual, and the freed WETH back
|
|
128
|
+
// to the signer.
|
|
129
|
+
sweepTo: builder.context.sender,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return { flow, request };
|
|
133
|
+
};
|
package/src/examples/run.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* LIFI_API_KEY=your-key COMPOSER_BASE_URL=https://composer.li.quest yarn example <name>
|
|
9
9
|
*
|
|
10
10
|
* Available examples:
|
|
11
|
+
* aave-borrow — supply USDC to Aave v3, borrow against it with a health-factor guard
|
|
12
|
+
* aave-hf-explode — read the Aave v3 health factor and assert it stays >= 1.0
|
|
11
13
|
* aave-repay — repay an Aave v3 WETH debt by supplying WETH
|
|
12
14
|
* aave-repay-atoken — repay an Aave v3 USDC debt by burning proxy aTokens
|
|
13
15
|
* aave-emode — switch the proxy's Aave v3 eMode category
|
|
@@ -21,6 +23,8 @@
|
|
|
21
23
|
* split-arithmetic — split USDC 70/30 with arithmetic assertions
|
|
22
24
|
* zap — zap USDC into Aave lending position
|
|
23
25
|
* zap-async — zap USDC into an async Aave position (future output)
|
|
26
|
+
* morpho-borrow — supply WETH collateral to Morpho Blue and borrow USDC
|
|
27
|
+
* morpho-repay — open, repay and close a Morpho Blue WETH/USDC position
|
|
24
28
|
* fly-swap — exit a Pendle PT position and swap ALL proceeds to WETH
|
|
25
29
|
* swap-zap — swap WETH to USDC then zap into Aave
|
|
26
30
|
* swap-check — swap WETH to USDC with balance check
|
|
@@ -34,6 +38,8 @@
|
|
|
34
38
|
* transfer — transfer full token balance to a recipient
|
|
35
39
|
* partial-transfer — transfer a specific amount, keeping the remainder
|
|
36
40
|
* untyped-ref — mix untypedOp with typed handles via raw.ref
|
|
41
|
+
* uniswap-v2-add — split USDC, swap half to WETH, add both to the Uniswap V2 pair
|
|
42
|
+
* uniswap-v3-mint — open a full-range Uniswap V3 USDC/WETH position
|
|
37
43
|
* route-exact — wrap 0.1 native ETH into WETH via /compose/route
|
|
38
44
|
* route-all — unwrap the whole WETH balance to ETH via /compose/route
|
|
39
45
|
* supported-chains — list the chains this deployment supports, and guard on them
|
|
@@ -46,6 +52,8 @@ import type {
|
|
|
46
52
|
import { bigintReplacer } from '../client.js';
|
|
47
53
|
import { type ComposeSdk, createComposeSdk } from '../sdk.js';
|
|
48
54
|
|
|
55
|
+
import { buildAaveBorrow } from './aaveBorrow.js';
|
|
56
|
+
import { buildAaveHealthFactorExplode } from './aaveHealthFactorExplode.js';
|
|
49
57
|
import { buildAaveRepay } from './aaveRepay.js';
|
|
50
58
|
import { buildAaveRepayWithATokens } from './aaveRepayWithATokens.js';
|
|
51
59
|
import { buildAaveSetEMode } from './aaveSetEMode.js';
|
|
@@ -60,6 +68,8 @@ import { buildFlySwapExample } from './flySwap.js';
|
|
|
60
68
|
import { buildLifiSwapExample } from './lifiSwap.js';
|
|
61
69
|
import { buildLifiZapExample } from './lifiZap.js';
|
|
62
70
|
import { buildLifiZapAsyncExample } from './lifiZapAsync.js';
|
|
71
|
+
import { buildMorphoBlueBorrow } from './morphoBlueBorrow.js';
|
|
72
|
+
import { buildMorphoBlueRepay } from './morphoBlueRepay.js';
|
|
63
73
|
import { buildRawCallWithArithmetic } from './rawCallWithArithmetic.js';
|
|
64
74
|
import { buildReadContractState } from './readContractState.js';
|
|
65
75
|
import {
|
|
@@ -75,12 +85,23 @@ import { buildSwapWithAllowRevertExample } from './swapWithAllowRevert.js';
|
|
|
75
85
|
import { buildSwapWithBalanceCheck } from './swapWithBalanceCheck.js';
|
|
76
86
|
import { buildSwapWithOutputValidation } from './swapWithOutputValidation.js';
|
|
77
87
|
import { buildPartialTransfer, buildTransferTokens } from './transferTokens.js';
|
|
88
|
+
import { buildUniswapV2AddLiquidity } from './uniswapV2AddLiquidity.js';
|
|
89
|
+
import { buildUniswapV3Mint } from './uniswapV3Mint.js';
|
|
78
90
|
import { buildUntypedOpWithTypedRef } from './untypedOpWithTypedRef.js';
|
|
79
91
|
|
|
80
92
|
const stringifyJson = (value: unknown) =>
|
|
81
93
|
JSON.stringify(value, bigintReplacer, 2);
|
|
82
94
|
|
|
83
95
|
const EXAMPLES: Record<string, () => ComposeCompileRequest> = {
|
|
96
|
+
'aave-borrow': () =>
|
|
97
|
+
buildAaveBorrow({
|
|
98
|
+
owner: OWNER,
|
|
99
|
+
recipient: RECIPIENT,
|
|
100
|
+
collateralAmount: '1000000000',
|
|
101
|
+
borrowAmount: '500000000',
|
|
102
|
+
}).request,
|
|
103
|
+
'aave-hf-explode': () =>
|
|
104
|
+
buildAaveHealthFactorExplode({ signer: OWNER }).request,
|
|
84
105
|
'aave-repay': () =>
|
|
85
106
|
buildAaveRepay({
|
|
86
107
|
owner: OWNER,
|
|
@@ -128,6 +149,19 @@ const EXAMPLES: Record<string, () => ComposeCompileRequest> = {
|
|
|
128
149
|
'zap-async': () =>
|
|
129
150
|
buildLifiZapAsyncExample({ owner: OWNER, recipient: RECIPIENT }).request,
|
|
130
151
|
'fly-swap': () => buildFlySwapExample().request,
|
|
152
|
+
'morpho-borrow': () =>
|
|
153
|
+
buildMorphoBlueBorrow({
|
|
154
|
+
owner: OWNER,
|
|
155
|
+
collateralAmount: '1000000000000000000',
|
|
156
|
+
borrowAmount: '1200000000',
|
|
157
|
+
}).request,
|
|
158
|
+
'morpho-repay': () =>
|
|
159
|
+
buildMorphoBlueRepay({
|
|
160
|
+
owner: OWNER,
|
|
161
|
+
collateralAmount: '1000000000000000000',
|
|
162
|
+
borrowAmount: '500000000',
|
|
163
|
+
repayAmount: '501000000',
|
|
164
|
+
}).request,
|
|
131
165
|
'swap-zap': () => buildSwapAndZapExample().request,
|
|
132
166
|
'swap-check': () =>
|
|
133
167
|
buildSwapWithBalanceCheck({ owner: OWNER, amount: '1000000000000000000' })
|
|
@@ -166,6 +200,15 @@ const EXAMPLES: Record<string, () => ComposeCompileRequest> = {
|
|
|
166
200
|
amount: '1000000000',
|
|
167
201
|
}).request,
|
|
168
202
|
'untyped-ref': () => buildUntypedOpWithTypedRef({ owner: OWNER }).request,
|
|
203
|
+
'uniswap-v2-add': () =>
|
|
204
|
+
buildUniswapV2AddLiquidity({ owner: OWNER, usdcAmount: '1000000000' })
|
|
205
|
+
.request,
|
|
206
|
+
'uniswap-v3-mint': () =>
|
|
207
|
+
buildUniswapV3Mint({
|
|
208
|
+
owner: OWNER,
|
|
209
|
+
usdcAmount: '1000000000',
|
|
210
|
+
wethAmount: '400000000000000000',
|
|
211
|
+
}).request,
|
|
169
212
|
};
|
|
170
213
|
|
|
171
214
|
// `/compose/route` examples build a `ComposeRouteRequest` and go to
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
|
|
11
|
+
import { API_KEY, BASE_URL } from './config.js';
|
|
12
|
+
|
|
13
|
+
// Ethereum mainnet (chainId 1).
|
|
14
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
15
|
+
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
|
16
|
+
const UNISWAP_V2_ROUTER = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D';
|
|
17
|
+
const UNISWAP_V2_FACTORY = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f';
|
|
18
|
+
const USDC_WETH_PAIR = '0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc';
|
|
19
|
+
|
|
20
|
+
// Tolerance of the swap leg: the aggregator's `toAmountMin` lets the swap
|
|
21
|
+
// deliver up to 3% less WETH than quoted.
|
|
22
|
+
const SWAP_SLIPPAGE = 0.03;
|
|
23
|
+
|
|
24
|
+
export interface UniswapV2AddLiquidityInput {
|
|
25
|
+
readonly owner: Address;
|
|
26
|
+
readonly usdcAmount: `${bigint}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Deposit USDC alone into the Uniswap V2 USDC/WETH pair.
|
|
31
|
+
*
|
|
32
|
+
* - `core.split` halves the USDC input.
|
|
33
|
+
* - `lifi.swap` turns one half into WETH.
|
|
34
|
+
* - `uniswapV2.addLiquidity` deposits the other USDC half and the swapped
|
|
35
|
+
* WETH, minting the UNI-V2 LP token. Unused input comes back as residuals.
|
|
36
|
+
* - The request sweeps the LP token and the residuals to the signer.
|
|
37
|
+
*/
|
|
38
|
+
export const buildUniswapV2AddLiquidity = ({
|
|
39
|
+
owner,
|
|
40
|
+
usdcAmount,
|
|
41
|
+
}: UniswapV2AddLiquidityInput): {
|
|
42
|
+
flow: Flow;
|
|
43
|
+
request: ComposeCompileRequest;
|
|
44
|
+
} => {
|
|
45
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL, apiKey: API_KEY });
|
|
46
|
+
|
|
47
|
+
const builder = sdk.flow(1, {
|
|
48
|
+
name: 'uniswap-v2-add-liquidity-usdc-weth',
|
|
49
|
+
inputs: {
|
|
50
|
+
usdcIn: resources.erc20(USDC, 1),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const { a: usdcHalf, b: usdcToSwap } = builder.core.split('split', {
|
|
55
|
+
bind: { source: builder.inputs.usdcIn },
|
|
56
|
+
config: { bps: 5000 },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const swap = builder.lifi.swap('swap', {
|
|
60
|
+
bind: { amountIn: usdcToSwap },
|
|
61
|
+
config: {
|
|
62
|
+
resourceOut: resources.erc20(WETH, 1),
|
|
63
|
+
slippage: SWAP_SLIPPAGE,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
builder.uniswapV2.addLiquidity('add-liquidity', {
|
|
68
|
+
bind: {
|
|
69
|
+
tokenA: usdcHalf,
|
|
70
|
+
tokenB: swap.amountOut,
|
|
71
|
+
},
|
|
72
|
+
config: {
|
|
73
|
+
router: UNISWAP_V2_ROUTER,
|
|
74
|
+
factory: UNISWAP_V2_FACTORY,
|
|
75
|
+
pair: USDC_WETH_PAIR,
|
|
76
|
+
},
|
|
77
|
+
guards: [
|
|
78
|
+
// The router is called with zero minimums, so this band is the sandwich
|
|
79
|
+
// protection: it reverts unless the pool price read before the deposit
|
|
80
|
+
// stays within ±50 bps of the simulated price. It does not depend on the
|
|
81
|
+
// swap leg.
|
|
82
|
+
guards.band({ port: 'poolPrice', bps: 50 }),
|
|
83
|
+
// A swap that fills at its 3% minimum delivers less WETH, so the add
|
|
84
|
+
// mints less LP. The floor must cover that tolerance (300 bps) plus the
|
|
85
|
+
// 50 bps band, or a valid swap fill reverts the transaction.
|
|
86
|
+
guards.slippage({ port: 'liquidity', bps: 350 }),
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const flow = builder.build();
|
|
91
|
+
|
|
92
|
+
const request = sdk.request(flow, {
|
|
93
|
+
signer: owner,
|
|
94
|
+
inputs: {
|
|
95
|
+
usdcIn: materialisers.directDeposit({ amount: usdcAmount }),
|
|
96
|
+
},
|
|
97
|
+
// Sweep the LP token and the unused USDC/WETH back to the signer.
|
|
98
|
+
sweepTo: builder.context.sender,
|
|
99
|
+
// The UNI-V2 LP token is unpriced, so the default 500 bps price-impact
|
|
100
|
+
// check reads the deposit as a 100% loss. 0 skips the check; the `band`
|
|
101
|
+
// and `slippage` guards above are the protection.
|
|
102
|
+
maxPriceImpactBps: 0,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return { flow, request };
|
|
106
|
+
};
|