|
@@ -1,4 +1,5 @@
|
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
|
|
|
+import { PublicKey } from '@solana/web3.js'
|
|
|
import {
|
|
import {
|
|
|
getPositionMappings,
|
|
getPositionMappings,
|
|
|
getPositionMappingById,
|
|
getPositionMappingById,
|
|
@@ -6,10 +7,11 @@ import {
|
|
|
updatePositionMappingStatus,
|
|
updatePositionMappingStatus,
|
|
|
} from '@/lib/db/queries'
|
|
} from '@/lib/db/queries'
|
|
|
import { CopyEngine } from '@/lib/copier/index'
|
|
import { CopyEngine } from '@/lib/copier/index'
|
|
|
|
|
+import { getConnection } from '@/lib/solana/connection'
|
|
|
import { getPoolInfoMap, getPoolLabel } from '@/lib/pool-info'
|
|
import { getPoolInfoMap, getPoolLabel } from '@/lib/pool-info'
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
|
-const { TickMath } = require('@/lib/clmm-sdk/dist/index.js')
|
|
|
|
|
|
|
+const { TickMath, LiquidityMath, SqrtPriceMath, Chain, BYREAL_CLMM_PROGRAM_ID } = require('@/lib/clmm-sdk/dist/index.js')
|
|
|
|
|
|
|
|
export async function GET(req: NextRequest) {
|
|
export async function GET(req: NextRequest) {
|
|
|
const status = req.nextUrl.searchParams.get('status') || undefined
|
|
const status = req.nextUrl.searchParams.get('status') || undefined
|
|
@@ -18,46 +20,90 @@ export async function GET(req: NextRequest) {
|
|
|
// Enrich positions with pool info and price range
|
|
// Enrich positions with pool info and price range
|
|
|
const poolMap = await getPoolInfoMap()
|
|
const poolMap = await getPoolInfoMap()
|
|
|
|
|
|
|
|
- const enriched = positions.map((pos) => {
|
|
|
|
|
- const pool = poolMap.get(pos.pool_id)
|
|
|
|
|
- let poolLabel = ''
|
|
|
|
|
- let priceLower = ''
|
|
|
|
|
- let priceUpper = ''
|
|
|
|
|
-
|
|
|
|
|
- if (pool) {
|
|
|
|
|
- poolLabel = getPoolLabel(pool)
|
|
|
|
|
- const decimalsA = pool.mintA.mintInfo.decimals
|
|
|
|
|
- const decimalsB = pool.mintB.mintInfo.decimals
|
|
|
|
|
- const baseIn = !pool.displayReversed
|
|
|
|
|
-
|
|
|
|
|
- // Calculate price from tick using SDK TickMath
|
|
|
|
|
- try {
|
|
|
|
|
- const lower = TickMath.getPriceFromTick({
|
|
|
|
|
- tick: pos.tick_lower,
|
|
|
|
|
- decimalsA,
|
|
|
|
|
- decimalsB,
|
|
|
|
|
- baseIn,
|
|
|
|
|
- })
|
|
|
|
|
- const upper = TickMath.getPriceFromTick({
|
|
|
|
|
- tick: pos.tick_upper,
|
|
|
|
|
- decimalsA,
|
|
|
|
|
- decimalsB,
|
|
|
|
|
- baseIn,
|
|
|
|
|
- })
|
|
|
|
|
- priceLower = lower.toString()
|
|
|
|
|
- priceUpper = upper.toString()
|
|
|
|
|
- } catch {
|
|
|
|
|
- // Fallback: show tick values if price calculation fails
|
|
|
|
|
|
|
+ // Fetch on-chain position sizes for active positions
|
|
|
|
|
+ const connection = getConnection()
|
|
|
|
|
+ const chain = new Chain({ connection, programId: BYREAL_CLMM_PROGRAM_ID })
|
|
|
|
|
+
|
|
|
|
|
+ const enriched = await Promise.all(
|
|
|
|
|
+ positions.map(async (pos) => {
|
|
|
|
|
+ const pool = poolMap.get(pos.pool_id)
|
|
|
|
|
+ let poolLabel = ''
|
|
|
|
|
+ let priceLower = ''
|
|
|
|
|
+ let priceUpper = ''
|
|
|
|
|
+ let sizeUsd = ''
|
|
|
|
|
+ let amountA = ''
|
|
|
|
|
+ let amountB = ''
|
|
|
|
|
+
|
|
|
|
|
+ if (pool) {
|
|
|
|
|
+ poolLabel = getPoolLabel(pool)
|
|
|
|
|
+ const decimalsA = pool.mintA.mintInfo.decimals
|
|
|
|
|
+ const decimalsB = pool.mintB.mintInfo.decimals
|
|
|
|
|
+ const baseIn = !pool.displayReversed
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate price from tick using SDK TickMath
|
|
|
|
|
+ try {
|
|
|
|
|
+ const lower = TickMath.getPriceFromTick({
|
|
|
|
|
+ tick: pos.tick_lower,
|
|
|
|
|
+ decimalsA,
|
|
|
|
|
+ decimalsB,
|
|
|
|
|
+ baseIn,
|
|
|
|
|
+ })
|
|
|
|
|
+ const upper = TickMath.getPriceFromTick({
|
|
|
|
|
+ tick: pos.tick_upper,
|
|
|
|
|
+ decimalsA,
|
|
|
|
|
+ decimalsB,
|
|
|
|
|
+ baseIn,
|
|
|
|
|
+ })
|
|
|
|
|
+ priceLower = lower.toString()
|
|
|
|
|
+ priceUpper = upper.toString()
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ // Fallback: show tick values if price calculation fails
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate position size for active positions with our NFT
|
|
|
|
|
+ if (pos.status === 'active' && pos.our_nft_mint) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const posInfo = await chain.getRawPositionInfoByNftMint(new PublicKey(pos.our_nft_mint))
|
|
|
|
|
+ if (posInfo && !posInfo.liquidity.isZero()) {
|
|
|
|
|
+ const poolInfo = await chain.getRawPoolInfoByPoolId(new PublicKey(pos.pool_id))
|
|
|
|
|
+ const sqrtPriceLower = SqrtPriceMath.getSqrtPriceX64FromTick(pos.tick_lower)
|
|
|
|
|
+ const sqrtPriceUpper = SqrtPriceMath.getSqrtPriceX64FromTick(pos.tick_upper)
|
|
|
|
|
+ const amounts = LiquidityMath.getAmountsFromLiquidity(
|
|
|
|
|
+ poolInfo.sqrtPriceX64,
|
|
|
|
|
+ sqrtPriceLower,
|
|
|
|
|
+ sqrtPriceUpper,
|
|
|
|
|
+ posInfo.liquidity,
|
|
|
|
|
+ false,
|
|
|
|
|
+ )
|
|
|
|
|
+ const humanA = Number(amounts.amountA.toString()) / 10 ** decimalsA
|
|
|
|
|
+ const humanB = Number(amounts.amountB.toString()) / 10 ** decimalsB
|
|
|
|
|
+ amountA = humanA.toFixed(decimalsA > 4 ? 4 : decimalsA)
|
|
|
|
|
+ amountB = humanB.toFixed(decimalsB > 4 ? 4 : decimalsB)
|
|
|
|
|
+
|
|
|
|
|
+ const priceA = parseFloat(pool.mintA.price) || 0
|
|
|
|
|
+ const priceB = parseFloat(pool.mintB.price) || 0
|
|
|
|
|
+ const usd = humanA * priceA + humanB * priceB
|
|
|
|
|
+ sizeUsd = usd.toFixed(2)
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ // Skip size calculation on error
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
- ...pos,
|
|
|
|
|
- pool_label: poolLabel,
|
|
|
|
|
- price_lower: priceLower,
|
|
|
|
|
- price_upper: priceUpper,
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...pos,
|
|
|
|
|
+ pool_label: poolLabel,
|
|
|
|
|
+ price_lower: priceLower,
|
|
|
|
|
+ price_upper: priceUpper,
|
|
|
|
|
+ size_usd: sizeUsd,
|
|
|
|
|
+ amount_a: amountA,
|
|
|
|
|
+ amount_b: amountB,
|
|
|
|
|
+ symbol_a: pool?.mintA.mintInfo.symbol || '',
|
|
|
|
|
+ symbol_b: pool?.mintB.mintInfo.symbol || '',
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
return NextResponse.json(enriched)
|
|
return NextResponse.json(enriched)
|
|
|
}
|
|
}
|