@w3lib/evm-batch-call 0.4.2 → 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.
Files changed (128) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +242 -2
  3. package/dist/fallback.d.ts +5 -0
  4. package/dist/fallback.d.ts.map +1 -1
  5. package/dist/fallback.js +7 -0
  6. package/dist/fallback.js.map +1 -1
  7. package/dist/liquidity/common.d.ts +72 -0
  8. package/dist/liquidity/common.d.ts.map +1 -0
  9. package/dist/liquidity/common.js +271 -0
  10. package/dist/liquidity/common.js.map +1 -0
  11. package/dist/liquidity/concentrated.d.ts +19 -0
  12. package/dist/liquidity/concentrated.d.ts.map +1 -0
  13. package/dist/liquidity/concentrated.js +106 -0
  14. package/dist/liquidity/concentrated.js.map +1 -0
  15. package/dist/liquidity/index.d.ts +42 -0
  16. package/dist/liquidity/index.d.ts.map +1 -0
  17. package/dist/liquidity/index.js +143 -0
  18. package/dist/liquidity/index.js.map +1 -0
  19. package/dist/liquidity/types.d.ts +182 -0
  20. package/dist/liquidity/types.d.ts.map +1 -0
  21. package/dist/liquidity/types.js +2 -0
  22. package/dist/liquidity/types.js.map +1 -0
  23. package/dist/liquidity/v2.d.ts +5 -0
  24. package/dist/liquidity/v2.d.ts.map +1 -0
  25. package/dist/liquidity/v2.js +342 -0
  26. package/dist/liquidity/v2.js.map +1 -0
  27. package/dist/liquidity/v3.d.ts +6 -0
  28. package/dist/liquidity/v3.d.ts.map +1 -0
  29. package/dist/liquidity/v3.js +310 -0
  30. package/dist/liquidity/v3.js.map +1 -0
  31. package/dist/liquidity/v4.d.ts +5 -0
  32. package/dist/liquidity/v4.d.ts.map +1 -0
  33. package/dist/liquidity/v4.js +279 -0
  34. package/dist/liquidity/v4.js.map +1 -0
  35. package/dist/lp.d.ts +156 -0
  36. package/dist/lp.d.ts.map +1 -0
  37. package/dist/lp.js +510 -0
  38. package/dist/lp.js.map +1 -0
  39. package/dist/lpMath.d.ts +80 -0
  40. package/dist/lpMath.d.ts.map +1 -0
  41. package/dist/lpMath.js +312 -0
  42. package/dist/lpMath.js.map +1 -0
  43. package/dist/lpV4.d.ts +108 -0
  44. package/dist/lpV4.d.ts.map +1 -0
  45. package/dist/lpV4.js +283 -0
  46. package/dist/lpV4.js.map +1 -0
  47. package/dist/owner.d.ts +1 -1
  48. package/dist/owner.d.ts.map +1 -1
  49. package/dist/owner.js +18 -5
  50. package/dist/owner.js.map +1 -1
  51. package/dist/scan.d.ts +54 -0
  52. package/dist/scan.d.ts.map +1 -1
  53. package/dist/scan.js +124 -92
  54. package/dist/scan.js.map +1 -1
  55. package/dist/source.d.ts +2 -0
  56. package/dist/source.d.ts.map +1 -1
  57. package/dist/source.js +4 -0
  58. package/dist/source.js.map +1 -1
  59. package/dist/subpaths/liquidity.d.ts +24 -0
  60. package/dist/subpaths/liquidity.d.ts.map +1 -0
  61. package/dist/subpaths/liquidity.js +21 -0
  62. package/dist/subpaths/liquidity.js.map +1 -0
  63. package/dist/subpaths/lp.d.ts +33 -0
  64. package/dist/subpaths/lp.d.ts.map +1 -0
  65. package/dist/subpaths/lp.js +47 -0
  66. package/dist/subpaths/lp.js.map +1 -0
  67. package/dist/subpaths/write.d.ts +20 -0
  68. package/dist/subpaths/write.d.ts.map +1 -0
  69. package/dist/subpaths/write.js +18 -0
  70. package/dist/subpaths/write.js.map +1 -0
  71. package/dist/tron.d.ts +2 -0
  72. package/dist/tron.d.ts.map +1 -1
  73. package/dist/tron.js +4 -0
  74. package/dist/tron.js.map +1 -1
  75. package/dist/write/batch.d.ts +88 -0
  76. package/dist/write/batch.d.ts.map +1 -0
  77. package/dist/write/batch.js +231 -0
  78. package/dist/write/batch.js.map +1 -0
  79. package/dist/write/collect.d.ts +53 -0
  80. package/dist/write/collect.d.ts.map +1 -0
  81. package/dist/write/collect.js +255 -0
  82. package/dist/write/collect.js.map +1 -0
  83. package/dist/write/rpc.d.ts +68 -0
  84. package/dist/write/rpc.d.ts.map +1 -0
  85. package/dist/write/rpc.js +236 -0
  86. package/dist/write/rpc.js.map +1 -0
  87. package/dist/write/send.d.ts +124 -0
  88. package/dist/write/send.d.ts.map +1 -0
  89. package/dist/write/send.js +373 -0
  90. package/dist/write/send.js.map +1 -0
  91. package/dist/write/signer.d.ts +58 -0
  92. package/dist/write/signer.d.ts.map +1 -0
  93. package/dist/write/signer.js +41 -0
  94. package/dist/write/signer.js.map +1 -0
  95. package/dist/write/transfer.d.ts +83 -0
  96. package/dist/write/transfer.d.ts.map +1 -0
  97. package/dist/write/transfer.js +259 -0
  98. package/dist/write/transfer.js.map +1 -0
  99. package/dist/write/tron.d.ts +60 -0
  100. package/dist/write/tron.d.ts.map +1 -0
  101. package/dist/write/tron.js +960 -0
  102. package/dist/write/tron.js.map +1 -0
  103. package/package.json +26 -5
  104. package/src/fallback.ts +8 -0
  105. package/src/liquidity/common.ts +314 -0
  106. package/src/liquidity/concentrated.ts +121 -0
  107. package/src/liquidity/index.ts +171 -0
  108. package/src/liquidity/types.ts +194 -0
  109. package/src/liquidity/v2.ts +371 -0
  110. package/src/liquidity/v3.ts +382 -0
  111. package/src/liquidity/v4.ts +348 -0
  112. package/src/lp.ts +739 -0
  113. package/src/lpMath.ts +336 -0
  114. package/src/lpV4.ts +406 -0
  115. package/src/owner.ts +19 -6
  116. package/src/scan.ts +155 -86
  117. package/src/source.ts +5 -0
  118. package/src/subpaths/liquidity.ts +52 -0
  119. package/src/subpaths/lp.ts +90 -0
  120. package/src/subpaths/write.ts +26 -0
  121. package/src/tron.ts +5 -0
  122. package/src/write/batch.ts +302 -0
  123. package/src/write/collect.ts +312 -0
  124. package/src/write/rpc.ts +277 -0
  125. package/src/write/send.ts +490 -0
  126. package/src/write/signer.ts +90 -0
  127. package/src/write/transfer.ts +344 -0
  128. package/src/write/tron.ts +1105 -0
@@ -0,0 +1,348 @@
1
+ import { AbiCoder, Interface, hexlify } from 'ethers'
2
+
3
+ import type { LpPosition, LpProtocol, LpToken } from '../lp.js'
4
+ import { getAmountsForLiquidity, getFeesEarned, getMaxAmounts, getMinAmounts, getMintAmounts, getSqrtRatioAtTick, getTickAtSqrtRatio } from '../lpMath.js'
5
+ import { decodePositionInfo, poolIdOf } from '../lpV4.js'
6
+ import { liquidityFor, resolveRange, sqrtPriceOf } from './concentrated.js'
7
+ import {
8
+ ZERO_ADDRESS,
9
+ concentratedProtocol,
10
+ evm,
11
+ isNative,
12
+ makePlan,
13
+ parseAmount,
14
+ permit2Approvals,
15
+ priceFields,
16
+ removeAmount,
17
+ tokenIdOf,
18
+ tokenInfo,
19
+ type Ctx,
20
+ } from './common.js'
21
+ import type { IncreaseLiquidityParams, LiquidityPlan, NewPositionParams, PositionRef, RemoveLiquidityParams } from './types.js'
22
+
23
+ const pmInterface = new Interface([
24
+ 'function ownerOf(uint256 tokenId) view returns (address)',
25
+ 'function getApproved(uint256 tokenId) view returns (address)',
26
+ 'function isApprovedForAll(address owner, address operator) view returns (bool)',
27
+ 'function getPoolAndPositionInfo(uint256 tokenId) view returns ((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, uint256 info)',
28
+ 'function getPositionLiquidity(uint256 tokenId) view returns (uint128)',
29
+ 'function modifyLiquidities(bytes unlockData, uint256 deadline) payable',
30
+ 'function initializePool((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) key, uint160 sqrtPriceX96) payable returns (int24)',
31
+ 'function multicall(bytes[] data) payable returns (bytes[] results)',
32
+ ])
33
+ const stateViewInterface = new Interface([
34
+ 'function getSlot0(bytes32 poolId) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee)',
35
+ 'function getFeeGrowthInside(bytes32 poolId, int24 tickLower, int24 tickUpper) view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128)',
36
+ 'function getPositionInfo(bytes32 poolId, address owner, int24 tickLower, int24 tickUpper, bytes32 salt) view returns (uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128)',
37
+ ])
38
+
39
+ /** v4-periphery 的 Actions */
40
+ const Actions = {
41
+ INCREASE_LIQUIDITY: 0x00,
42
+ DECREASE_LIQUIDITY: 0x01,
43
+ MINT_POSITION: 0x02,
44
+ BURN_POSITION: 0x03,
45
+ SETTLE_PAIR: 0x0d,
46
+ TAKE_PAIR: 0x11,
47
+ CLOSE_CURRENCY: 0x12,
48
+ SWEEP: 0x14,
49
+ } as const
50
+
51
+ /** 标准手续费档位对应的 tick 间距(与 Uniswap 网页一致) */
52
+ const DEFAULT_TICK_SPACING: Readonly<Record<number, number>> = { 100: 1, 500: 10, 3000: 60, 10000: 200 }
53
+
54
+ const coder = AbiCoder.defaultAbiCoder()
55
+ const POOL_KEY = 'tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks)'
56
+
57
+ interface PoolKey {
58
+ currency0: string
59
+ currency1: string
60
+ fee: number
61
+ tickSpacing: number
62
+ hooks: string
63
+ }
64
+
65
+ const keyTuple = (k: PoolKey) => [k.currency0, k.currency1, k.fee, k.tickSpacing, k.hooks]
66
+
67
+ /** 依次执行的 action 列表 → modifyLiquidities 的 unlockData */
68
+ class Planner {
69
+ readonly actions: number[] = []
70
+ readonly params: string[] = []
71
+
72
+ add(action: number, types: string[], values: unknown[]): this {
73
+ this.actions.push(action)
74
+ this.params.push(coder.encode(types, values))
75
+ return this
76
+ }
77
+
78
+ encode(): string {
79
+ return coder.encode(['bytes', 'bytes[]'], [hexlify(new Uint8Array(this.actions)), this.params])
80
+ }
81
+ }
82
+
83
+ interface V4Pool {
84
+ key: PoolKey
85
+ poolId: string
86
+ sqrtPriceX96: bigint
87
+ tick: number
88
+ }
89
+
90
+ interface V4Position {
91
+ protocol: LpProtocol
92
+ pm: string
93
+ stateView: string
94
+ tokenId: bigint
95
+ owner: string
96
+ /** 发送者能不能管理这个仓位(所有者或被授权) */
97
+ canManage: boolean
98
+ pool: V4Pool
99
+ tickLower: number
100
+ tickUpper: number
101
+ liquidity: bigint
102
+ fees0: bigint
103
+ fees1: bigint
104
+ }
105
+
106
+ function addresses(ctx: Ctx, protocol: LpProtocol): { pm: string; stateView: string } {
107
+ if (ctx.tron) {
108
+ throw new Error('Uniswap V4 is not supported on Tron')
109
+ }
110
+ if (!protocol.positionManager || !protocol.stateView) {
111
+ throw new Error(`Protocol ${protocol.id} needs positionManager and stateView`)
112
+ }
113
+ return { pm: evm(protocol.positionManager), stateView: evm(protocol.stateView) }
114
+ }
115
+
116
+ async function readPosition(ctx: Ctx, ref: PositionRef | LpPosition): Promise<V4Position> {
117
+ const protocol = concentratedProtocol(ctx, ref)
118
+ const { pm, stateView } = addresses(ctx, protocol)
119
+ const tokenId = tokenIdOf(ref)
120
+ const c = ctx.provider.contract(pm, pmInterface)
121
+ const [owner, info, liquidity] = (await ctx.provider.all([c.ownerOf(tokenId), c.getPoolAndPositionInfo(tokenId), c.getPositionLiquidity(tokenId)])) as [
122
+ string,
123
+ { 0: { currency0: string; currency1: string; fee: bigint; tickSpacing: bigint; hooks: string }; 1: bigint },
124
+ bigint,
125
+ ]
126
+ const raw = info[0]
127
+ const key: PoolKey = { currency0: evm(raw.currency0), currency1: evm(raw.currency1), fee: Number(raw.fee), tickSpacing: Number(raw.tickSpacing), hooks: evm(raw.hooks) }
128
+ const poolId = poolIdOf(key)
129
+ const { tickLower, tickUpper } = decodePositionInfo(info[1])
130
+ const sv = ctx.provider.contract(stateView, stateViewInterface)
131
+ const salt = `0x${tokenId.toString(16).padStart(64, '0')}`
132
+ const [slot0, inside, last, approved, approvedForAll] = (await ctx.provider.all([
133
+ sv.getSlot0(poolId),
134
+ sv.getFeeGrowthInside(poolId, tickLower, tickUpper),
135
+ sv.getPositionInfo(poolId, pm, tickLower, tickUpper, salt),
136
+ c.getApproved(tokenId),
137
+ c.isApprovedForAll(owner, ctx.owner),
138
+ ])) as [{ 0: bigint; 1: bigint }, { 0: bigint; 1: bigint }, { 1: bigint; 2: bigint }, string, boolean]
139
+ return {
140
+ protocol,
141
+ pm,
142
+ stateView,
143
+ tokenId,
144
+ owner: evm(owner),
145
+ // 与 PositionManager 的 onlyIfApproved 一致:所有者、单个仓位的 approve、setApprovalForAll 都可以
146
+ canManage: evm(owner) === ctx.owner || evm(approved) === ctx.owner || approvedForAll,
147
+ pool: { key, poolId, sqrtPriceX96: slot0[0], tick: Number(slot0[1]) },
148
+ tickLower,
149
+ tickUpper,
150
+ liquidity,
151
+ fees0: getFeesEarned(liquidity, inside[0], last[1]),
152
+ fees1: getFeesEarned(liquidity, inside[1], last[2]),
153
+ }
154
+ }
155
+
156
+ function assertOwner(ctx: Ctx, position: V4Position): void {
157
+ if (!position.canManage) {
158
+ throw new Error(`${ctx.owner} is not the owner of position ${position.tokenId} (owner: ${position.owner})`)
159
+ }
160
+ }
161
+
162
+ const hookData = (ctx: Ctx) => (ctx.options.hookData ? hexlify(ctx.options.hookData) : '0x')
163
+
164
+ export async function v4Add(ctx: Ctx, params: IncreaseLiquidityParams | NewPositionParams): Promise<LiquidityPlan> {
165
+ let protocol: LpProtocol
166
+ let pm: string
167
+ let pool: V4Pool
168
+ let tickLower: number
169
+ let tickUpper: number
170
+ let tokenId: bigint | null = null
171
+ let createPrice: bigint | null = null
172
+ let fees: [bigint, bigint] = [0n, 0n]
173
+ let t0: LpToken
174
+ let t1: LpToken
175
+ let given0: bigint | undefined
176
+ let given1: bigint | undefined
177
+
178
+ if ('position' in params) {
179
+ const position = await readPosition(ctx, params.position)
180
+ assertOwner(ctx, position)
181
+ ;({ protocol, pm, pool, tickLower, tickUpper } = position)
182
+ tokenId = position.tokenId
183
+ fees = [position.fees0, position.fees1]
184
+ ;[t0, t1] = (await tokenInfo(ctx, [pool.key.currency0, pool.key.currency1])) as [LpToken, LpToken]
185
+ given0 = parseAmount(params.amount0, params.amount0Formatted, t0, 'amount0')
186
+ given1 = parseAmount(params.amount1, params.amount1Formatted, t1, 'amount1')
187
+ } else {
188
+ protocol = params.protocol as LpProtocol
189
+ const { pm: positionManager, stateView } = addresses(ctx, protocol)
190
+ pm = positionManager
191
+ if (params.fee === undefined) {
192
+ throw new Error('fee is required for V4 (e.g. 500, 3000, 10000)')
193
+ }
194
+ const tickSpacing = params.tickSpacing ?? DEFAULT_TICK_SPACING[params.fee]
195
+ if (tickSpacing === undefined) {
196
+ throw new Error(`Pass tickSpacing: there is no default for fee ${params.fee}`)
197
+ }
198
+ // 主币是零地址,排在前面
199
+ const a = isNative(ctx, params.tokenA) ? ZERO_ADDRESS : evm(params.tokenA)
200
+ const b = isNative(ctx, params.tokenB) ? ZERO_ADDRESS : evm(params.tokenB)
201
+ if (a === b) {
202
+ throw new Error('tokenA and tokenB must be different')
203
+ }
204
+ const aIs0 = BigInt(a) < BigInt(b)
205
+ const key: PoolKey = { currency0: aIs0 ? a : b, currency1: aIs0 ? b : a, fee: params.fee, tickSpacing, hooks: params.hooks ? evm(params.hooks) : ZERO_ADDRESS }
206
+ const poolId = poolIdOf(key)
207
+ const slot0 = (await ctx.provider.call(ctx.provider.contract(stateView, stateViewInterface).getSlot0(poolId))) as { 0: bigint; 1: bigint }
208
+ pool = { key, poolId, sqrtPriceX96: slot0[0], tick: Number(slot0[1]) }
209
+ ;[t0, t1] = (await tokenInfo(ctx, [key.currency0, key.currency1])) as [LpToken, LpToken]
210
+ if (pool.sqrtPriceX96 === 0n) {
211
+ if (params.initialPrice === undefined) {
212
+ throw new Error('This V4 pool (currencies, fee, tickSpacing, hooks) is not initialized yet; pass initialPrice to create it')
213
+ }
214
+ createPrice = sqrtPriceOf(params.initialPrice, aIs0, t0, t1, 'initialPrice')
215
+ pool = { ...pool, sqrtPriceX96: createPrice, tick: getTickAtSqrtRatio(createPrice) }
216
+ }
217
+ ;[tickLower, tickUpper] = resolveRange(params, aIs0, t0, t1, tickSpacing)
218
+ const amountA = parseAmount(params.amountA, params.amountAFormatted, aIs0 ? t0 : t1, 'amountA')
219
+ const amountB = parseAmount(params.amountB, params.amountBFormatted, aIs0 ? t1 : t0, 'amountB')
220
+ ;[given0, given1] = aIs0 ? [amountA, amountB] : [amountB, amountA]
221
+ }
222
+
223
+ const { key } = pool
224
+ const sqrtA = getSqrtRatioAtTick(tickLower)
225
+ const sqrtB = getSqrtRatioAtTick(tickUpper)
226
+ const liquidity = liquidityFor(pool.sqrtPriceX96, tickLower, tickUpper, given0, given1, t0, t1)
227
+ const [amount0, amount1] = getMintAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity)
228
+ // V4 直接指定流动性,价格变动时需要的数量随之变化:按滑点给出上限
229
+ const [max0, max1] = getMaxAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity, ctx.slippage)
230
+ const native = key.currency0 === ZERO_ADDRESS
231
+ const planner = new Planner()
232
+ if (tokenId === null) {
233
+ planner.add(
234
+ Actions.MINT_POSITION,
235
+ [POOL_KEY, 'int24', 'int24', 'uint256', 'uint128', 'uint128', 'address', 'bytes'],
236
+ [keyTuple(key), tickLower, tickUpper, liquidity, max0, max1, ctx.recipient, hookData(ctx)],
237
+ )
238
+ planner.add(Actions.SETTLE_PAIR, ['address', 'address'], [key.currency0, key.currency1])
239
+ } else {
240
+ planner.add(Actions.INCREASE_LIQUIDITY, ['uint256', 'uint256', 'uint128', 'uint128', 'bytes'], [tokenId, liquidity, max0, max1, hookData(ctx)])
241
+ // 追加时会自动领取手续费:手续费可能比要存入的多(差额为正),CLOSE_CURRENCY 按差额的正负自动付款或收款
242
+ planner.add(Actions.CLOSE_CURRENCY, ['address'], [key.currency0])
243
+ planner.add(Actions.CLOSE_CURRENCY, ['address'], [key.currency1])
244
+ }
245
+ if (native) {
246
+ // 附带的主币按上限给,多余的退回
247
+ planner.add(Actions.SWEEP, ['address', 'address'], [ZERO_ADDRESS, ctx.owner])
248
+ }
249
+ const modify = pmInterface.encodeFunctionData('modifyLiquidities', [planner.encode(), ctx.deadline])
250
+ const data = createPrice === null ? modify : pmInterface.encodeFunctionData('multicall', [[pmInterface.encodeFunctionData('initializePool', [keyTuple(key), createPrice]), modify]])
251
+ const value = native ? max0 : 0n
252
+ const approve = await permit2Approvals(
253
+ ctx,
254
+ pm,
255
+ [
256
+ { token: key.currency0, amount: max0 },
257
+ { token: key.currency1, amount: max1 },
258
+ ].filter((i) => i.token !== ZERO_ADDRESS),
259
+ )
260
+ return makePlan(ctx, {
261
+ action: 'add',
262
+ version: 'v4',
263
+ protocol: protocol.id,
264
+ protocolName: protocol.name,
265
+ pool: pool.poolId,
266
+ createPool: createPrice !== null,
267
+ token0: t0,
268
+ token1: t1,
269
+ tokenId: tokenId === null ? null : tokenId.toString(),
270
+ tickLower,
271
+ tickUpper,
272
+ tick: pool.tick,
273
+ ...priceFields(t0, t1, pool.sqrtPriceX96, tickLower, tickUpper),
274
+ liquidity: liquidity.toString(),
275
+ amount0: amount0.toString(),
276
+ amount1: amount1.toString(),
277
+ amount0Max: max0.toString(),
278
+ amount1Max: max1.toString(),
279
+ amount0Min: null,
280
+ amount1Min: null,
281
+ fees0: fees[0].toString(),
282
+ fees1: fees[1].toString(),
283
+ native: native ? 'token0' : null,
284
+ value: value.toString(),
285
+ transactions: [...approve, { kind: 'add', to: pm, data, value: value.toString() }],
286
+ })
287
+ }
288
+
289
+ export async function v4Remove(ctx: Ctx, params: RemoveLiquidityParams, collectOnly = false): Promise<LiquidityPlan> {
290
+ const position = await readPosition(ctx, params.position)
291
+ assertOwner(ctx, position)
292
+ const { pm, pool, tickLower, tickUpper, tokenId } = position
293
+ const { key } = pool
294
+ let liquidity = 0n
295
+ if (!collectOnly) {
296
+ if (position.liquidity === 0n) {
297
+ throw new Error(`Position ${tokenId} has no liquidity`)
298
+ }
299
+ liquidity = removeAmount(position.liquidity, params.percent, params.liquidity)
300
+ } else if (position.fees0 === 0n && position.fees1 === 0n) {
301
+ throw new Error(`Position ${tokenId} has no fees to collect`)
302
+ }
303
+ const burn = !collectOnly && !!params.burn
304
+ if (burn && liquidity !== position.liquidity) {
305
+ throw new Error('burn: true needs the whole position to be removed')
306
+ }
307
+ const [t0, t1] = (await tokenInfo(ctx, [key.currency0, key.currency1])) as [LpToken, LpToken]
308
+ const sqrtA = getSqrtRatioAtTick(tickLower)
309
+ const sqrtB = getSqrtRatioAtTick(tickUpper)
310
+ const [amount0, amount1] = getAmountsForLiquidity(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity)
311
+ const [min0, min1] = getMinAmounts(pool.sqrtPriceX96, sqrtA, sqrtB, liquidity, ctx.slippage)
312
+ const planner = new Planner()
313
+ if (burn) {
314
+ planner.add(Actions.BURN_POSITION, ['uint256', 'uint128', 'uint128', 'bytes'], [tokenId, min0, min1, hookData(ctx)])
315
+ } else {
316
+ // 流动性为 0 时只领取手续费
317
+ planner.add(Actions.DECREASE_LIQUIDITY, ['uint256', 'uint256', 'uint128', 'uint128', 'bytes'], [tokenId, liquidity, min0, min1, hookData(ctx)])
318
+ }
319
+ planner.add(Actions.TAKE_PAIR, ['address', 'address', 'address'], [key.currency0, key.currency1, ctx.recipient])
320
+ const data = pmInterface.encodeFunctionData('modifyLiquidities', [planner.encode(), ctx.deadline])
321
+ return makePlan(ctx, {
322
+ action: collectOnly ? 'collect' : 'remove',
323
+ version: 'v4',
324
+ protocol: position.protocol.id,
325
+ protocolName: position.protocol.name,
326
+ pool: pool.poolId,
327
+ createPool: false,
328
+ token0: t0,
329
+ token1: t1,
330
+ tokenId: tokenId.toString(),
331
+ tickLower,
332
+ tickUpper,
333
+ tick: pool.tick,
334
+ ...priceFields(t0, t1, pool.sqrtPriceX96, tickLower, tickUpper),
335
+ liquidity: liquidity.toString(),
336
+ amount0: amount0.toString(),
337
+ amount1: amount1.toString(),
338
+ amount0Max: null,
339
+ amount1Max: null,
340
+ amount0Min: min0.toString(),
341
+ amount1Min: min1.toString(),
342
+ fees0: position.fees0.toString(),
343
+ fees1: position.fees1.toString(),
344
+ native: key.currency0 === ZERO_ADDRESS ? 'token0' : null,
345
+ value: '0',
346
+ transactions: [{ kind: collectOnly ? 'collect' : 'remove', to: pm, data, value: '0' }],
347
+ })
348
+ }