Sfoglia il codice sorgente

清理冗余日志,Position Mappings 显示仓位大小

- 移除 monitor 和 copier 中的调试/噪音日志,保留关键操作和错误日志
- 仓位列表新增 Size 列,显示链上实时 USD 价值和 token 数量

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
zhangchunrui 1 settimana fa
parent
commit
7b6b067401
4 ha cambiato i file con 105 aggiunte e 80 eliminazioni
  1. 85 39
      src/app/api/positions/route.ts
  2. 20 0
      src/app/positions/page.tsx
  3. 0 24
      src/lib/copier/index.ts
  4. 0 17
      src/lib/monitor/index.ts

+ 85 - 39
src/app/api/positions/route.ts

@@ -1,4 +1,5 @@
 import { NextRequest, NextResponse } from 'next/server'
+import { PublicKey } from '@solana/web3.js'
 import {
   getPositionMappings,
   getPositionMappingById,
@@ -6,10 +7,11 @@ import {
   updatePositionMappingStatus,
 } from '@/lib/db/queries'
 import { CopyEngine } from '@/lib/copier/index'
+import { getConnection } from '@/lib/solana/connection'
 import { getPoolInfoMap, getPoolLabel } from '@/lib/pool-info'
 
 // 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) {
   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
   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)
 }

+ 20 - 0
src/app/positions/page.tsx

@@ -16,6 +16,11 @@ interface PositionRow {
   tick_upper: number
   price_lower: string
   price_upper: string
+  size_usd: string
+  amount_a: string
+  amount_b: string
+  symbol_a: string
+  symbol_b: string
   status: string
   created_at: string
 }
@@ -117,6 +122,7 @@ export default function PositionsPage() {
                   <th className="text-left py-2 pr-3">Target NFT</th>
                   <th className="text-left py-2 pr-3">Our NFT</th>
                   <th className="text-left py-2 pr-3">Pool</th>
+                  <th className="text-left py-2 pr-3">Size</th>
                   <th className="text-left py-2 pr-3">Price Range</th>
                   <th className="text-left py-2 pr-3">Status</th>
                   <th className="text-left py-2 pr-3">Created</th>
@@ -156,6 +162,20 @@ export default function PositionsPage() {
                     <td className="py-2 pr-3 text-zinc-400">
                       {row.pool_label || `${row.pool_id.slice(0, 6)}...`}
                     </td>
+                    <td className="py-2 pr-3">
+                      {row.size_usd ? (
+                        <div>
+                          <span className="text-green-400 font-medium">${row.size_usd}</span>
+                          <div className="text-zinc-500 text-[10px] leading-tight mt-0.5">
+                            {row.amount_a && row.symbol_a && `${row.amount_a} ${row.symbol_a}`}
+                            {row.amount_a && row.amount_b && ' + '}
+                            {row.amount_b && row.symbol_b && `${row.amount_b} ${row.symbol_b}`}
+                          </div>
+                        </div>
+                      ) : (
+                        <span className="text-zinc-500">-</span>
+                      )}
+                    </td>
                     <td className="py-2 pr-3 text-zinc-400">
                       {row.price_lower && row.price_upper
                         ? `${formatPrice(row.price_lower)} ~ ${formatPrice(row.price_upper)}`

+ 0 - 24
src/lib/copier/index.ts

@@ -80,8 +80,6 @@ export class CopyEngine {
   }
 
   async executeCopy(operation: ParsedOperation): Promise<void> {
-    console.log(`[CopyEngine] Executing ${operation.type} copy for tx ${operation.signature}`)
-
     switch (operation.type) {
       case 'open_position':
         return this.copyOpenPosition(operation)
@@ -228,11 +226,6 @@ export class CopyEngine {
         maxUsd: addrSettings.maxUsd,
       })
 
-      console.log(
-        `[CopyEngine] Target: $${targetUsd.toFixed(2)}, Multiplier: ${addrSettings.multiplier}x, ` +
-          `Max: $${addrSettings.maxUsd}, Our: $${ourUsd.toFixed(2)} (ratio: ${ratio.toFixed(4)})`,
-      )
-
       if (ratio <= 0) {
         throw new Error('Copy ratio is zero - target position value is zero or prices unavailable')
       }
@@ -291,10 +284,6 @@ export class CopyEngine {
           ? new PublicKey(op.refererPosition)
           : new PublicKey(op.personalPosition)
 
-      console.log(
-        `[CopyEngine] Referrer mode: ${addrSettings.referrerMode}, referer: ${refererPubkey.toBase58()}`,
-      )
-
       // Execute position creation with referer_position memo
       const txid = await this.chain.createPosition({
         userAddress: getUserAddress(),
@@ -312,11 +301,6 @@ export class CopyEngine {
 
       // Extract our NFT mint from the confirmed transaction
       const ourNftMint = await extractOurNftMint(txid, getUserAddress().toBase58())
-      if (ourNftMint) {
-        console.log(`[CopyEngine] Our NFT mint: ${ourNftMint}`)
-      } else {
-        console.warn(`[CopyEngine] Could not extract our NFT mint from TX ${txid}`)
-      }
 
       updateCopyHistory(historyId, {
         ourNftMint: ourNftMint || undefined,
@@ -420,10 +404,6 @@ export class CopyEngine {
         maxUsd: addrSettings.maxUsd,
       })
 
-      console.log(
-        `[CopyEngine] Add liquidity - Target: $${targetUsd.toFixed(2)}, Our: $${ourUsd.toFixed(2)} (ratio: ${ratio.toFixed(4)})`,
-      )
-
       if (ratio <= 0) {
         throw new Error('Copy ratio is zero')
       }
@@ -559,10 +539,6 @@ export class CopyEngine {
         throw new Error('Nothing to decrease')
       }
 
-      console.log(
-        `[CopyEngine] Decrease: target=${op.liquidity || 'N/A'}, ours=${ourPositionInfo.liquidity.toString()}, decreasing=${liquidityToDecrease.toString()}`,
-      )
-
       // Use generous slippage for decrease — we're removing our own liquidity,
       // price can move significantly between detection and execution
       const txid = await this.chain.decreaseLiquidity({

+ 0 - 17
src/lib/monitor/index.ts

@@ -294,8 +294,6 @@ export class MonitorService {
       return
     }
 
-    console.log(`[Monitor] WS received: ${sig.slice(0, 12)}...`)
-
     try {
       await this.processTransaction(sig)
     } catch (e) {
@@ -348,18 +346,14 @@ export class MonitorService {
   }
 
   private async processTransaction(signature: string) {
-    const shortSig = `${signature.slice(0, 12)}...`
-
     // Skip if already processing (in-flight lock)
     if (this.processingSigs.has(signature)) {
-      console.log(`[Monitor] Skip ${shortSig}: already processing`)
       return
     }
     this.processingSigs.add(signature)
 
     try {
       if (isTxProcessed(signature)) {
-        console.log(`[Monitor] Skip ${shortSig}: already in DB`)
         return
       }
 
@@ -370,22 +364,11 @@ export class MonitorService {
       })
 
       if (!tx) {
-        console.log(`[Monitor] Skip ${shortSig}: getParsedTransaction returned null`)
         return
       }
 
       const parsed = parseTransaction(tx, this.watchedAddresses)
       if (!parsed) {
-        // Debug: check if any watched address is in this tx
-        const txAddrs = tx.transaction.message.accountKeys.map((k) =>
-          typeof k === 'string' ? k : 'pubkey' in k ? k.pubkey.toBase58() : '',
-        )
-        const matchedAddr = txAddrs.find((a) => this.watchedAddresses.has(a))
-        if (matchedAddr) {
-          console.log(
-            `[Monitor] Skip ${shortSig}: parseTransaction returned null but watched address ${matchedAddr.slice(0, 12)}... IS in tx accounts`,
-          )
-        }
         return
       }