@w3lib/evm-batch-call 0.5.0 → 0.6.0
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/CHANGELOG.md +14 -0
- package/README.md +79 -2
- package/dist/liquidity/common.d.ts +72 -0
- package/dist/liquidity/common.d.ts.map +1 -0
- package/dist/liquidity/common.js +271 -0
- package/dist/liquidity/common.js.map +1 -0
- package/dist/liquidity/concentrated.d.ts +19 -0
- package/dist/liquidity/concentrated.d.ts.map +1 -0
- package/dist/liquidity/concentrated.js +106 -0
- package/dist/liquidity/concentrated.js.map +1 -0
- package/dist/liquidity/index.d.ts +42 -0
- package/dist/liquidity/index.d.ts.map +1 -0
- package/dist/liquidity/index.js +143 -0
- package/dist/liquidity/index.js.map +1 -0
- package/dist/liquidity/types.d.ts +182 -0
- package/dist/liquidity/types.d.ts.map +1 -0
- package/dist/liquidity/types.js +2 -0
- package/dist/liquidity/types.js.map +1 -0
- package/dist/liquidity/v2.d.ts +5 -0
- package/dist/liquidity/v2.d.ts.map +1 -0
- package/dist/liquidity/v2.js +342 -0
- package/dist/liquidity/v2.js.map +1 -0
- package/dist/liquidity/v3.d.ts +6 -0
- package/dist/liquidity/v3.d.ts.map +1 -0
- package/dist/liquidity/v3.js +310 -0
- package/dist/liquidity/v3.js.map +1 -0
- package/dist/liquidity/v4.d.ts +5 -0
- package/dist/liquidity/v4.d.ts.map +1 -0
- package/dist/liquidity/v4.js +279 -0
- package/dist/liquidity/v4.js.map +1 -0
- package/dist/lp.d.ts +8 -1
- package/dist/lp.d.ts.map +1 -1
- package/dist/lp.js +29 -18
- package/dist/lp.js.map +1 -1
- package/dist/lpMath.d.ts +55 -0
- package/dist/lpMath.d.ts.map +1 -1
- package/dist/lpMath.js +209 -0
- package/dist/lpMath.js.map +1 -1
- package/dist/subpaths/liquidity.d.ts +24 -0
- package/dist/subpaths/liquidity.d.ts.map +1 -0
- package/dist/subpaths/liquidity.js +21 -0
- package/dist/subpaths/liquidity.js.map +1 -0
- package/dist/subpaths/lp.d.ts +1 -1
- package/dist/subpaths/lp.d.ts.map +1 -1
- package/dist/subpaths/lp.js +1 -1
- package/dist/subpaths/lp.js.map +1 -1
- package/dist/write/tron.d.ts +2 -0
- package/dist/write/tron.d.ts.map +1 -1
- package/dist/write/tron.js +4 -0
- package/dist/write/tron.js.map +1 -1
- package/package.json +20 -12
- package/src/liquidity/common.ts +314 -0
- package/src/liquidity/concentrated.ts +121 -0
- package/src/liquidity/index.ts +171 -0
- package/src/liquidity/types.ts +194 -0
- package/src/liquidity/v2.ts +371 -0
- package/src/liquidity/v3.ts +382 -0
- package/src/liquidity/v4.ts +348 -0
- package/src/lp.ts +36 -18
- package/src/lpMath.ts +223 -0
- package/src/subpaths/liquidity.ts +52 -0
- package/src/subpaths/lp.ts +20 -1
- package/src/write/tron.ts +5 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { Interface } from 'ethers';
|
|
2
|
+
import { getAmountsForLiquidity, getFeeGrowthInside, getFeesEarned, getMinAmounts, getMintAmounts, getSqrtRatioAtTick, getTickAtSqrtRatio } from '../lpMath.js';
|
|
3
|
+
import { liquidityFor, resolveRange, sqrtPriceOf, v3MinAmounts } from './concentrated.js';
|
|
4
|
+
import { MAX_UINT128, ZERO_ADDRESS, approvals, concentratedProtocol, evm, isNative, makePlan, out, parseAmount, priceFields, removeAmount, tokenIdOf, tokenInfo, } from './common.js';
|
|
5
|
+
const npmInterface = new Interface([
|
|
6
|
+
'function WETH9() view returns (address)',
|
|
7
|
+
'function factory() view returns (address)',
|
|
8
|
+
'function ownerOf(uint256 tokenId) view returns (address)',
|
|
9
|
+
'function isApprovedForAll(address owner, address operator) view returns (bool)',
|
|
10
|
+
'function positions(uint256 tokenId) view returns (uint96 nonce, address operator, address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1)',
|
|
11
|
+
'function mint((address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline) params) payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)',
|
|
12
|
+
'function increaseLiquidity((uint256 tokenId, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, uint256 deadline) params) payable returns (uint128 liquidity, uint256 amount0, uint256 amount1)',
|
|
13
|
+
'function decreaseLiquidity((uint256 tokenId, uint128 liquidity, uint256 amount0Min, uint256 amount1Min, uint256 deadline) params) payable returns (uint256 amount0, uint256 amount1)',
|
|
14
|
+
'function collect((uint256 tokenId, address recipient, uint128 amount0Max, uint128 amount1Max) params) payable returns (uint256 amount0, uint256 amount1)',
|
|
15
|
+
'function burn(uint256 tokenId) payable',
|
|
16
|
+
'function multicall(bytes[] data) payable returns (bytes[] results)',
|
|
17
|
+
'function refundETH() payable',
|
|
18
|
+
'function unwrapWETH9(uint256 amountMinimum, address recipient) payable',
|
|
19
|
+
'function sweepToken(address token, uint256 amountMinimum, address recipient) payable',
|
|
20
|
+
'function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96) payable returns (address pool)',
|
|
21
|
+
]);
|
|
22
|
+
const factoryInterface = new Interface([
|
|
23
|
+
'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address)',
|
|
24
|
+
'function feeAmountTickSpacing(uint24 fee) view returns (int24)',
|
|
25
|
+
]);
|
|
26
|
+
// 只声明前两个返回值:各分叉 slot0 后面的字段类型不同
|
|
27
|
+
const poolInterface = new Interface([
|
|
28
|
+
'function slot0() view returns (uint160 sqrtPriceX96, int24 tick)',
|
|
29
|
+
'function tickSpacing() view returns (int24)',
|
|
30
|
+
'function feeGrowthGlobal0X128() view returns (uint256)',
|
|
31
|
+
'function feeGrowthGlobal1X128() view returns (uint256)',
|
|
32
|
+
'function ticks(int24 tick) view returns (uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128)',
|
|
33
|
+
]);
|
|
34
|
+
const U128_MASK = MAX_UINT128;
|
|
35
|
+
async function factoryOf(ctx, protocol) {
|
|
36
|
+
if (protocol.factory) {
|
|
37
|
+
return evm(protocol.factory);
|
|
38
|
+
}
|
|
39
|
+
return evm((await ctx.provider.call(ctx.provider.contract(protocol.positionManager, npmInterface).factory())));
|
|
40
|
+
}
|
|
41
|
+
const wethOf = async (ctx, npm) => evm((await ctx.provider.call(ctx.provider.contract(npm, npmInterface).WETH9())));
|
|
42
|
+
/** 读已有的仓位:仓位信息、池子状态、未领取的手续费 */
|
|
43
|
+
async function readPosition(ctx, ref) {
|
|
44
|
+
const protocol = concentratedProtocol(ctx, ref);
|
|
45
|
+
const npm = evm(protocol.positionManager);
|
|
46
|
+
const tokenId = tokenIdOf(ref);
|
|
47
|
+
const c = ctx.provider.contract(npm, npmInterface);
|
|
48
|
+
const [factory, [owner, p]] = await Promise.all([
|
|
49
|
+
factoryOf(ctx, protocol),
|
|
50
|
+
ctx.provider.all([c.ownerOf(tokenId), c.positions(tokenId)]),
|
|
51
|
+
]);
|
|
52
|
+
const pool = evm((await ctx.provider.call(ctx.provider.contract(factory, factoryInterface).getPool(p.token0, p.token1, p.fee))));
|
|
53
|
+
if (pool === ZERO_ADDRESS) {
|
|
54
|
+
throw new Error(`Cannot find the pool of position ${tokenId}`);
|
|
55
|
+
}
|
|
56
|
+
const tickLower = Number(p.tickLower);
|
|
57
|
+
const tickUpper = Number(p.tickUpper);
|
|
58
|
+
const pc = ctx.provider.contract(pool, poolInterface);
|
|
59
|
+
const [slot0, spacing, global0, global1, lower, upper, approvedForAll] = (await ctx.provider.all([
|
|
60
|
+
pc.slot0(),
|
|
61
|
+
pc.tickSpacing(),
|
|
62
|
+
pc.feeGrowthGlobal0X128(),
|
|
63
|
+
pc.feeGrowthGlobal1X128(),
|
|
64
|
+
pc.ticks(tickLower),
|
|
65
|
+
pc.ticks(tickUpper),
|
|
66
|
+
c.isApprovedForAll(owner, ctx.owner),
|
|
67
|
+
]));
|
|
68
|
+
const tick = Number(slot0[1]);
|
|
69
|
+
const inside0 = getFeeGrowthInside(tick, tickLower, tickUpper, global0, lower[2], upper[2]);
|
|
70
|
+
const inside1 = getFeeGrowthInside(tick, tickLower, tickUpper, global1, lower[3], upper[3]);
|
|
71
|
+
return {
|
|
72
|
+
protocol,
|
|
73
|
+
npm,
|
|
74
|
+
tokenId,
|
|
75
|
+
owner: evm(owner),
|
|
76
|
+
operator: evm(p.operator),
|
|
77
|
+
approvedForAll,
|
|
78
|
+
token0: evm(p.token0),
|
|
79
|
+
token1: evm(p.token1),
|
|
80
|
+
fee: Number(p.fee),
|
|
81
|
+
tickLower,
|
|
82
|
+
tickUpper,
|
|
83
|
+
liquidity: p.liquidity,
|
|
84
|
+
pool: { address: pool, sqrtPriceX96: slot0[0], tick, tickSpacing: Number(spacing) },
|
|
85
|
+
// 与仓位合约一致:tokensOwed += uint128(新增),按 uint128 回绕
|
|
86
|
+
fees0: (p.tokensOwed0 + (getFeesEarned(p.liquidity, inside0, p.feeGrowthInside0LastX128) & U128_MASK)) & U128_MASK,
|
|
87
|
+
fees1: (p.tokensOwed1 + (getFeesEarned(p.liquidity, inside1, p.feeGrowthInside1LastX128) & U128_MASK)) & U128_MASK,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** 追加 / 移除 / 领取需要是仓位的所有者,或被授权的 operator(单个仓位的 approve 或 setApprovalForAll),与合约一致 */
|
|
91
|
+
function assertCanManage(ctx, position) {
|
|
92
|
+
if (position.owner !== ctx.owner && position.operator !== ctx.owner && !position.approvedForAll) {
|
|
93
|
+
throw new Error(`${out(ctx, ctx.owner)} is not the owner of position ${position.tokenId} (owner: ${out(ctx, position.owner)})`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** 包装币那一边用主币时:确认池子里有包装币 */
|
|
97
|
+
async function nativeWeth(ctx, npm, token0, token1) {
|
|
98
|
+
if (!ctx.options.native) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const weth = await wethOf(ctx, npm);
|
|
102
|
+
if (weth !== token0 && weth !== token1) {
|
|
103
|
+
throw new Error('native: true needs one side of the pool to be the wrapped native token');
|
|
104
|
+
}
|
|
105
|
+
return weth;
|
|
106
|
+
}
|
|
107
|
+
const call = (name, args) => npmInterface.encodeFunctionData(name, args);
|
|
108
|
+
/** 一个调用直接发,多个用仓位合约的 multicall 合成一笔 */
|
|
109
|
+
const wrap = (calls) => (calls.length === 1 ? calls[0] : npmInterface.encodeFunctionData('multicall', [calls]));
|
|
110
|
+
export async function v3Add(ctx, params) {
|
|
111
|
+
let protocol;
|
|
112
|
+
let npm;
|
|
113
|
+
let token0;
|
|
114
|
+
let token1;
|
|
115
|
+
let fee;
|
|
116
|
+
let pool;
|
|
117
|
+
let tickLower;
|
|
118
|
+
let tickUpper;
|
|
119
|
+
let tokenId = null;
|
|
120
|
+
let createPrice = null;
|
|
121
|
+
let weth;
|
|
122
|
+
let t0;
|
|
123
|
+
let t1;
|
|
124
|
+
let given0;
|
|
125
|
+
let given1;
|
|
126
|
+
if ('position' in params) {
|
|
127
|
+
const position = await readPosition(ctx, params.position);
|
|
128
|
+
// 合约允许任何人往别人的仓位里加流动性:tokenId 写错时代币会白送给别人,这里只允许所有者 / operator
|
|
129
|
+
assertCanManage(ctx, position);
|
|
130
|
+
({ protocol, npm, token0, token1, fee, pool, tickLower, tickUpper } = position);
|
|
131
|
+
tokenId = position.tokenId;
|
|
132
|
+
weth = await nativeWeth(ctx, npm, token0, token1);
|
|
133
|
+
[t0, t1] = (await tokenInfo(ctx, [token0, token1]));
|
|
134
|
+
given0 = parseAmount(params.amount0, params.amount0Formatted, t0, 'amount0');
|
|
135
|
+
given1 = parseAmount(params.amount1, params.amount1Formatted, t1, 'amount1');
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
protocol = params.protocol;
|
|
139
|
+
npm = evm(protocol.positionManager);
|
|
140
|
+
if (params.fee === undefined) {
|
|
141
|
+
throw new Error('fee is required for V3 (e.g. 500, 3000, 10000)');
|
|
142
|
+
}
|
|
143
|
+
fee = params.fee;
|
|
144
|
+
let a = evm(params.tokenA);
|
|
145
|
+
let b = evm(params.tokenB);
|
|
146
|
+
weth = null;
|
|
147
|
+
if (isNative(ctx, a) || isNative(ctx, b) || ctx.options.native) {
|
|
148
|
+
weth = await wethOf(ctx, npm);
|
|
149
|
+
a = isNative(ctx, a) ? weth : a;
|
|
150
|
+
b = isNative(ctx, b) ? weth : b;
|
|
151
|
+
if (a !== weth && b !== weth) {
|
|
152
|
+
throw new Error('native: true needs one side of the pool to be the wrapped native token');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (a === b) {
|
|
156
|
+
throw new Error('tokenA and tokenB must be different');
|
|
157
|
+
}
|
|
158
|
+
const aIs0 = BigInt(a) < BigInt(b);
|
|
159
|
+
[token0, token1] = aIs0 ? [a, b] : [b, a];
|
|
160
|
+
const factory = await factoryOf(ctx, protocol);
|
|
161
|
+
const f = ctx.provider.contract(factory, factoryInterface);
|
|
162
|
+
const [address, defaultSpacing] = (await ctx.provider.all([f.getPool(token0, token1, fee), f.feeAmountTickSpacing(fee)]));
|
|
163
|
+
pool = { address: evm(address) === ZERO_ADDRESS ? null : evm(address), sqrtPriceX96: 0n, tick: 0, tickSpacing: Number(defaultSpacing) };
|
|
164
|
+
if (pool.address) {
|
|
165
|
+
const pc = ctx.provider.contract(pool.address, poolInterface);
|
|
166
|
+
const [slot0, spacing] = (await ctx.provider.all([pc.slot0(), pc.tickSpacing()]));
|
|
167
|
+
pool = { ...pool, sqrtPriceX96: slot0[0], tick: Number(slot0[1]), tickSpacing: Number(spacing) };
|
|
168
|
+
}
|
|
169
|
+
else if (pool.tickSpacing <= 0) {
|
|
170
|
+
throw new Error(`Fee tier ${fee} is not enabled on ${protocol.id}`);
|
|
171
|
+
}
|
|
172
|
+
;
|
|
173
|
+
[t0, t1] = (await tokenInfo(ctx, [token0, token1]));
|
|
174
|
+
if (pool.sqrtPriceX96 === 0n) {
|
|
175
|
+
// 池子不存在或还没初始化:用 initialPrice 创建
|
|
176
|
+
if (params.initialPrice === undefined) {
|
|
177
|
+
throw new Error(`The ${protocol.id} pool for this pair and fee ${fee} does not exist yet; pass initialPrice to create it`);
|
|
178
|
+
}
|
|
179
|
+
createPrice = sqrtPriceOf(params.initialPrice, aIs0, t0, t1, 'initialPrice');
|
|
180
|
+
pool = { ...pool, sqrtPriceX96: createPrice, tick: getTickAtSqrtRatio(createPrice) };
|
|
181
|
+
}
|
|
182
|
+
;
|
|
183
|
+
[tickLower, tickUpper] = resolveRange(params, aIs0, t0, t1, pool.tickSpacing);
|
|
184
|
+
const amountA = parseAmount(params.amountA, params.amountAFormatted, aIs0 ? t0 : t1, 'amountA');
|
|
185
|
+
const amountB = parseAmount(params.amountB, params.amountBFormatted, aIs0 ? t1 : t0, 'amountB');
|
|
186
|
+
[given0, given1] = aIs0 ? [amountA, amountB] : [amountB, amountA];
|
|
187
|
+
}
|
|
188
|
+
const liquidity = liquidityFor(pool.sqrtPriceX96, tickLower, tickUpper, given0, given1, t0, t1);
|
|
189
|
+
const [amount0, amount1] = getMintAmounts(pool.sqrtPriceX96, getSqrtRatioAtTick(tickLower), getSqrtRatioAtTick(tickUpper), liquidity);
|
|
190
|
+
// 传给合约的数量:给了的按给的,另一边按算出来的(合约按两边较少的算流动性,结果就是上面的 liquidity)。
|
|
191
|
+
// 价格在区间外时用不到的一边为 0(不用授权 / 附带主币)
|
|
192
|
+
const desired0 = amount0 === 0n ? 0n : (given0 ?? amount0);
|
|
193
|
+
const desired1 = amount1 === 0n ? 0n : (given1 ?? amount1);
|
|
194
|
+
const [min0, min1] = v3MinAmounts(pool.sqrtPriceX96, tickLower, tickUpper, desired0, desired1, ctx.slippage);
|
|
195
|
+
const calls = [];
|
|
196
|
+
if (createPrice !== null) {
|
|
197
|
+
calls.push(call('createAndInitializePoolIfNecessary', [token0, token1, fee, createPrice]));
|
|
198
|
+
}
|
|
199
|
+
if (tokenId === null) {
|
|
200
|
+
calls.push(call('mint', [{ token0, token1, fee, tickLower, tickUpper, amount0Desired: desired0, amount1Desired: desired1, amount0Min: min0, amount1Min: min1, recipient: ctx.recipient, deadline: ctx.deadline }]));
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
calls.push(call('increaseLiquidity', [{ tokenId, amount0Desired: desired0, amount1Desired: desired1, amount0Min: min0, amount1Min: min1, deadline: ctx.deadline }]));
|
|
204
|
+
}
|
|
205
|
+
// 主币:附带包装币那一边的数量,多余的退回
|
|
206
|
+
const value = weth === token0 ? desired0 : weth === token1 ? desired1 : 0n;
|
|
207
|
+
if (value > 0n) {
|
|
208
|
+
calls.push(call('refundETH', []));
|
|
209
|
+
}
|
|
210
|
+
const main = { kind: 'add', to: out(ctx, npm), data: wrap(calls), value: value.toString() };
|
|
211
|
+
const approve = await approvals(ctx, [
|
|
212
|
+
{ token: token0, spender: npm, amount: desired0 },
|
|
213
|
+
{ token: token1, spender: npm, amount: desired1 },
|
|
214
|
+
].filter((i) => i.token !== weth));
|
|
215
|
+
return makePlan(ctx, {
|
|
216
|
+
action: 'add',
|
|
217
|
+
version: 'v3',
|
|
218
|
+
protocol: protocol.id,
|
|
219
|
+
protocolName: protocol.name,
|
|
220
|
+
pool: pool.address,
|
|
221
|
+
createPool: createPrice !== null,
|
|
222
|
+
token0: t0,
|
|
223
|
+
token1: t1,
|
|
224
|
+
tokenId: tokenId === null ? null : tokenId.toString(),
|
|
225
|
+
tickLower,
|
|
226
|
+
tickUpper,
|
|
227
|
+
tick: pool.tick,
|
|
228
|
+
...priceFields(t0, t1, pool.sqrtPriceX96, tickLower, tickUpper),
|
|
229
|
+
liquidity: liquidity.toString(),
|
|
230
|
+
amount0: amount0.toString(),
|
|
231
|
+
amount1: amount1.toString(),
|
|
232
|
+
amount0Max: desired0.toString(),
|
|
233
|
+
amount1Max: desired1.toString(),
|
|
234
|
+
amount0Min: null,
|
|
235
|
+
amount1Min: null,
|
|
236
|
+
// V3 追加流动性不会领取手续费(留在仓位里)
|
|
237
|
+
fees0: '0',
|
|
238
|
+
fees1: '0',
|
|
239
|
+
native: weth === null ? null : weth === token0 ? 'token0' : 'token1',
|
|
240
|
+
value: value.toString(),
|
|
241
|
+
transactions: [...approve, main],
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/** 移除(liquidity > 0)/ 只领取手续费(collectOnly) */
|
|
245
|
+
export async function v3Remove(ctx, params, collectOnly = false) {
|
|
246
|
+
const position = await readPosition(ctx, params.position);
|
|
247
|
+
assertCanManage(ctx, position);
|
|
248
|
+
const { npm, token0, token1, tickLower, tickUpper, tokenId, pool } = position;
|
|
249
|
+
let liquidity = 0n;
|
|
250
|
+
if (!collectOnly) {
|
|
251
|
+
if (position.liquidity === 0n) {
|
|
252
|
+
throw new Error(`Position ${tokenId} has no liquidity; use collectFees to collect what is owed`);
|
|
253
|
+
}
|
|
254
|
+
liquidity = removeAmount(position.liquidity, params.percent, params.liquidity);
|
|
255
|
+
}
|
|
256
|
+
else if (position.fees0 === 0n && position.fees1 === 0n) {
|
|
257
|
+
throw new Error(`Position ${tokenId} has no fees to collect`);
|
|
258
|
+
}
|
|
259
|
+
const burn = !collectOnly && !!params.burn;
|
|
260
|
+
if (burn && liquidity !== position.liquidity) {
|
|
261
|
+
throw new Error('burn: true needs the whole position to be removed');
|
|
262
|
+
}
|
|
263
|
+
const weth = await nativeWeth(ctx, npm, token0, token1);
|
|
264
|
+
const [t0, t1] = (await tokenInfo(ctx, [token0, token1]));
|
|
265
|
+
const sqrtA = getSqrtRatioAtTick(tickLower);
|
|
266
|
+
const sqrtB = getSqrtRatioAtTick(tickUpper);
|
|
267
|
+
const [amount0, amount1] = getAmountsForLiquidity(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity);
|
|
268
|
+
const [min0, min1] = getMinAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity, ctx.slippage);
|
|
269
|
+
const calls = [];
|
|
270
|
+
if (liquidity > 0n) {
|
|
271
|
+
calls.push(call('decreaseLiquidity', [{ tokenId, liquidity, amount0Min: min0, amount1Min: min1, deadline: ctx.deadline }]));
|
|
272
|
+
}
|
|
273
|
+
// 撤出的代币和手续费都要 collect 才会转出;包装币换成主币时先收到仓位合约(零地址)再转出
|
|
274
|
+
calls.push(call('collect', [{ tokenId, recipient: weth ? ZERO_ADDRESS : ctx.recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }]));
|
|
275
|
+
if (weth) {
|
|
276
|
+
calls.push(call('unwrapWETH9', [0n, ctx.recipient]));
|
|
277
|
+
calls.push(call('sweepToken', [weth === token0 ? token1 : token0, 0n, ctx.recipient]));
|
|
278
|
+
}
|
|
279
|
+
if (burn) {
|
|
280
|
+
calls.push(call('burn', [tokenId]));
|
|
281
|
+
}
|
|
282
|
+
return makePlan(ctx, {
|
|
283
|
+
action: collectOnly ? 'collect' : 'remove',
|
|
284
|
+
version: 'v3',
|
|
285
|
+
protocol: position.protocol.id,
|
|
286
|
+
protocolName: position.protocol.name,
|
|
287
|
+
pool: pool.address,
|
|
288
|
+
createPool: false,
|
|
289
|
+
token0: t0,
|
|
290
|
+
token1: t1,
|
|
291
|
+
tokenId: tokenId.toString(),
|
|
292
|
+
tickLower,
|
|
293
|
+
tickUpper,
|
|
294
|
+
tick: pool.tick,
|
|
295
|
+
...priceFields(t0, t1, pool.sqrtPriceX96, tickLower, tickUpper),
|
|
296
|
+
liquidity: liquidity.toString(),
|
|
297
|
+
amount0: amount0.toString(),
|
|
298
|
+
amount1: amount1.toString(),
|
|
299
|
+
amount0Max: null,
|
|
300
|
+
amount1Max: null,
|
|
301
|
+
amount0Min: min0.toString(),
|
|
302
|
+
amount1Min: min1.toString(),
|
|
303
|
+
fees0: position.fees0.toString(),
|
|
304
|
+
fees1: position.fees1.toString(),
|
|
305
|
+
native: weth === null ? null : weth === token0 ? 'token0' : 'token1',
|
|
306
|
+
value: '0',
|
|
307
|
+
transactions: [{ kind: collectOnly ? 'collect' : 'remove', to: out(ctx, npm), data: wrap(calls), value: '0' }],
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=v3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3.js","sourceRoot":"","sources":["../../src/liquidity/v3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Ctx } from './common.js';
|
|
2
|
+
import type { IncreaseLiquidityParams, LiquidityPlan, NewPositionParams, RemoveLiquidityParams } from './types.js';
|
|
3
|
+
export declare function v4Add(ctx: Ctx, params: IncreaseLiquidityParams | NewPositionParams): Promise<LiquidityPlan>;
|
|
4
|
+
export declare function v4Remove(ctx: Ctx, params: RemoveLiquidityParams, collectOnly?: boolean): Promise<LiquidityPlan>;
|
|
5
|
+
//# sourceMappingURL=v4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v4.d.ts","sourceRoot":"","sources":["../../src/liquidity/v4.ts"],"names":[],"mappings":"AAMA,OAAO,EAYL,KAAK,GAAG,EACT,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,uBAAuB,EAAE,aAAa,EAAE,iBAAiB,EAAe,qBAAqB,EAAE,MAAM,YAAY,CAAA;AA+I/H,wBAAsB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,uBAAuB,GAAG,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CA2HjH;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,qBAAqB,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CA2DnH"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { AbiCoder, Interface, hexlify } from 'ethers';
|
|
2
|
+
import { getAmountsForLiquidity, getFeesEarned, getMaxAmounts, getMinAmounts, getMintAmounts, getSqrtRatioAtTick, getTickAtSqrtRatio } from '../lpMath.js';
|
|
3
|
+
import { decodePositionInfo, poolIdOf } from '../lpV4.js';
|
|
4
|
+
import { liquidityFor, resolveRange, sqrtPriceOf } from './concentrated.js';
|
|
5
|
+
import { ZERO_ADDRESS, concentratedProtocol, evm, isNative, makePlan, parseAmount, permit2Approvals, priceFields, removeAmount, tokenIdOf, tokenInfo, } from './common.js';
|
|
6
|
+
const pmInterface = new Interface([
|
|
7
|
+
'function ownerOf(uint256 tokenId) view returns (address)',
|
|
8
|
+
'function getApproved(uint256 tokenId) view returns (address)',
|
|
9
|
+
'function isApprovedForAll(address owner, address operator) view returns (bool)',
|
|
10
|
+
'function getPoolAndPositionInfo(uint256 tokenId) view returns ((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, uint256 info)',
|
|
11
|
+
'function getPositionLiquidity(uint256 tokenId) view returns (uint128)',
|
|
12
|
+
'function modifyLiquidities(bytes unlockData, uint256 deadline) payable',
|
|
13
|
+
'function initializePool((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) key, uint160 sqrtPriceX96) payable returns (int24)',
|
|
14
|
+
'function multicall(bytes[] data) payable returns (bytes[] results)',
|
|
15
|
+
]);
|
|
16
|
+
const stateViewInterface = new Interface([
|
|
17
|
+
'function getSlot0(bytes32 poolId) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee)',
|
|
18
|
+
'function getFeeGrowthInside(bytes32 poolId, int24 tickLower, int24 tickUpper) view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128)',
|
|
19
|
+
'function getPositionInfo(bytes32 poolId, address owner, int24 tickLower, int24 tickUpper, bytes32 salt) view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128)',
|
|
20
|
+
]);
|
|
21
|
+
/** v4-periphery 的 Actions */
|
|
22
|
+
const Actions = {
|
|
23
|
+
INCREASE_LIQUIDITY: 0x00,
|
|
24
|
+
DECREASE_LIQUIDITY: 0x01,
|
|
25
|
+
MINT_POSITION: 0x02,
|
|
26
|
+
BURN_POSITION: 0x03,
|
|
27
|
+
SETTLE_PAIR: 0x0d,
|
|
28
|
+
TAKE_PAIR: 0x11,
|
|
29
|
+
CLOSE_CURRENCY: 0x12,
|
|
30
|
+
SWEEP: 0x14,
|
|
31
|
+
};
|
|
32
|
+
/** 标准手续费档位对应的 tick 间距(与 Uniswap 网页一致) */
|
|
33
|
+
const DEFAULT_TICK_SPACING = { 100: 1, 500: 10, 3000: 60, 10000: 200 };
|
|
34
|
+
const coder = AbiCoder.defaultAbiCoder();
|
|
35
|
+
const POOL_KEY = 'tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks)';
|
|
36
|
+
const keyTuple = (k) => [k.currency0, k.currency1, k.fee, k.tickSpacing, k.hooks];
|
|
37
|
+
/** 依次执行的 action 列表 → modifyLiquidities 的 unlockData */
|
|
38
|
+
class Planner {
|
|
39
|
+
actions = [];
|
|
40
|
+
params = [];
|
|
41
|
+
add(action, types, values) {
|
|
42
|
+
this.actions.push(action);
|
|
43
|
+
this.params.push(coder.encode(types, values));
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
encode() {
|
|
47
|
+
return coder.encode(['bytes', 'bytes[]'], [hexlify(new Uint8Array(this.actions)), this.params]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function addresses(ctx, protocol) {
|
|
51
|
+
if (ctx.tron) {
|
|
52
|
+
throw new Error('Uniswap V4 is not supported on Tron');
|
|
53
|
+
}
|
|
54
|
+
if (!protocol.positionManager || !protocol.stateView) {
|
|
55
|
+
throw new Error(`Protocol ${protocol.id} needs positionManager and stateView`);
|
|
56
|
+
}
|
|
57
|
+
return { pm: evm(protocol.positionManager), stateView: evm(protocol.stateView) };
|
|
58
|
+
}
|
|
59
|
+
async function readPosition(ctx, ref) {
|
|
60
|
+
const protocol = concentratedProtocol(ctx, ref);
|
|
61
|
+
const { pm, stateView } = addresses(ctx, protocol);
|
|
62
|
+
const tokenId = tokenIdOf(ref);
|
|
63
|
+
const c = ctx.provider.contract(pm, pmInterface);
|
|
64
|
+
const [owner, info, liquidity] = (await ctx.provider.all([c.ownerOf(tokenId), c.getPoolAndPositionInfo(tokenId), c.getPositionLiquidity(tokenId)]));
|
|
65
|
+
const raw = info[0];
|
|
66
|
+
const key = { currency0: evm(raw.currency0), currency1: evm(raw.currency1), fee: Number(raw.fee), tickSpacing: Number(raw.tickSpacing), hooks: evm(raw.hooks) };
|
|
67
|
+
const poolId = poolIdOf(key);
|
|
68
|
+
const { tickLower, tickUpper } = decodePositionInfo(info[1]);
|
|
69
|
+
const sv = ctx.provider.contract(stateView, stateViewInterface);
|
|
70
|
+
const salt = `0x${tokenId.toString(16).padStart(64, '0')}`;
|
|
71
|
+
const [slot0, inside, last, approved, approvedForAll] = (await ctx.provider.all([
|
|
72
|
+
sv.getSlot0(poolId),
|
|
73
|
+
sv.getFeeGrowthInside(poolId, tickLower, tickUpper),
|
|
74
|
+
sv.getPositionInfo(poolId, pm, tickLower, tickUpper, salt),
|
|
75
|
+
c.getApproved(tokenId),
|
|
76
|
+
c.isApprovedForAll(owner, ctx.owner),
|
|
77
|
+
]));
|
|
78
|
+
return {
|
|
79
|
+
protocol,
|
|
80
|
+
pm,
|
|
81
|
+
stateView,
|
|
82
|
+
tokenId,
|
|
83
|
+
owner: evm(owner),
|
|
84
|
+
// 与 PositionManager 的 onlyIfApproved 一致:所有者、单个仓位的 approve、setApprovalForAll 都可以
|
|
85
|
+
canManage: evm(owner) === ctx.owner || evm(approved) === ctx.owner || approvedForAll,
|
|
86
|
+
pool: { key, poolId, sqrtPriceX96: slot0[0], tick: Number(slot0[1]) },
|
|
87
|
+
tickLower,
|
|
88
|
+
tickUpper,
|
|
89
|
+
liquidity,
|
|
90
|
+
fees0: getFeesEarned(liquidity, inside[0], last[1]),
|
|
91
|
+
fees1: getFeesEarned(liquidity, inside[1], last[2]),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function assertOwner(ctx, position) {
|
|
95
|
+
if (!position.canManage) {
|
|
96
|
+
throw new Error(`${ctx.owner} is not the owner of position ${position.tokenId} (owner: ${position.owner})`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const hookData = (ctx) => (ctx.options.hookData ? hexlify(ctx.options.hookData) : '0x');
|
|
100
|
+
export async function v4Add(ctx, params) {
|
|
101
|
+
let protocol;
|
|
102
|
+
let pm;
|
|
103
|
+
let pool;
|
|
104
|
+
let tickLower;
|
|
105
|
+
let tickUpper;
|
|
106
|
+
let tokenId = null;
|
|
107
|
+
let createPrice = null;
|
|
108
|
+
let fees = [0n, 0n];
|
|
109
|
+
let t0;
|
|
110
|
+
let t1;
|
|
111
|
+
let given0;
|
|
112
|
+
let given1;
|
|
113
|
+
if ('position' in params) {
|
|
114
|
+
const position = await readPosition(ctx, params.position);
|
|
115
|
+
assertOwner(ctx, position);
|
|
116
|
+
({ protocol, pm, pool, tickLower, tickUpper } = position);
|
|
117
|
+
tokenId = position.tokenId;
|
|
118
|
+
fees = [position.fees0, position.fees1];
|
|
119
|
+
[t0, t1] = (await tokenInfo(ctx, [pool.key.currency0, pool.key.currency1]));
|
|
120
|
+
given0 = parseAmount(params.amount0, params.amount0Formatted, t0, 'amount0');
|
|
121
|
+
given1 = parseAmount(params.amount1, params.amount1Formatted, t1, 'amount1');
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
protocol = params.protocol;
|
|
125
|
+
const { pm: positionManager, stateView } = addresses(ctx, protocol);
|
|
126
|
+
pm = positionManager;
|
|
127
|
+
if (params.fee === undefined) {
|
|
128
|
+
throw new Error('fee is required for V4 (e.g. 500, 3000, 10000)');
|
|
129
|
+
}
|
|
130
|
+
const tickSpacing = params.tickSpacing ?? DEFAULT_TICK_SPACING[params.fee];
|
|
131
|
+
if (tickSpacing === undefined) {
|
|
132
|
+
throw new Error(`Pass tickSpacing: there is no default for fee ${params.fee}`);
|
|
133
|
+
}
|
|
134
|
+
// 主币是零地址,排在前面
|
|
135
|
+
const a = isNative(ctx, params.tokenA) ? ZERO_ADDRESS : evm(params.tokenA);
|
|
136
|
+
const b = isNative(ctx, params.tokenB) ? ZERO_ADDRESS : evm(params.tokenB);
|
|
137
|
+
if (a === b) {
|
|
138
|
+
throw new Error('tokenA and tokenB must be different');
|
|
139
|
+
}
|
|
140
|
+
const aIs0 = BigInt(a) < BigInt(b);
|
|
141
|
+
const key = { currency0: aIs0 ? a : b, currency1: aIs0 ? b : a, fee: params.fee, tickSpacing, hooks: params.hooks ? evm(params.hooks) : ZERO_ADDRESS };
|
|
142
|
+
const poolId = poolIdOf(key);
|
|
143
|
+
const slot0 = (await ctx.provider.call(ctx.provider.contract(stateView, stateViewInterface).getSlot0(poolId)));
|
|
144
|
+
pool = { key, poolId, sqrtPriceX96: slot0[0], tick: Number(slot0[1]) };
|
|
145
|
+
[t0, t1] = (await tokenInfo(ctx, [key.currency0, key.currency1]));
|
|
146
|
+
if (pool.sqrtPriceX96 === 0n) {
|
|
147
|
+
if (params.initialPrice === undefined) {
|
|
148
|
+
throw new Error('This V4 pool (currencies, fee, tickSpacing, hooks) is not initialized yet; pass initialPrice to create it');
|
|
149
|
+
}
|
|
150
|
+
createPrice = sqrtPriceOf(params.initialPrice, aIs0, t0, t1, 'initialPrice');
|
|
151
|
+
pool = { ...pool, sqrtPriceX96: createPrice, tick: getTickAtSqrtRatio(createPrice) };
|
|
152
|
+
}
|
|
153
|
+
;
|
|
154
|
+
[tickLower, tickUpper] = resolveRange(params, aIs0, t0, t1, tickSpacing);
|
|
155
|
+
const amountA = parseAmount(params.amountA, params.amountAFormatted, aIs0 ? t0 : t1, 'amountA');
|
|
156
|
+
const amountB = parseAmount(params.amountB, params.amountBFormatted, aIs0 ? t1 : t0, 'amountB');
|
|
157
|
+
[given0, given1] = aIs0 ? [amountA, amountB] : [amountB, amountA];
|
|
158
|
+
}
|
|
159
|
+
const { key } = pool;
|
|
160
|
+
const sqrtA = getSqrtRatioAtTick(tickLower);
|
|
161
|
+
const sqrtB = getSqrtRatioAtTick(tickUpper);
|
|
162
|
+
const liquidity = liquidityFor(pool.sqrtPriceX96, tickLower, tickUpper, given0, given1, t0, t1);
|
|
163
|
+
const [amount0, amount1] = getMintAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity);
|
|
164
|
+
// V4 直接指定流动性,价格变动时需要的数量随之变化:按滑点给出上限
|
|
165
|
+
const [max0, max1] = getMaxAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity, ctx.slippage);
|
|
166
|
+
const native = key.currency0 === ZERO_ADDRESS;
|
|
167
|
+
const planner = new Planner();
|
|
168
|
+
if (tokenId === null) {
|
|
169
|
+
planner.add(Actions.MINT_POSITION, [POOL_KEY, 'int24', 'int24', 'uint256', 'uint128', 'uint128', 'address', 'bytes'], [keyTuple(key), tickLower, tickUpper, liquidity, max0, max1, ctx.recipient, hookData(ctx)]);
|
|
170
|
+
planner.add(Actions.SETTLE_PAIR, ['address', 'address'], [key.currency0, key.currency1]);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
planner.add(Actions.INCREASE_LIQUIDITY, ['uint256', 'uint256', 'uint128', 'uint128', 'bytes'], [tokenId, liquidity, max0, max1, hookData(ctx)]);
|
|
174
|
+
// 追加时会自动领取手续费:手续费可能比要存入的多(差额为正),CLOSE_CURRENCY 按差额的正负自动付款或收款
|
|
175
|
+
planner.add(Actions.CLOSE_CURRENCY, ['address'], [key.currency0]);
|
|
176
|
+
planner.add(Actions.CLOSE_CURRENCY, ['address'], [key.currency1]);
|
|
177
|
+
}
|
|
178
|
+
if (native) {
|
|
179
|
+
// 附带的主币按上限给,多余的退回
|
|
180
|
+
planner.add(Actions.SWEEP, ['address', 'address'], [ZERO_ADDRESS, ctx.owner]);
|
|
181
|
+
}
|
|
182
|
+
const modify = pmInterface.encodeFunctionData('modifyLiquidities', [planner.encode(), ctx.deadline]);
|
|
183
|
+
const data = createPrice === null ? modify : pmInterface.encodeFunctionData('multicall', [[pmInterface.encodeFunctionData('initializePool', [keyTuple(key), createPrice]), modify]]);
|
|
184
|
+
const value = native ? max0 : 0n;
|
|
185
|
+
const approve = await permit2Approvals(ctx, pm, [
|
|
186
|
+
{ token: key.currency0, amount: max0 },
|
|
187
|
+
{ token: key.currency1, amount: max1 },
|
|
188
|
+
].filter((i) => i.token !== ZERO_ADDRESS));
|
|
189
|
+
return makePlan(ctx, {
|
|
190
|
+
action: 'add',
|
|
191
|
+
version: 'v4',
|
|
192
|
+
protocol: protocol.id,
|
|
193
|
+
protocolName: protocol.name,
|
|
194
|
+
pool: pool.poolId,
|
|
195
|
+
createPool: createPrice !== null,
|
|
196
|
+
token0: t0,
|
|
197
|
+
token1: t1,
|
|
198
|
+
tokenId: tokenId === null ? null : tokenId.toString(),
|
|
199
|
+
tickLower,
|
|
200
|
+
tickUpper,
|
|
201
|
+
tick: pool.tick,
|
|
202
|
+
...priceFields(t0, t1, pool.sqrtPriceX96, tickLower, tickUpper),
|
|
203
|
+
liquidity: liquidity.toString(),
|
|
204
|
+
amount0: amount0.toString(),
|
|
205
|
+
amount1: amount1.toString(),
|
|
206
|
+
amount0Max: max0.toString(),
|
|
207
|
+
amount1Max: max1.toString(),
|
|
208
|
+
amount0Min: null,
|
|
209
|
+
amount1Min: null,
|
|
210
|
+
fees0: fees[0].toString(),
|
|
211
|
+
fees1: fees[1].toString(),
|
|
212
|
+
native: native ? 'token0' : null,
|
|
213
|
+
value: value.toString(),
|
|
214
|
+
transactions: [...approve, { kind: 'add', to: pm, data, value: value.toString() }],
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
export async function v4Remove(ctx, params, collectOnly = false) {
|
|
218
|
+
const position = await readPosition(ctx, params.position);
|
|
219
|
+
assertOwner(ctx, position);
|
|
220
|
+
const { pm, pool, tickLower, tickUpper, tokenId } = position;
|
|
221
|
+
const { key } = pool;
|
|
222
|
+
let liquidity = 0n;
|
|
223
|
+
if (!collectOnly) {
|
|
224
|
+
if (position.liquidity === 0n) {
|
|
225
|
+
throw new Error(`Position ${tokenId} has no liquidity`);
|
|
226
|
+
}
|
|
227
|
+
liquidity = removeAmount(position.liquidity, params.percent, params.liquidity);
|
|
228
|
+
}
|
|
229
|
+
else if (position.fees0 === 0n && position.fees1 === 0n) {
|
|
230
|
+
throw new Error(`Position ${tokenId} has no fees to collect`);
|
|
231
|
+
}
|
|
232
|
+
const burn = !collectOnly && !!params.burn;
|
|
233
|
+
if (burn && liquidity !== position.liquidity) {
|
|
234
|
+
throw new Error('burn: true needs the whole position to be removed');
|
|
235
|
+
}
|
|
236
|
+
const [t0, t1] = (await tokenInfo(ctx, [key.currency0, key.currency1]));
|
|
237
|
+
const sqrtA = getSqrtRatioAtTick(tickLower);
|
|
238
|
+
const sqrtB = getSqrtRatioAtTick(tickUpper);
|
|
239
|
+
const [amount0, amount1] = getAmountsForLiquidity(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity);
|
|
240
|
+
const [min0, min1] = getMinAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity, ctx.slippage);
|
|
241
|
+
const planner = new Planner();
|
|
242
|
+
if (burn) {
|
|
243
|
+
planner.add(Actions.BURN_POSITION, ['uint256', 'uint128', 'uint128', 'bytes'], [tokenId, min0, min1, hookData(ctx)]);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// 流动性为 0 时只领取手续费
|
|
247
|
+
planner.add(Actions.DECREASE_LIQUIDITY, ['uint256', 'uint256', 'uint128', 'uint128', 'bytes'], [tokenId, liquidity, min0, min1, hookData(ctx)]);
|
|
248
|
+
}
|
|
249
|
+
planner.add(Actions.TAKE_PAIR, ['address', 'address', 'address'], [key.currency0, key.currency1, ctx.recipient]);
|
|
250
|
+
const data = pmInterface.encodeFunctionData('modifyLiquidities', [planner.encode(), ctx.deadline]);
|
|
251
|
+
return makePlan(ctx, {
|
|
252
|
+
action: collectOnly ? 'collect' : 'remove',
|
|
253
|
+
version: 'v4',
|
|
254
|
+
protocol: position.protocol.id,
|
|
255
|
+
protocolName: position.protocol.name,
|
|
256
|
+
pool: pool.poolId,
|
|
257
|
+
createPool: false,
|
|
258
|
+
token0: t0,
|
|
259
|
+
token1: t1,
|
|
260
|
+
tokenId: tokenId.toString(),
|
|
261
|
+
tickLower,
|
|
262
|
+
tickUpper,
|
|
263
|
+
tick: pool.tick,
|
|
264
|
+
...priceFields(t0, t1, pool.sqrtPriceX96, tickLower, tickUpper),
|
|
265
|
+
liquidity: liquidity.toString(),
|
|
266
|
+
amount0: amount0.toString(),
|
|
267
|
+
amount1: amount1.toString(),
|
|
268
|
+
amount0Max: null,
|
|
269
|
+
amount1Max: null,
|
|
270
|
+
amount0Min: min0.toString(),
|
|
271
|
+
amount1Min: min1.toString(),
|
|
272
|
+
fees0: position.fees0.toString(),
|
|
273
|
+
fees1: position.fees1.toString(),
|
|
274
|
+
native: key.currency0 === ZERO_ADDRESS ? 'token0' : null,
|
|
275
|
+
value: '0',
|
|
276
|
+
transactions: [{ kind: collectOnly ? 'collect' : 'remove', to: pm, data, value: '0' }],
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=v4.js.map
|