|
|
@@ -4,11 +4,18 @@ import BN from 'bn.js'
|
|
|
import { Decimal } from 'decimal.js'
|
|
|
import bs58 from 'bs58'
|
|
|
import { chain } from '@/lib/config'
|
|
|
+import { TickMath } from '@/lib/byreal-clmm-sdk/src/instructions/utils/tickMath'
|
|
|
|
|
|
export async function POST(request: NextRequest) {
|
|
|
try {
|
|
|
const body = await request.json()
|
|
|
- const { positionAddress, maxUsdValue, nftMintAddress } = body
|
|
|
+ const {
|
|
|
+ positionAddress,
|
|
|
+ maxUsdValue,
|
|
|
+ nftMintAddress,
|
|
|
+ priceLower: priceLowerPct = 0,
|
|
|
+ priceUpper: priceUpperPct = 0,
|
|
|
+ } = body
|
|
|
|
|
|
if (!positionAddress) {
|
|
|
return NextResponse.json(
|
|
|
@@ -41,8 +48,9 @@ export async function POST(request: NextRequest) {
|
|
|
return NextResponse.json({ error: 'Position not found' }, { status: 404 })
|
|
|
}
|
|
|
|
|
|
- const { rawPoolInfo, priceLower, priceUpper } = positionInfo
|
|
|
+ const { rawPoolInfo } = positionInfo
|
|
|
const poolInfo = rawPoolInfo
|
|
|
+ const tickSpacing = poolInfo.tickSpacing
|
|
|
|
|
|
// 使用 currentPrice (A/B 价格) 来计算 token 的 USD 价格
|
|
|
// currentPrice 是 tokenA / tokenB 的比率
|
|
|
@@ -87,9 +95,69 @@ export async function POST(request: NextRequest) {
|
|
|
console.log('tokenBPriceUsd', tokenBPriceUsd)
|
|
|
|
|
|
// 计算比例,使得总价值不超过 maxUsdValue
|
|
|
- // 使用 position 的 tickLower 和 tickUpper
|
|
|
- const tickLower = positionInfo.rawPositionInfo.tickLower
|
|
|
- const tickUpper = positionInfo.rawPositionInfo.tickUpper
|
|
|
+ // tickLower/tickUpper 默认使用父仓位的范围;当传入 priceLower/priceUpper(百分比) 且都不为 0 时覆盖
|
|
|
+ let tickLower = positionInfo.rawPositionInfo.tickLower
|
|
|
+ let tickUpper = positionInfo.rawPositionInfo.tickUpper
|
|
|
+
|
|
|
+ let priceLower = positionInfo.priceLower
|
|
|
+ let priceUpper = positionInfo.priceUpper
|
|
|
+
|
|
|
+ const shouldOverrideTicks =
|
|
|
+ Number(priceLowerPct) !== 0 && Number(priceUpperPct) !== 0
|
|
|
+
|
|
|
+ if (shouldOverrideTicks) {
|
|
|
+ const currentPriceDecimal = new Decimal(currentPrice)
|
|
|
+ // 语义:
|
|
|
+ // - priceLower: 当前价格下拉百分比(10 表示下拉 10%,即 current * 0.9)
|
|
|
+ // - priceUpper: 当前价格上浮百分比(10 表示上浮 10%,即 current * 1.1)
|
|
|
+ const lowerMultiplier = new Decimal(1).sub(
|
|
|
+ new Decimal(priceLowerPct).div(100)
|
|
|
+ )
|
|
|
+ const upperMultiplier = new Decimal(1).add(
|
|
|
+ new Decimal(priceUpperPct).div(100)
|
|
|
+ )
|
|
|
+
|
|
|
+ if (lowerMultiplier.lte(0) || upperMultiplier.lte(0)) {
|
|
|
+ return NextResponse.json(
|
|
|
+ { error: 'Invalid priceLower/priceUpper percent' },
|
|
|
+ { status: 400 }
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const lowerPrice = currentPriceDecimal.mul(lowerMultiplier)
|
|
|
+ const upperPrice = currentPriceDecimal.mul(upperMultiplier)
|
|
|
+
|
|
|
+ // 对齐到 tickSpacing,避免 on-chain 创建失败
|
|
|
+ const alignedLower = TickMath.getTickWithPriceAndTickspacing(
|
|
|
+ lowerPrice,
|
|
|
+ tickSpacing,
|
|
|
+ poolInfo.mintDecimalsA,
|
|
|
+ poolInfo.mintDecimalsB
|
|
|
+ )
|
|
|
+ const alignedUpper = TickMath.getTickWithPriceAndTickspacing(
|
|
|
+ upperPrice,
|
|
|
+ tickSpacing,
|
|
|
+ poolInfo.mintDecimalsA,
|
|
|
+ poolInfo.mintDecimalsB
|
|
|
+ )
|
|
|
+
|
|
|
+ tickLower = Math.min(alignedLower, alignedUpper)
|
|
|
+ tickUpper = Math.max(alignedLower, alignedUpper)
|
|
|
+
|
|
|
+ // 同步 priceLower/priceUpper,保证后续 amount 计算与 tick 范围一致
|
|
|
+ priceLower = TickMath.getPriceFromTick({
|
|
|
+ tick: tickLower,
|
|
|
+ decimalsA: poolInfo.mintDecimalsA,
|
|
|
+ decimalsB: poolInfo.mintDecimalsB,
|
|
|
+ baseIn: true,
|
|
|
+ })
|
|
|
+ priceUpper = TickMath.getPriceFromTick({
|
|
|
+ tick: tickUpper,
|
|
|
+ decimalsA: poolInfo.mintDecimalsA,
|
|
|
+ decimalsB: poolInfo.mintDecimalsB,
|
|
|
+ baseIn: true,
|
|
|
+ })
|
|
|
+ }
|
|
|
|
|
|
// 计算需要投入的金额
|
|
|
// 假设使用较小的 token 作为 base
|
|
|
@@ -347,6 +415,11 @@ export async function POST(request: NextRequest) {
|
|
|
console.log('Tick Upper:', tickUpper)
|
|
|
console.log('Price Lower:', priceLower.toString())
|
|
|
console.log('Price Upper:', priceUpper.toString())
|
|
|
+ if (shouldOverrideTicks) {
|
|
|
+ console.log('\n--- Range Override (Percent) ---')
|
|
|
+ console.log('Input priceLower(% of current):', priceLowerPct)
|
|
|
+ console.log('Input priceUpper(% of current):', priceUpperPct)
|
|
|
+ }
|
|
|
console.log('\n--- Investment Details ---')
|
|
|
console.log('Max USD Value:', maxUsdValue)
|
|
|
console.log('Base Token:', base)
|