@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
package/src/lpMath.ts ADDED
@@ -0,0 +1,336 @@
1
+ /**
2
+ * 集中流动性(Uniswap V3 / V4 及其分叉)的数学:tick ↔ sqrtPrice、按流动性算代币数量、未领取的手续费。
3
+ * 与 Uniswap 合约里的整数算法逐位一致(TickMath、LiquidityAmounts、Position 的手续费计算)。
4
+ */
5
+
6
+ export const MIN_TICK = -887272
7
+ export const MAX_TICK = 887272
8
+
9
+ const Q96 = 2n ** 96n
10
+ const Q128 = 2n ** 128n
11
+ const Q256 = 2n ** 256n
12
+ const MAX_UINT256 = Q256 - 1n
13
+
14
+ /** TickMath.getSqrtRatioAtTick 里的常数:sqrt(1.0001)^(-2^i) 的 Q128 定点数 */
15
+ const RATIOS: readonly bigint[] = [
16
+ 0xfff97272373d413259a46990580e213an,
17
+ 0xfff2e50f5f656932ef12357cf3c7fdccn,
18
+ 0xffe5caca7e10e4e61c3624eaa0941cd0n,
19
+ 0xffcb9843d60f6159c9db58835c926644n,
20
+ 0xff973b41fa98c081472e6896dfb254c0n,
21
+ 0xff2ea16466c96a3843ec78b326b52861n,
22
+ 0xfe5dee046a99a2a811c461f1969c3053n,
23
+ 0xfcbe86c7900a88aedcffc83b479aa3a4n,
24
+ 0xf987a7253ac413176f2b074cf7815e54n,
25
+ 0xf3392b0822b70005940c7a398e4b70f3n,
26
+ 0xe7159475a2c29b7443b29c7fa6e889d9n,
27
+ 0xd097f3bdfd2022b8845ad8f792aa5825n,
28
+ 0xa9f746462d870fdf8a65dc1f90e061e5n,
29
+ 0x70d869a156d2a1b890bb3df62baf32f7n,
30
+ 0x31be135f97d08fd981231505542fcfa6n,
31
+ 0x9aa508b5b7a84e1c677de54f3e99bc9n,
32
+ 0x5d6af8dedb81196699c329225ee604n,
33
+ 0x2216e584f5fa1ea926041bedfe98n,
34
+ 0x48a170391f7dc42444e8fa2n,
35
+ ]
36
+
37
+ /** TickMath.getSqrtRatioAtTick:tick → sqrt(1.0001^tick) 的 Q64.96 定点数 */
38
+ export function getSqrtRatioAtTick(tick: number): bigint {
39
+ if (!Number.isInteger(tick) || tick < MIN_TICK || tick > MAX_TICK) {
40
+ throw new RangeError(`tick out of range: ${tick}`)
41
+ }
42
+ const absTick = Math.abs(tick)
43
+ let ratio = absTick & 0x1 ? 0xfffcb933bd6fad37aa2d162d1a594001n : 0x100000000000000000000000000000000n
44
+ for (let i = 0; i < RATIOS.length; i++) {
45
+ if (absTick & (0x2 << i)) {
46
+ ratio = (ratio * (RATIOS[i] as bigint)) >> 128n
47
+ }
48
+ }
49
+ if (tick > 0) {
50
+ ratio = MAX_UINT256 / ratio
51
+ }
52
+ // Q128.128 → Q64.96,向上取整(与合约一致)
53
+ return (ratio >> 32n) + (ratio % (1n << 32n) === 0n ? 0n : 1n)
54
+ }
55
+
56
+ /** SqrtPriceMath.getAmount0Delta(向下取整):区间 [a, b] 内 L 对应的 token0 数量 */
57
+ function amount0Delta(sqrtA: bigint, sqrtB: bigint, liquidity: bigint): bigint {
58
+ return ((liquidity << 96n) * (sqrtB - sqrtA)) / sqrtB / sqrtA
59
+ }
60
+
61
+ /** SqrtPriceMath.getAmount1Delta(向下取整):区间 [a, b] 内 L 对应的 token1 数量 */
62
+ function amount1Delta(sqrtA: bigint, sqrtB: bigint, liquidity: bigint): bigint {
63
+ return (liquidity * (sqrtB - sqrtA)) / Q96
64
+ }
65
+
66
+ /**
67
+ * LiquidityAmounts.getAmountsForLiquidity:当前价格 sqrtPrice 下,价格区间 [sqrtA, sqrtB] 内流动性 L 对应的两种代币数量
68
+ * (与移除全部流动性时拿到的数量一致,向下取整)
69
+ */
70
+ export function getAmountsForLiquidity(sqrtPrice: bigint, sqrtA: bigint, sqrtB: bigint, liquidity: bigint): [bigint, bigint] {
71
+ if (sqrtA > sqrtB) {
72
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
73
+ }
74
+ if (liquidity === 0n) {
75
+ return [0n, 0n]
76
+ }
77
+ if (sqrtPrice <= sqrtA) {
78
+ return [amount0Delta(sqrtA, sqrtB, liquidity), 0n]
79
+ }
80
+ if (sqrtPrice < sqrtB) {
81
+ return [amount0Delta(sqrtPrice, sqrtB, liquidity), amount1Delta(sqrtA, sqrtPrice, liquidity)]
82
+ }
83
+ return [0n, amount1Delta(sqrtA, sqrtB, liquidity)]
84
+ }
85
+
86
+ const wrap = (x: bigint) => ((x % Q256) + Q256) % Q256
87
+
88
+ /**
89
+ * 区间内的累计手续费增长(Tick.getFeeGrowthInside,按 uint256 回绕)。
90
+ * global:池子的 feeGrowthGlobalX128;lowerOutside / upperOutside:两端 tick 的 feeGrowthOutsideX128
91
+ */
92
+ export function getFeeGrowthInside(tick: number, tickLower: number, tickUpper: number, global: bigint, lowerOutside: bigint, upperOutside: bigint): bigint {
93
+ const below = tick >= tickLower ? lowerOutside : wrap(global - lowerOutside)
94
+ const above = tick < tickUpper ? upperOutside : wrap(global - upperOutside)
95
+ return wrap(global - below - above)
96
+ }
97
+
98
+ /** 自上次结算以来新产生的手续费:L × (inside − insideLast) / 2^128(差值按 uint256 回绕,与合约一致) */
99
+ export function getFeesEarned(liquidity: bigint, feeGrowthInside: bigint, feeGrowthInsideLast: bigint): bigint {
100
+ return (liquidity * wrap(feeGrowthInside - feeGrowthInsideLast)) / Q128
101
+ }
102
+
103
+ /** sqrtPriceX96 → token1 / token0 的价格(按精度换算后的人类可读数字,有浮点误差,只用于展示) */
104
+ export function sqrtPriceToPrice(sqrtPriceX96: bigint, decimals0: number, decimals1: number): number {
105
+ // 全程浮点:sqrtPrice 不超过 2^160,转 Number 只丢末尾的位(相对误差约 1e-16);先除 2^96 再平方,价格很小时也不会变成 0
106
+ const ratio = Number(sqrtPriceX96) / 2 ** 96
107
+ return ratio * ratio * 10 ** (decimals0 - decimals1)
108
+ }
109
+
110
+ /** tick → token1 / token0 的价格(按精度换算,只用于展示) */
111
+ export function tickToPrice(tick: number, decimals0: number, decimals1: number): number {
112
+ return 1.0001 ** tick * 10 ** (decimals0 - decimals1)
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // 添加 / 移除流动性用到的计算
117
+ // ---------------------------------------------------------------------------
118
+
119
+ /** getSqrtRatioAtTick(MIN_TICK) / getSqrtRatioAtTick(MAX_TICK):池子价格的上下限(价格必须在 [MIN, MAX) 内) */
120
+ export const MIN_SQRT_RATIO = 4295128739n
121
+ export const MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342n
122
+ export const MAX_UINT128 = 2n ** 128n - 1n
123
+
124
+ const divUp = (a: bigint, b: bigint) => (a + b - 1n) / b
125
+
126
+ /** SqrtPriceMath.getAmount0Delta:区间 [a, b] 内 L 对应的 token0,roundUp 时向上取整(添加流动性时池子收取的数量) */
127
+ export function getAmount0Delta(sqrtA: bigint, sqrtB: bigint, liquidity: bigint, roundUp: boolean): bigint {
128
+ if (sqrtA > sqrtB) {
129
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
130
+ }
131
+ const n1 = liquidity << 96n
132
+ const n2 = sqrtB - sqrtA
133
+ return roundUp ? divUp(divUp(n1 * n2, sqrtB), sqrtA) : (n1 * n2) / sqrtB / sqrtA
134
+ }
135
+
136
+ /** SqrtPriceMath.getAmount1Delta:区间 [a, b] 内 L 对应的 token1,roundUp 时向上取整 */
137
+ export function getAmount1Delta(sqrtA: bigint, sqrtB: bigint, liquidity: bigint, roundUp: boolean): bigint {
138
+ if (sqrtA > sqrtB) {
139
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
140
+ }
141
+ return roundUp ? divUp(liquidity * (sqrtB - sqrtA), Q96) : (liquidity * (sqrtB - sqrtA)) / Q96
142
+ }
143
+
144
+ /** 添加流动性 L 时池子收取的两种代币数量(向上取整,与池子的 modifyPosition 一致) */
145
+ export function getMintAmounts(sqrtPrice: bigint, sqrtA: bigint, sqrtB: bigint, liquidity: bigint): [bigint, bigint] {
146
+ if (sqrtA > sqrtB) {
147
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
148
+ }
149
+ if (sqrtPrice <= sqrtA) {
150
+ return [getAmount0Delta(sqrtA, sqrtB, liquidity, true), 0n]
151
+ }
152
+ if (sqrtPrice < sqrtB) {
153
+ return [getAmount0Delta(sqrtPrice, sqrtB, liquidity, true), getAmount1Delta(sqrtA, sqrtPrice, liquidity, true)]
154
+ }
155
+ return [0n, getAmount1Delta(sqrtA, sqrtB, liquidity, true)]
156
+ }
157
+
158
+ /** LiquidityAmounts.getLiquidityForAmount0:区间 [a, b] 内 amount0 个 token0 对应的流动性(向下取整) */
159
+ export function getLiquidityForAmount0(sqrtA: bigint, sqrtB: bigint, amount0: bigint): bigint {
160
+ if (sqrtA > sqrtB) {
161
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
162
+ }
163
+ return (amount0 * ((sqrtA * sqrtB) / Q96)) / (sqrtB - sqrtA)
164
+ }
165
+
166
+ /** LiquidityAmounts.getLiquidityForAmount1:区间 [a, b] 内 amount1 个 token1 对应的流动性(向下取整) */
167
+ export function getLiquidityForAmount1(sqrtA: bigint, sqrtB: bigint, amount1: bigint): bigint {
168
+ if (sqrtA > sqrtB) {
169
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
170
+ }
171
+ return (amount1 * Q96) / (sqrtB - sqrtA)
172
+ }
173
+
174
+ /**
175
+ * LiquidityAmounts.getLiquidityForAmounts:最多用 amount0 / amount1 时能添加的流动性(取两边较小的,与 V3 仓位合约一致)。
176
+ * 价格在区间下方只用 token0,上方只用 token1
177
+ */
178
+ export function getLiquidityForAmounts(sqrtPrice: bigint, sqrtA: bigint, sqrtB: bigint, amount0: bigint, amount1: bigint): bigint {
179
+ if (sqrtA > sqrtB) {
180
+ ;[sqrtA, sqrtB] = [sqrtB, sqrtA]
181
+ }
182
+ if (sqrtPrice <= sqrtA) {
183
+ return getLiquidityForAmount0(sqrtA, sqrtB, amount0)
184
+ }
185
+ if (sqrtPrice < sqrtB) {
186
+ const l0 = getLiquidityForAmount0(sqrtPrice, sqrtB, amount0)
187
+ const l1 = getLiquidityForAmount1(sqrtA, sqrtPrice, amount1)
188
+ return l0 < l1 ? l0 : l1
189
+ }
190
+ return getLiquidityForAmount1(sqrtA, sqrtB, amount1)
191
+ }
192
+
193
+ /** 流动性是 uint128:超出时报错(合约里同样会 revert) */
194
+ export function toUint128(liquidity: bigint): bigint {
195
+ if (liquidity < 0n || liquidity > MAX_UINT128) {
196
+ throw new RangeError(`liquidity out of uint128 range: ${liquidity}`)
197
+ }
198
+ return liquidity
199
+ }
200
+
201
+ /** TickMath.getTickAtSqrtRatio:满足 getSqrtRatioAtTick(tick) <= sqrtPrice 的最大 tick(精确,二分查找) */
202
+ export function getTickAtSqrtRatio(sqrtPriceX96: bigint): number {
203
+ if (sqrtPriceX96 < MIN_SQRT_RATIO || sqrtPriceX96 >= MAX_SQRT_RATIO) {
204
+ throw new RangeError(`sqrtPriceX96 out of range: ${sqrtPriceX96}`)
205
+ }
206
+ let lo = MIN_TICK
207
+ let hi = MAX_TICK
208
+ while (lo < hi) {
209
+ const mid = Math.floor((lo + hi + 1) / 2)
210
+ if (getSqrtRatioAtTick(mid) <= sqrtPriceX96) {
211
+ lo = mid
212
+ } else {
213
+ hi = mid - 1
214
+ }
215
+ }
216
+ return lo
217
+ }
218
+
219
+ /** 整数平方根(向下取整) */
220
+ export function sqrtBigInt(value: bigint): bigint {
221
+ if (value < 0n) {
222
+ throw new RangeError('square root of a negative number')
223
+ }
224
+ if (value < 2n) {
225
+ return value
226
+ }
227
+ // 牛顿迭代:从不小于平方根的 2^ceil(位数 / 2) 开始单调递减,收敛到向下取整的平方根
228
+ let x = 1n << BigInt((value.toString(2).length + 1) >> 1)
229
+ for (;;) {
230
+ const y = (x + value / x) >> 1n
231
+ if (y >= x) {
232
+ break
233
+ }
234
+ x = y
235
+ }
236
+ return x
237
+ }
238
+
239
+ /** 十进制数('1.5'、'2e-7'、1.5)→ 分子 / 分母,精确,不经过浮点 */
240
+ export function parseDecimal(value: string | number): { num: bigint; den: bigint } {
241
+ const text = typeof value === 'number' ? (Number.isFinite(value) ? String(value) : '') : value.trim()
242
+ const m = /^\+?(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/.exec(text)
243
+ if (!m || (!m[1] && !m[2])) {
244
+ throw new Error(`Invalid number: ${String(value)}`)
245
+ }
246
+ const frac = m[2] ?? ''
247
+ const exp = Number(m[3] ?? 0) - frac.length
248
+ if (Math.abs(exp) > 400) {
249
+ throw new RangeError(`Number out of range: ${String(value)}`)
250
+ }
251
+ const digits = BigInt(`${m[1] ?? ''}${frac}` || '0')
252
+ return exp >= 0 ? { num: digits * 10n ** BigInt(exp), den: 1n } : { num: digits, den: 10n ** BigInt(-exp) }
253
+ }
254
+
255
+ /**
256
+ * 价格(token1 / token0,按精度换算后的人类可读数字,如 600 表示 1 个 token0 值 600 个 token1)→ sqrtPriceX96。
257
+ * 精确计算(字符串不经过浮点),结果向下取整;价格必须大于 0
258
+ */
259
+ export function priceToSqrtPriceX96(price: string | number, decimals0: number, decimals1: number): bigint {
260
+ const { num, den } = parseDecimal(price)
261
+ return ratioToSqrtPriceX96(num, den, decimals0, decimals1)
262
+ }
263
+
264
+ /** 价格(分数 num / den,token1 / token0,按精度换算)→ sqrtPriceX96,向下取整;价格必须大于 0 */
265
+ export function ratioToSqrtPriceX96(num: bigint, den: bigint, decimals0: number, decimals1: number): bigint {
266
+ if (num <= 0n || den <= 0n) {
267
+ throw new RangeError('price must be greater than 0')
268
+ }
269
+ // raw = price × 10^(d1 − d0);sqrtPriceX96 = sqrt(raw × 2^192)
270
+ const scale = decimals1 - decimals0
271
+ const n = num * (scale > 0 ? 10n ** BigInt(scale) : 1n) * 2n ** 192n
272
+ const d = den * (scale < 0 ? 10n ** BigInt(-scale) : 1n)
273
+ return sqrtBigInt(n / d)
274
+ }
275
+
276
+ /** sqrtPriceX96 → tick(getTickAtSqrtRatio),超出池子价格范围时取 MIN_TICK / MAX_TICK */
277
+ export function clampedTickAtSqrtRatio(sqrtPrice: bigint): number {
278
+ if (sqrtPrice < MIN_SQRT_RATIO) {
279
+ return MIN_TICK
280
+ }
281
+ if (sqrtPrice >= MAX_SQRT_RATIO) {
282
+ return MAX_TICK
283
+ }
284
+ return getTickAtSqrtRatio(sqrtPrice)
285
+ }
286
+
287
+ /** 价格(token1 / token0,按精度换算)→ tick:满足 tickToPrice(tick) <= price 的最大 tick,限制在 [MIN_TICK, MAX_TICK] 内 */
288
+ export function priceToTick(price: string | number, decimals0: number, decimals1: number): number {
289
+ return clampedTickAtSqrtRatio(priceToSqrtPriceX96(price, decimals0, decimals1))
290
+ }
291
+
292
+ /** 最近的可用 tick(tickSpacing 的整数倍),限制在 [MIN_TICK, MAX_TICK] 内 */
293
+ export function nearestUsableTick(tick: number, tickSpacing: number): number {
294
+ if (!Number.isInteger(tickSpacing) || tickSpacing <= 0) {
295
+ throw new RangeError(`invalid tickSpacing: ${tickSpacing}`)
296
+ }
297
+ const rounded = Math.round(tick / tickSpacing) * tickSpacing
298
+ if (rounded < MIN_TICK) {
299
+ return rounded + tickSpacing
300
+ }
301
+ if (rounded > MAX_TICK) {
302
+ return rounded - tickSpacing
303
+ }
304
+ return rounded
305
+ }
306
+
307
+ /**
308
+ * 价格按 slippage 上下浮动后的 sqrtPrice:[sqrtPrice × sqrt(1 − s), sqrtPrice × sqrt(1 + s)],限制在池子允许的价格范围内。
309
+ * slippage 是比例(0.005 = 0.5%),精确到百万分之一
310
+ */
311
+ export function sqrtPriceBounds(sqrtPriceX96: bigint, slippage: number): [bigint, bigint] {
312
+ if (!(slippage >= 0 && slippage < 1)) {
313
+ throw new RangeError(`slippage must be in [0, 1): ${slippage}`)
314
+ }
315
+ const ppm = BigInt(Math.round(slippage * 1e6))
316
+ const square = sqrtPriceX96 * sqrtPriceX96
317
+ const lower = sqrtBigInt((square * (1_000_000n - ppm)) / 1_000_000n)
318
+ const upper = sqrtBigInt((square * (1_000_000n + ppm)) / 1_000_000n)
319
+ const clamp = (x: bigint) => (x <= MIN_SQRT_RATIO ? MIN_SQRT_RATIO + 1n : x >= MAX_SQRT_RATIO ? MAX_SQRT_RATIO - 1n : x)
320
+ return [clamp(lower), clamp(upper)]
321
+ }
322
+
323
+ /**
324
+ * 价格在 slippage 范围内变动时,流动性 L 至少对应多少代币(移除流动性 / V3 添加时的最小数量,向下取整)。
325
+ * token0 随价格上涨而减少,取上限价格时的数量;token1 取下限价格时的
326
+ */
327
+ export function getMinAmounts(sqrtPriceX96: bigint, sqrtA: bigint, sqrtB: bigint, liquidity: bigint, slippage: number): [bigint, bigint] {
328
+ const [lower, upper] = sqrtPriceBounds(sqrtPriceX96, slippage)
329
+ return [getAmountsForLiquidity(upper, sqrtA, sqrtB, liquidity)[0], getAmountsForLiquidity(lower, sqrtA, sqrtB, liquidity)[1]]
330
+ }
331
+
332
+ /** 价格在 slippage 范围内变动时,添加流动性 L 最多需要多少代币(V4 的 amountMax,向上取整) */
333
+ export function getMaxAmounts(sqrtPriceX96: bigint, sqrtA: bigint, sqrtB: bigint, liquidity: bigint, slippage: number): [bigint, bigint] {
334
+ const [lower, upper] = sqrtPriceBounds(sqrtPriceX96, slippage)
335
+ return [getMintAmounts(lower, sqrtA, sqrtB, liquidity)[0], getMintAmounts(upper, sqrtA, sqrtB, liquidity)[1]]
336
+ }