@w3lib/solana-batch-call 0.3.0 → 0.5.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 +43 -0
- package/README.md +304 -3
- package/dist/address.d.ts +2 -0
- package/dist/address.d.ts.map +1 -1
- package/dist/address.js +8 -0
- package/dist/address.js.map +1 -1
- package/dist/approvals.d.ts +129 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +221 -0
- package/dist/approvals.js.map +1 -0
- package/dist/fees.d.ts +89 -0
- package/dist/fees.d.ts.map +1 -0
- package/dist/fees.js +166 -0
- package/dist/fees.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +2 -0
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +2 -2
- package/dist/layout.js.map +1 -1
- package/dist/liquidity/amm.d.ts +7 -0
- package/dist/liquidity/amm.d.ts.map +1 -0
- package/dist/liquidity/amm.js +307 -0
- package/dist/liquidity/amm.js.map +1 -0
- package/dist/liquidity/clmm.d.ts +18 -0
- package/dist/liquidity/clmm.d.ts.map +1 -0
- package/dist/liquidity/clmm.js +85 -0
- package/dist/liquidity/clmm.js.map +1 -0
- package/dist/liquidity/common.d.ts +94 -0
- package/dist/liquidity/common.d.ts.map +1 -0
- package/dist/liquidity/common.js +228 -0
- package/dist/liquidity/common.js.map +1 -0
- package/dist/liquidity/index.d.ts +34 -0
- package/dist/liquidity/index.d.ts.map +1 -0
- package/dist/liquidity/index.js +157 -0
- package/dist/liquidity/index.js.map +1 -0
- package/dist/liquidity/orca.d.ts +6 -0
- package/dist/liquidity/orca.d.ts.map +1 -0
- package/dist/liquidity/orca.js +303 -0
- package/dist/liquidity/orca.js.map +1 -0
- package/dist/liquidity/raydiumClmm.d.ts +6 -0
- package/dist/liquidity/raydiumClmm.d.ts.map +1 -0
- package/dist/liquidity/raydiumClmm.js +307 -0
- package/dist/liquidity/raydiumClmm.js.map +1 -0
- package/dist/liquidity/types.d.ts +150 -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/lp.d.ts +53 -15
- package/dist/lp.d.ts.map +1 -1
- package/dist/lp.js +15 -38
- package/dist/lp.js.map +1 -1
- package/dist/lpMath.d.ts +45 -0
- package/dist/lpMath.d.ts.map +1 -1
- package/dist/lpMath.js +189 -0
- package/dist/lpMath.js.map +1 -1
- package/dist/names.d.ts +68 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +257 -0
- package/dist/names.js.map +1 -0
- package/dist/pools.d.ts +9 -0
- package/dist/pools.d.ts.map +1 -0
- package/dist/pools.js +45 -0
- package/dist/pools.js.map +1 -0
- package/dist/portfolio.d.ts +64 -0
- package/dist/portfolio.d.ts.map +1 -0
- package/dist/portfolio.js +57 -0
- package/dist/portfolio.js.map +1 -0
- package/dist/price.d.ts +90 -0
- package/dist/price.d.ts.map +1 -0
- package/dist/price.js +287 -0
- package/dist/price.js.map +1 -0
- package/dist/risk.d.ts +84 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +211 -0
- package/dist/risk.js.map +1 -0
- package/dist/rpc.d.ts +2 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +33 -9
- package/dist/rpc.js.map +1 -1
- package/dist/subpaths/approvals.d.ts +29 -0
- package/dist/subpaths/approvals.d.ts.map +1 -0
- package/dist/subpaths/approvals.js +30 -0
- package/dist/subpaths/approvals.js.map +1 -0
- package/dist/subpaths/fees.d.ts +23 -0
- package/dist/subpaths/fees.d.ts.map +1 -0
- package/dist/subpaths/fees.js +25 -0
- package/dist/subpaths/fees.js.map +1 -0
- package/dist/subpaths/liquidity.d.ts +23 -0
- package/dist/subpaths/liquidity.d.ts.map +1 -0
- package/dist/subpaths/liquidity.js +19 -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/subpaths/names.d.ts +37 -0
- package/dist/subpaths/names.d.ts.map +1 -0
- package/dist/subpaths/names.js +27 -0
- package/dist/subpaths/names.js.map +1 -0
- package/dist/subpaths/price.d.ts +40 -0
- package/dist/subpaths/price.d.ts.map +1 -0
- package/dist/subpaths/price.js +43 -0
- package/dist/subpaths/price.js.map +1 -0
- package/dist/subpaths/risk.d.ts +28 -0
- package/dist/subpaths/risk.d.ts.map +1 -0
- package/dist/subpaths/risk.js +30 -0
- package/dist/subpaths/risk.js.map +1 -0
- package/dist/subpaths/swap.d.ts +22 -0
- package/dist/subpaths/swap.d.ts.map +1 -0
- package/dist/subpaths/swap.js +21 -0
- package/dist/subpaths/swap.js.map +1 -0
- package/dist/subpaths/write.d.ts +1 -1
- package/dist/subpaths/write.d.ts.map +1 -1
- package/dist/subpaths/write.js +1 -1
- package/dist/subpaths/write.js.map +1 -1
- package/dist/swap/amm.d.ts +20 -0
- package/dist/swap/amm.d.ts.map +1 -0
- package/dist/swap/amm.js +77 -0
- package/dist/swap/amm.js.map +1 -0
- package/dist/swap/clmmMath.d.ts +24 -0
- package/dist/swap/clmmMath.d.ts.map +1 -0
- package/dist/swap/clmmMath.js +82 -0
- package/dist/swap/clmmMath.js.map +1 -0
- package/dist/swap/common.d.ts +68 -0
- package/dist/swap/common.d.ts.map +1 -0
- package/dist/swap/common.js +65 -0
- package/dist/swap/common.js.map +1 -0
- package/dist/swap/cpmm.d.ts +28 -0
- package/dist/swap/cpmm.d.ts.map +1 -0
- package/dist/swap/cpmm.js +113 -0
- package/dist/swap/cpmm.js.map +1 -0
- package/dist/swap/index.d.ts +145 -0
- package/dist/swap/index.d.ts.map +1 -0
- package/dist/swap/index.js +349 -0
- package/dist/swap/index.js.map +1 -0
- package/dist/swap/orca.d.ts +50 -0
- package/dist/swap/orca.d.ts.map +1 -0
- package/dist/swap/orca.js +382 -0
- package/dist/swap/orca.js.map +1 -0
- package/dist/swap/raydiumClmm.d.ts +62 -0
- package/dist/swap/raydiumClmm.d.ts.map +1 -0
- package/dist/swap/raydiumClmm.js +447 -0
- package/dist/swap/raydiumClmm.js.map +1 -0
- package/dist/tradeCheck.d.ts +45 -0
- package/dist/tradeCheck.d.ts.map +1 -0
- package/dist/tradeCheck.js +131 -0
- package/dist/tradeCheck.js.map +1 -0
- package/dist/write/instructions.d.ts +7 -0
- package/dist/write/instructions.d.ts.map +1 -1
- package/dist/write/instructions.js +24 -1
- package/dist/write/instructions.js.map +1 -1
- package/dist/write/transaction.d.ts +23 -0
- package/dist/write/transaction.d.ts.map +1 -1
- package/dist/write/transaction.js +50 -0
- package/dist/write/transaction.js.map +1 -1
- package/package.json +41 -2
- package/src/address.ts +9 -0
- package/src/approvals.ts +358 -0
- package/src/fees.ts +259 -0
- package/src/index.ts +1 -1
- package/src/layout.ts +2 -2
- package/src/liquidity/amm.ts +353 -0
- package/src/liquidity/clmm.ts +108 -0
- package/src/liquidity/common.ts +285 -0
- package/src/liquidity/index.ts +177 -0
- package/src/liquidity/orca.ts +344 -0
- package/src/liquidity/raydiumClmm.ts +348 -0
- package/src/liquidity/types.ts +158 -0
- package/src/lp.ts +17 -52
- package/src/lpMath.ts +215 -0
- package/src/names.ts +329 -0
- package/src/pools.ts +43 -0
- package/src/portfolio.ts +124 -0
- package/src/price.ts +366 -0
- package/src/risk.ts +243 -0
- package/src/rpc.ts +36 -9
- package/src/subpaths/approvals.ts +47 -0
- package/src/subpaths/fees.ts +41 -0
- package/src/subpaths/liquidity.ts +45 -0
- package/src/subpaths/lp.ts +9 -0
- package/src/subpaths/names.ts +45 -0
- package/src/subpaths/price.ts +62 -0
- package/src/subpaths/risk.ts +36 -0
- package/src/subpaths/swap.ts +41 -0
- package/src/subpaths/write.ts +2 -0
- package/src/swap/amm.ts +88 -0
- package/src/swap/clmmMath.ts +96 -0
- package/src/swap/common.ts +113 -0
- package/src/swap/cpmm.ts +132 -0
- package/src/swap/index.ts +467 -0
- package/src/swap/orca.ts +397 -0
- package/src/swap/raydiumClmm.ts +464 -0
- package/src/tradeCheck.ts +166 -0
- package/src/write/instructions.ts +26 -1
- package/src/write/transaction.ts +62 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { SolanaLpPosition, SolanaLpProtocol, SolanaLpToken } from '../lp.js'
|
|
2
|
+
import type { SendOptions, WriteResult, WriteStatus } from '../write/send.js'
|
|
3
|
+
import type { Instruction } from '../write/transaction.js'
|
|
4
|
+
|
|
5
|
+
type Amount = bigint | number | string
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 已有的仓位:直接传 getLpPositions 查到的 SolanaLpPosition,或者只传能定位仓位的字段:
|
|
9
|
+
* - Raydium CLMM / Orca Whirlpool:positionMint(仓位 NFT 的 mint)
|
|
10
|
+
* - Raydium AMM v4 / CPMM:pool(池子地址)
|
|
11
|
+
*/
|
|
12
|
+
export interface SolanaPositionRef {
|
|
13
|
+
protocol: SolanaLpProtocol
|
|
14
|
+
positionMint?: string
|
|
15
|
+
pool?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** 往已有仓位追加:两边数量给一边即可(另一边按当前价格算),也可以都给(按较少的一边) */
|
|
19
|
+
export interface SolanaIncreaseLiquidityParams {
|
|
20
|
+
position: SolanaPositionRef | SolanaLpPosition
|
|
21
|
+
/** token0 的数量:amount0(最小单位)和 amount0Formatted(按精度换算,如 '1.5')二选一 */
|
|
22
|
+
amount0?: Amount
|
|
23
|
+
amount0Formatted?: string
|
|
24
|
+
amount1?: Amount
|
|
25
|
+
amount1Formatted?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 新建仓位(Raydium CLMM / Orca:一个新的仓位 NFT;AMM v4 / CPMM:往池子里加流动性,拿到 LP 代币)。
|
|
30
|
+
* 池子用地址指定(Solana 上同一对代币有很多池子,按地址最明确);价格是“1 个 token0 值多少个 token1”(池子的方向)
|
|
31
|
+
*/
|
|
32
|
+
export interface SolanaNewPositionParams {
|
|
33
|
+
protocol: SolanaLpProtocol
|
|
34
|
+
pool: string
|
|
35
|
+
/** 集中流动性:价格区间(token1 / token0,取最近的可用 tick);或直接给 tick;或 fullRange */
|
|
36
|
+
priceLower?: string | number
|
|
37
|
+
priceUpper?: string | number
|
|
38
|
+
tickLower?: number
|
|
39
|
+
tickUpper?: number
|
|
40
|
+
fullRange?: boolean
|
|
41
|
+
amount0?: Amount
|
|
42
|
+
amount0Formatted?: string
|
|
43
|
+
amount1?: Amount
|
|
44
|
+
amount1Formatted?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type SolanaAddLiquidityParams = SolanaIncreaseLiquidityParams | SolanaNewPositionParams
|
|
48
|
+
|
|
49
|
+
export interface SolanaRemoveLiquidityParams {
|
|
50
|
+
position: SolanaPositionRef | SolanaLpPosition
|
|
51
|
+
/** 移除的比例(百分数,(0, 100]),默认 100 */
|
|
52
|
+
percent?: number
|
|
53
|
+
/** 或者直接给移除的数量:集中流动性是 L,AMM 是 LP 代币数量(最小单位) */
|
|
54
|
+
liquidity?: Amount
|
|
55
|
+
/** 集中流动性:全部移除后关闭仓位、销毁 NFT(退回租金),默认 false */
|
|
56
|
+
burn?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface SolanaCollectFeesParams {
|
|
60
|
+
/** 集中流动性的仓位(AMM 的手续费已经计入 LP 份额) */
|
|
61
|
+
position: SolanaPositionRef | SolanaLpPosition
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface SolanaLiquidityOptions {
|
|
65
|
+
/** 价格滑点(比例,0.005 = 0.5%),默认 0.005 */
|
|
66
|
+
slippage?: number
|
|
67
|
+
/**
|
|
68
|
+
* Wrapped SOL 那一边用 SOL 支付 / 收取(自动包装 / 解包),默认 true。
|
|
69
|
+
* 用的是 wSOL 的关联代币账户:原来没有时创建、用完关闭(剩余的 wSOL 换回 SOL);原来就有时只补差额、不关闭
|
|
70
|
+
*/
|
|
71
|
+
native?: boolean
|
|
72
|
+
/** 新建仓位的 NFT 归谁,默认 owner */
|
|
73
|
+
recipient?: string
|
|
74
|
+
/** 取消读取 */
|
|
75
|
+
signal?: AbortSignal
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 一步:要原子执行的一组指令 */
|
|
79
|
+
export interface SolanaLiquidityStep {
|
|
80
|
+
/**
|
|
81
|
+
* - setup:创建代币账户、初始化 tick 数组等准备工作(与主指令能放进同一笔交易时会合并)
|
|
82
|
+
* - add / remove / collect:添加 / 移除流动性、领取手续费
|
|
83
|
+
*/
|
|
84
|
+
kind: 'setup' | 'add' | 'remove' | 'collect'
|
|
85
|
+
instructions: Instruction[]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 添加 / 移除流动性的计划:预计的数量 + 按顺序执行的指令。只读取、不签名,可以当作报价 */
|
|
89
|
+
export interface SolanaLiquidityPlan {
|
|
90
|
+
action: 'add' | 'remove' | 'collect'
|
|
91
|
+
protocol: SolanaLpProtocol
|
|
92
|
+
protocolName: string
|
|
93
|
+
type: 'clmm' | 'amm'
|
|
94
|
+
pool: string
|
|
95
|
+
owner: string
|
|
96
|
+
token0: SolanaLpToken
|
|
97
|
+
token1: SolanaLpToken
|
|
98
|
+
/** 集中流动性:仓位 NFT 的 mint(新建时是新生成的,见 signers)和仓位账户 */
|
|
99
|
+
positionMint: string | null
|
|
100
|
+
position: string | null
|
|
101
|
+
/** AMM:LP 代币的 mint */
|
|
102
|
+
lpMint: string | null
|
|
103
|
+
/** 集中流动性:区间、池子当前的 tick 和价格(token1 / token0,按精度换算,只用于展示) */
|
|
104
|
+
tickLower: number | null
|
|
105
|
+
tickUpper: number | null
|
|
106
|
+
tick: number | null
|
|
107
|
+
price: number | null
|
|
108
|
+
priceLower: number | null
|
|
109
|
+
priceUpper: number | null
|
|
110
|
+
/** 添加 / 移除的流动性:集中流动性是 L;AMM 是 LP 代币数量 */
|
|
111
|
+
liquidity: string
|
|
112
|
+
/** 按当前价格预计存入 / 取出的数量(不含手续费) */
|
|
113
|
+
amount0: string
|
|
114
|
+
amount1: string
|
|
115
|
+
amount0Formatted: string | null
|
|
116
|
+
amount1Formatted: string | null
|
|
117
|
+
/** 添加:最多会用掉的数量(按滑点);移除:null */
|
|
118
|
+
amount0Max: string | null
|
|
119
|
+
amount1Max: string | null
|
|
120
|
+
/** 移除:至少会拿到的数量;添加:null */
|
|
121
|
+
amount0Min: string | null
|
|
122
|
+
amount1Min: string | null
|
|
123
|
+
/** 一并领取的未领取手续费(集中流动性移除 / 领取时);AMM 为 '0' */
|
|
124
|
+
fees0: string
|
|
125
|
+
fees1: string
|
|
126
|
+
fees0Formatted: string | null
|
|
127
|
+
fees1Formatted: string | null
|
|
128
|
+
/** 一并领取的挖矿奖励的代币(集中流动性移除 / 领取时;数量要等交易执行后才知道) */
|
|
129
|
+
rewardMints: string[]
|
|
130
|
+
/** 哪一边用 SOL 支付 / 收取(自动包装 / 解包 wSOL) */
|
|
131
|
+
native: 'token0' | 'token1' | null
|
|
132
|
+
/** 按顺序执行;setup 和主指令能放进同一笔交易时会合并 */
|
|
133
|
+
steps: SolanaLiquidityStep[]
|
|
134
|
+
/**
|
|
135
|
+
* 除 owner 外还需要签名的临时密钥(新建集中流动性仓位时的 NFT mint,32 字节种子)。
|
|
136
|
+
* 自己发送时用它签名主指令所在的交易(如 web3.js 的 Keypair.fromSeed(secretKey))
|
|
137
|
+
*/
|
|
138
|
+
signers: Array<{ address: string; secretKey: Uint8Array }>
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface SolanaLiquiditySendOptions extends SolanaLiquidityOptions, Omit<SendOptions, 'onUpdate' | 'signal'> {
|
|
142
|
+
/** 发送前检查余额够不够(添加时),默认 true */
|
|
143
|
+
checkBalance?: boolean
|
|
144
|
+
onUpdate?: (result: SolanaLiquidityStepResult) => void
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface SolanaLiquidityStepResult extends WriteResult {
|
|
148
|
+
kind: SolanaLiquidityStep['kind']
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface SolanaLiquidityResult {
|
|
152
|
+
plan: SolanaLiquidityPlan
|
|
153
|
+
/** 与 plan.steps 一一对应 */
|
|
154
|
+
results: SolanaLiquidityStepResult[]
|
|
155
|
+
/** 主指令(最后一步)的状态和签名 */
|
|
156
|
+
status: WriteStatus
|
|
157
|
+
signature: string | null
|
|
158
|
+
}
|
package/src/lp.ts
CHANGED
|
@@ -6,15 +6,16 @@ import type { RequestOptions, SolanaClient } from './client.js'
|
|
|
6
6
|
import { formatUnits, parseMint, parseTokenAccount, type AccountInfo } from './layout.js'
|
|
7
7
|
import { AllNodesFailedError, HttpError, RpcError } from './rpc.js'
|
|
8
8
|
import { getAmountsForLiquidity, getFeeGrowthInside, getFeesOwed, orcaSqrtPriceAtTick, raydiumSqrtPriceAtTick, sqrtPriceToPrice, tickToPrice, type TickFeeData } from './lpMath.js'
|
|
9
|
+
import { defillamaPrices, type SolanaPriceSource } from './price.js'
|
|
9
10
|
|
|
10
11
|
export const RAYDIUM_CLMM_PROGRAM_ID = 'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK'
|
|
11
12
|
export const ORCA_WHIRLPOOL_PROGRAM_ID = 'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc'
|
|
12
13
|
export const RAYDIUM_AMM_PROGRAM_ID = '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'
|
|
13
14
|
export const RAYDIUM_CPMM_PROGRAM_ID = 'CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C'
|
|
14
15
|
/** AMM v4 的 LP mint 权限(固定地址) */
|
|
15
|
-
const RAYDIUM_AMM_AUTHORITY = '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1'
|
|
16
|
+
export const RAYDIUM_AMM_AUTHORITY = '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1'
|
|
16
17
|
/** CPMM 的 LP mint 权限:PDA ["vault_and_lp_mint_auth_seed"] */
|
|
17
|
-
const RAYDIUM_CPMM_AUTHORITY = 'GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL'
|
|
18
|
+
export const RAYDIUM_CPMM_AUTHORITY = 'GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL'
|
|
18
19
|
|
|
19
20
|
export type SolanaLpProtocol = 'raydium-clmm' | 'orca-whirlpool' | 'raydium-amm' | 'raydium-cpmm'
|
|
20
21
|
|
|
@@ -25,45 +26,8 @@ export const SOLANA_LP_PROTOCOLS: Readonly<Record<SolanaLpProtocol, { name: stri
|
|
|
25
26
|
'raydium-cpmm': { name: 'Raydium CPMM', type: 'amm', programId: RAYDIUM_CPMM_PROGRAM_ID },
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
|
|
32
|
-
export interface SolanaPriceSource {
|
|
33
|
-
readonly name: string
|
|
34
|
-
/** 返回 mint → 美元价格;没有价格的不返回 */
|
|
35
|
-
prices(ctx: { mints: readonly string[]; fetch: typeof fetch; signal?: AbortSignal }): Promise<Map<string, number>>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** DefiLlama 价格(免费免 Key,按 100 个一批查询);confidence 低于 minConfidence(默认 0.9)的价格不用 */
|
|
39
|
-
export function defillamaPrices(options: { minConfidence?: number } = {}): SolanaPriceSource {
|
|
40
|
-
const minConfidence = options.minConfidence ?? 0.9
|
|
41
|
-
return {
|
|
42
|
-
name: 'defillama',
|
|
43
|
-
async prices({ mints, fetch: fetchFn, signal }) {
|
|
44
|
-
const result = new Map<string, number>()
|
|
45
|
-
const batches: string[][] = []
|
|
46
|
-
for (let i = 0; i < mints.length; i += 100) {
|
|
47
|
-
batches.push(mints.slice(i, i + 100))
|
|
48
|
-
}
|
|
49
|
-
await Promise.all(
|
|
50
|
-
batches.map(async (batch) => {
|
|
51
|
-
const res = await fetchFn(`https://coins.llama.fi/prices/current/${batch.map((m) => `solana:${m}`).join(',')}`, { signal })
|
|
52
|
-
if (!res.ok) {
|
|
53
|
-
throw new Error(`DefiLlama prices: HTTP ${res.status}`)
|
|
54
|
-
}
|
|
55
|
-
const body = (await res.json()) as { coins?: Record<string, { price?: number; confidence?: number }> }
|
|
56
|
-
for (const [key, coin] of Object.entries(body.coins ?? {})) {
|
|
57
|
-
if (typeof coin.price === 'number' && Number.isFinite(coin.price) && (coin.confidence === undefined || coin.confidence >= minConfidence)) {
|
|
58
|
-
result.set(key.slice(key.indexOf(':') + 1), coin.price)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}),
|
|
62
|
-
)
|
|
63
|
-
return result
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
}
|
|
29
|
+
// 价格源在 price.ts(子路径 /price);这里继续导出,旧的 import 不受影响
|
|
30
|
+
export { defillamaPrices, type SolanaPriceSource, type SolanaPriceSourceContext } from './price.js'
|
|
67
31
|
|
|
68
32
|
// ---------------------------------------------------------------------------
|
|
69
33
|
// 结果类型
|
|
@@ -152,7 +116,7 @@ export interface LpPositionsOptions extends RequestOptions {
|
|
|
152
116
|
pools?: readonly string[]
|
|
153
117
|
/** 返回已清空的仓位(流动性为 0、手续费也领完,NFT 还在)。默认 false */
|
|
154
118
|
includeClosed?: boolean
|
|
155
|
-
/** 查美元价值:true 用 DefiLlama,也可以传自己的 SolanaPriceSource
|
|
119
|
+
/** 查美元价值:true 用 DefiLlama,也可以传自己的 SolanaPriceSource(如 /price 的 combinePrices([defillamaPrices(), onchainPrices()]))。默认不查 */
|
|
156
120
|
prices?: boolean | SolanaPriceSource
|
|
157
121
|
/**
|
|
158
122
|
* 价格源出错时调用(服务不可用、被拦截等):这时照样返回仓位,只是 valueUsd / feesUsd 为 null,
|
|
@@ -167,7 +131,8 @@ export interface LpPositionsOptions extends RequestOptions {
|
|
|
167
131
|
// 账户布局(各程序的 Anchor / 原生账户,偏移量含 8 字节判别符)
|
|
168
132
|
// ---------------------------------------------------------------------------
|
|
169
133
|
|
|
170
|
-
|
|
134
|
+
/** 小端读取账户数据(添加 / 移除流动性也用) */
|
|
135
|
+
export class Bytes {
|
|
171
136
|
readonly #view: DataView
|
|
172
137
|
constructor(readonly data: Uint8Array) {
|
|
173
138
|
this.#view = new DataView(data.buffer, data.byteOffset, data.byteLength)
|
|
@@ -300,7 +265,7 @@ function orcaPool(b: Bytes): ClmmPoolState {
|
|
|
300
265
|
}
|
|
301
266
|
}
|
|
302
267
|
|
|
303
|
-
interface AmmPoolState {
|
|
268
|
+
export interface AmmPoolState {
|
|
304
269
|
protocol: 'raydium-amm' | 'raydium-cpmm'
|
|
305
270
|
address: string
|
|
306
271
|
lpMint: string
|
|
@@ -350,7 +315,7 @@ function cpmmPool(address: string, b: Bytes): AmmPoolState {
|
|
|
350
315
|
}
|
|
351
316
|
}
|
|
352
317
|
|
|
353
|
-
function parseAmmPool(account: AccountInfo): AmmPoolState | null {
|
|
318
|
+
export function parseAmmPool(account: AccountInfo): AmmPoolState | null {
|
|
354
319
|
const b = new Bytes(account.data)
|
|
355
320
|
if (account.owner === RAYDIUM_AMM_PROGRAM_ID && account.data.length === AMM_POOL_SIZE) {
|
|
356
321
|
return ammPool(account.address, b)
|
|
@@ -361,14 +326,14 @@ function parseAmmPool(account: AccountInfo): AmmPoolState | null {
|
|
|
361
326
|
return null
|
|
362
327
|
}
|
|
363
328
|
|
|
364
|
-
const utf8 = (s: string) => new TextEncoder().encode(s)
|
|
329
|
+
export const utf8 = (s: string) => new TextEncoder().encode(s)
|
|
365
330
|
|
|
366
331
|
function floorDiv(a: number, b: number): number {
|
|
367
332
|
return Math.floor(a / b) * b
|
|
368
333
|
}
|
|
369
334
|
|
|
370
335
|
/** Raydium tick 数组:PDA ["tick_array", pool, start(i32 大端)],每个数组 60 个 tick */
|
|
371
|
-
function raydiumTickArray(pool: string, tick: number, spacing: number): { address: string; start: number } {
|
|
336
|
+
export function raydiumTickArray(pool: string, tick: number, spacing: number): { address: string; start: number } {
|
|
372
337
|
const start = floorDiv(tick, spacing * RAYDIUM_TICK_ARRAY_SIZE)
|
|
373
338
|
const be = new Uint8Array(4)
|
|
374
339
|
new DataView(be.buffer).setInt32(0, start, false)
|
|
@@ -376,7 +341,7 @@ function raydiumTickArray(pool: string, tick: number, spacing: number): { addres
|
|
|
376
341
|
}
|
|
377
342
|
|
|
378
343
|
/** Orca tick 数组:PDA ["tick_array", whirlpool, start(十进制字符串)],每个数组 88 个 tick */
|
|
379
|
-
function orcaTickArray(pool: string, tick: number, spacing: number): { address: string; start: number } {
|
|
344
|
+
export function orcaTickArray(pool: string, tick: number, spacing: number): { address: string; start: number } {
|
|
380
345
|
const start = floorDiv(tick, spacing * ORCA_TICK_ARRAY_SIZE)
|
|
381
346
|
return { address: findProgramAddress([utf8('tick_array'), decodeAddress(pool), utf8(String(start))], ORCA_WHIRLPOOL_PROGRAM_ID), start }
|
|
382
347
|
}
|
|
@@ -670,9 +635,9 @@ export async function lpPositions(client: SolanaClient, owner: string, options:
|
|
|
670
635
|
|
|
671
636
|
/**
|
|
672
637
|
* 节点不提供这个方法(方法不存在、被拦截、需要付费 / Token、403 等):这时报错里说明怎么绕过。
|
|
673
|
-
*
|
|
638
|
+
* 多节点时所有节点都是这个原因才算;限频、超时、网络不对等原样抛出(资产总值 portfolio 也用)
|
|
674
639
|
*/
|
|
675
|
-
function methodUnavailable(err: unknown): boolean {
|
|
640
|
+
export function methodUnavailable(err: unknown): boolean {
|
|
676
641
|
if (err instanceof AllNodesFailedError) {
|
|
677
642
|
return err.errors.length > 0 && err.errors.every((e) => methodUnavailable(e.error))
|
|
678
643
|
}
|
|
@@ -750,8 +715,8 @@ async function finalize(client: SolanaClient, raw: readonly (RawClmm | RawAmm)[]
|
|
|
750
715
|
return new Map()
|
|
751
716
|
}
|
|
752
717
|
try {
|
|
753
|
-
// wSOL 的价格就是 SOL 的价格,DefiLlama 用 So111…112
|
|
754
|
-
return await priceSource.prices({ mints, fetch: fetchFn, signal })
|
|
718
|
+
// wSOL 的价格就是 SOL 的价格,DefiLlama 用 So111…112 查得到;client 给链上价格源读池子
|
|
719
|
+
return await priceSource.prices({ mints, fetch: fetchFn, signal, client })
|
|
755
720
|
} catch (err) {
|
|
756
721
|
if (signal?.aborted) {
|
|
757
722
|
throw err
|
package/src/lpMath.ts
CHANGED
|
@@ -203,3 +203,218 @@ export function sqrtPriceToPrice(sqrtPrice: bigint, decimals0: number, decimals1
|
|
|
203
203
|
export function tickToPrice(tick: number, decimals0: number, decimals1: number): number {
|
|
204
204
|
return 1.0001 ** tick * 10 ** (decimals0 - decimals1)
|
|
205
205
|
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// 添加 / 移除流动性用到的计算(两个程序公式相同,tick ↔ 价格按各自的换算)
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
/** 两个程序允许的 sqrt 价格范围(Raydium:MIN / MAX_SQRT_PRICE_X64;Orca:MIN / MAX_SQRT_PRICE) */
|
|
212
|
+
export const RAYDIUM_MIN_SQRT_PRICE = 4295048016n
|
|
213
|
+
export const RAYDIUM_MAX_SQRT_PRICE = 79226673521066979257578248091n
|
|
214
|
+
export const ORCA_MIN_SQRT_PRICE = 4295048016n
|
|
215
|
+
export const ORCA_MAX_SQRT_PRICE = 79226673515401279992447579055n
|
|
216
|
+
|
|
217
|
+
export type ClmmProgram = 'raydium' | 'orca'
|
|
218
|
+
|
|
219
|
+
const sqrtAtTick = (program: ClmmProgram) => (program === 'raydium' ? raydiumSqrtPriceAtTick : orcaSqrtPriceAtTick)
|
|
220
|
+
const sqrtRange = (program: ClmmProgram): [bigint, bigint] => (program === 'raydium' ? [RAYDIUM_MIN_SQRT_PRICE, RAYDIUM_MAX_SQRT_PRICE] : [ORCA_MIN_SQRT_PRICE, ORCA_MAX_SQRT_PRICE])
|
|
221
|
+
|
|
222
|
+
/** tick → sqrt 价格(按程序) */
|
|
223
|
+
export function sqrtPriceAtTick(program: ClmmProgram, tick: number): bigint {
|
|
224
|
+
return sqrtAtTick(program)(tick)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** sqrt 价格 → tick:满足 sqrtPriceAtTick(tick) <= sqrtPrice 的最大 tick(二分查找,与程序的 get_tick_at_sqrt_price 一致),限制在 [MIN_TICK, MAX_TICK] 内 */
|
|
228
|
+
export function tickAtSqrtPrice(program: ClmmProgram, sqrtPrice: bigint): number {
|
|
229
|
+
const at = sqrtAtTick(program)
|
|
230
|
+
if (sqrtPrice < at(MIN_TICK)) {
|
|
231
|
+
return MIN_TICK
|
|
232
|
+
}
|
|
233
|
+
let lo = MIN_TICK
|
|
234
|
+
let hi = MAX_TICK
|
|
235
|
+
while (lo < hi) {
|
|
236
|
+
const mid = Math.floor((lo + hi + 1) / 2)
|
|
237
|
+
if (at(mid) <= sqrtPrice) {
|
|
238
|
+
lo = mid
|
|
239
|
+
} else {
|
|
240
|
+
hi = mid - 1
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return lo
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** 向上取整的整数除法 */
|
|
247
|
+
export const ceilDiv = (a: bigint, b: bigint) => (a + b - 1n) / b
|
|
248
|
+
|
|
249
|
+
/** 添加流动性 L 时程序收取的 token0(向上取整):ceil(L · 2^64 · (b − a) / (a · b)) */
|
|
250
|
+
function amount0Up(a: bigint, b: bigint, liquidity: bigint): bigint {
|
|
251
|
+
const [lo, hi] = a < b ? [a, b] : [b, a]
|
|
252
|
+
return ceilDiv((liquidity << 64n) * (hi - lo), lo * hi)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** 添加流动性 L 时程序收取的 token1(向上取整):ceil(L · (b − a) / 2^64) */
|
|
256
|
+
function amount1Up(a: bigint, b: bigint, liquidity: bigint): bigint {
|
|
257
|
+
const [lo, hi] = a < b ? [a, b] : [b, a]
|
|
258
|
+
return ceilDiv(liquidity * (hi - lo), Q64)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** 添加流动性 L 时程序收取的两种代币数量(向上取整;按当前 tick 判断区间,与程序一致) */
|
|
262
|
+
export function getMintAmounts(tick: number, sqrtPrice: bigint, sqrtLower: bigint, sqrtUpper: bigint, liquidity: bigint, tickLower: number, tickUpper: number): [bigint, bigint] {
|
|
263
|
+
if (liquidity === 0n) {
|
|
264
|
+
return [0n, 0n]
|
|
265
|
+
}
|
|
266
|
+
if (tick < tickLower) {
|
|
267
|
+
return [amount0Up(sqrtLower, sqrtUpper, liquidity), 0n]
|
|
268
|
+
}
|
|
269
|
+
if (tick < tickUpper) {
|
|
270
|
+
return [amount0Up(sqrtPrice, sqrtUpper, liquidity), amount1Up(sqrtLower, sqrtPrice, liquidity)]
|
|
271
|
+
}
|
|
272
|
+
return [0n, amount1Up(sqrtLower, sqrtUpper, liquidity)]
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** 区间 [a, b] 内 amount0 个 token0 对应的流动性(向下取整):amount0 · (a · b / 2^64) / (b − a) */
|
|
276
|
+
export function getLiquidityForAmount0(a: bigint, b: bigint, amount0: bigint): bigint {
|
|
277
|
+
const [lo, hi] = a < b ? [a, b] : [b, a]
|
|
278
|
+
return (amount0 * ((lo * hi) / Q64)) / (hi - lo)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** 区间 [a, b] 内 amount1 个 token1 对应的流动性(向下取整):amount1 · 2^64 / (b − a) */
|
|
282
|
+
export function getLiquidityForAmount1(a: bigint, b: bigint, amount1: bigint): bigint {
|
|
283
|
+
const [lo, hi] = a < b ? [a, b] : [b, a]
|
|
284
|
+
return (amount1 * Q64) / (hi - lo)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 最多用 amount0 / amount1 时能添加的流动性:两边都给时取较小的;只给一边时只按这一边算(另一边要多少由 getMintAmounts 算)。
|
|
289
|
+
* 区间在当前价格之外时只需要一种代币,给的是另一种时返回 0
|
|
290
|
+
*/
|
|
291
|
+
export function getLiquidityForAmounts(
|
|
292
|
+
tick: number,
|
|
293
|
+
sqrtPrice: bigint,
|
|
294
|
+
sqrtLower: bigint,
|
|
295
|
+
sqrtUpper: bigint,
|
|
296
|
+
amount0: bigint | undefined,
|
|
297
|
+
amount1: bigint | undefined,
|
|
298
|
+
tickLower: number,
|
|
299
|
+
tickUpper: number,
|
|
300
|
+
): bigint {
|
|
301
|
+
let l0: bigint | null = null
|
|
302
|
+
let l1: bigint | null = null
|
|
303
|
+
// 价格正好在下限(tick == tickLower 且 sqrtPrice == sqrtLower)时也只需要 token0:按下方处理,避免除以 0
|
|
304
|
+
if (tick < tickLower || sqrtPrice <= sqrtLower) {
|
|
305
|
+
l0 = amount0 === undefined ? null : getLiquidityForAmount0(sqrtLower, sqrtUpper, amount0)
|
|
306
|
+
l1 = amount1 === undefined ? null : amount0 === undefined ? 0n : null
|
|
307
|
+
} else if (tick < tickUpper && sqrtPrice < sqrtUpper) {
|
|
308
|
+
// 价格正好在上限(向下穿过 tick 后 tick = tickUpper − 1、sqrtPrice = 上限)时只需要 token1:按上方处理
|
|
309
|
+
l0 = amount0 === undefined ? null : getLiquidityForAmount0(sqrtPrice, sqrtUpper, amount0)
|
|
310
|
+
l1 = amount1 === undefined ? null : getLiquidityForAmount1(sqrtLower, sqrtPrice, amount1)
|
|
311
|
+
} else {
|
|
312
|
+
l1 = amount1 === undefined ? null : getLiquidityForAmount1(sqrtLower, sqrtUpper, amount1)
|
|
313
|
+
l0 = amount0 === undefined ? null : amount1 === undefined ? 0n : null
|
|
314
|
+
}
|
|
315
|
+
if (l0 === null) return l1 ?? 0n
|
|
316
|
+
if (l1 === null) return l0
|
|
317
|
+
return l0 < l1 ? l0 : l1
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** 整数平方根(向下取整,牛顿迭代从不小于平方根的值开始) */
|
|
321
|
+
export function sqrtBigInt(value: bigint): bigint {
|
|
322
|
+
if (value < 0n) {
|
|
323
|
+
throw new RangeError('square root of a negative number')
|
|
324
|
+
}
|
|
325
|
+
if (value < 2n) {
|
|
326
|
+
return value
|
|
327
|
+
}
|
|
328
|
+
let x = 1n << BigInt((value.toString(2).length + 1) >> 1)
|
|
329
|
+
for (;;) {
|
|
330
|
+
const y = (x + value / x) >> 1n
|
|
331
|
+
if (y >= x) {
|
|
332
|
+
return x
|
|
333
|
+
}
|
|
334
|
+
x = y
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** 十进制数('1.5'、'2e-7'、1.5)→ 分子 / 分母,精确,不经过浮点 */
|
|
339
|
+
export function parseDecimal(value: string | number): { num: bigint; den: bigint } {
|
|
340
|
+
const text = typeof value === 'number' ? (Number.isFinite(value) ? String(value) : '') : value.trim()
|
|
341
|
+
const m = /^\+?(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/.exec(text)
|
|
342
|
+
if (!m || (!m[1] && !m[2])) {
|
|
343
|
+
throw new Error(`Invalid number: ${String(value)}`)
|
|
344
|
+
}
|
|
345
|
+
const frac = m[2] ?? ''
|
|
346
|
+
const exp = Number(m[3] ?? 0) - frac.length
|
|
347
|
+
if (Math.abs(exp) > 400) {
|
|
348
|
+
throw new RangeError(`Number out of range: ${String(value)}`)
|
|
349
|
+
}
|
|
350
|
+
const digits = BigInt(`${m[1] ?? ''}${frac}` || '0')
|
|
351
|
+
return exp >= 0 ? { num: digits * 10n ** BigInt(exp), den: 1n } : { num: digits, den: 10n ** BigInt(-exp) }
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** 价格(token1 / token0,按精度换算)→ Q64.64 sqrt 价格(向下取整);num / den 是价格的分子分母 */
|
|
355
|
+
export function ratioToSqrtPrice(num: bigint, den: bigint, decimals0: number, decimals1: number): bigint {
|
|
356
|
+
if (num <= 0n || den <= 0n) {
|
|
357
|
+
throw new RangeError('price must be greater than 0')
|
|
358
|
+
}
|
|
359
|
+
const scale = decimals1 - decimals0
|
|
360
|
+
return sqrtBigInt((num * (scale > 0 ? 10n ** BigInt(scale) : 1n) * 2n ** 128n) / (den * (scale < 0 ? 10n ** BigInt(-scale) : 1n)))
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** 价格(token1 / token0,按精度换算)→ tick(按程序;满足价格 >= tick 价格的最大 tick) */
|
|
364
|
+
export function priceToTick(program: ClmmProgram, price: string | number, decimals0: number, decimals1: number): number {
|
|
365
|
+
const { num, den } = parseDecimal(price)
|
|
366
|
+
return tickAtSqrtPrice(program, ratioToSqrtPrice(num, den, decimals0, decimals1))
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** 最近的可用 tick(tickSpacing 的整数倍),限制在 [MIN_TICK, MAX_TICK] 内 */
|
|
370
|
+
export function nearestUsableTick(tick: number, tickSpacing: number): number {
|
|
371
|
+
if (!Number.isInteger(tickSpacing) || tickSpacing <= 0) {
|
|
372
|
+
throw new RangeError(`invalid tickSpacing: ${tickSpacing}`)
|
|
373
|
+
}
|
|
374
|
+
const rounded = Math.round(tick / tickSpacing) * tickSpacing
|
|
375
|
+
if (rounded < MIN_TICK) {
|
|
376
|
+
return rounded + tickSpacing
|
|
377
|
+
}
|
|
378
|
+
if (rounded > MAX_TICK) {
|
|
379
|
+
return rounded - tickSpacing
|
|
380
|
+
}
|
|
381
|
+
return rounded
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** 价格按 slippage 上下浮动后的 sqrt 价格和对应的 tick,限制在程序允许的范围内(slippage 是比例,精确到百万分之一) */
|
|
385
|
+
export function sqrtPriceBounds(program: ClmmProgram, sqrtPrice: bigint, slippage: number): Array<{ sqrtPrice: bigint; tick: number }> {
|
|
386
|
+
if (!(slippage >= 0 && slippage < 1)) {
|
|
387
|
+
throw new RangeError(`slippage must be in [0, 1): ${slippage}`)
|
|
388
|
+
}
|
|
389
|
+
const [min, max] = sqrtRange(program)
|
|
390
|
+
const ppm = BigInt(Math.round(slippage * 1e6))
|
|
391
|
+
const square = sqrtPrice * sqrtPrice
|
|
392
|
+
return [1_000_000n - ppm, 1_000_000n + ppm].map((factor) => {
|
|
393
|
+
let s = sqrtBigInt((square * factor) / 1_000_000n)
|
|
394
|
+
s = s < min ? min : s >= max ? max - 1n : s
|
|
395
|
+
return { sqrtPrice: s, tick: tickAtSqrtPrice(program, s) }
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** 价格在 slippage 范围内变动时,流动性 L 至少对应多少代币(移除时的最小数量,向下取整) */
|
|
400
|
+
export function getMinAmounts(program: ClmmProgram, sqrtPrice: bigint, tickLower: number, tickUpper: number, liquidity: bigint, slippage: number): [bigint, bigint] {
|
|
401
|
+
const [lower, upper] = sqrtPriceBounds(program, sqrtPrice, slippage) as [{ sqrtPrice: bigint; tick: number }, { sqrtPrice: bigint; tick: number }]
|
|
402
|
+
const a = sqrtPriceAtTick(program, tickLower)
|
|
403
|
+
const b = sqrtPriceAtTick(program, tickUpper)
|
|
404
|
+
// token0 随价格上涨而减少,取上限价格时的;token1 取下限价格时的
|
|
405
|
+
return [
|
|
406
|
+
getAmountsForLiquidity(upper.tick, upper.sqrtPrice, a, b, liquidity, tickLower, tickUpper)[0],
|
|
407
|
+
getAmountsForLiquidity(lower.tick, lower.sqrtPrice, a, b, liquidity, tickLower, tickUpper)[1],
|
|
408
|
+
]
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** 价格在 slippage 范围内变动时,添加流动性 L 最多需要多少代币(amountMax,向上取整) */
|
|
412
|
+
export function getMaxAmounts(program: ClmmProgram, sqrtPrice: bigint, tickLower: number, tickUpper: number, liquidity: bigint, slippage: number): [bigint, bigint] {
|
|
413
|
+
const [lower, upper] = sqrtPriceBounds(program, sqrtPrice, slippage) as [{ sqrtPrice: bigint; tick: number }, { sqrtPrice: bigint; tick: number }]
|
|
414
|
+
const a = sqrtPriceAtTick(program, tickLower)
|
|
415
|
+
const b = sqrtPriceAtTick(program, tickUpper)
|
|
416
|
+
return [
|
|
417
|
+
getMintAmounts(lower.tick, lower.sqrtPrice, a, b, liquidity, tickLower, tickUpper)[0],
|
|
418
|
+
getMintAmounts(upper.tick, upper.sqrtPrice, a, b, liquidity, tickLower, tickUpper)[1],
|
|
419
|
+
]
|
|
420
|
+
}
|