Explorar o código

fix: 余额检查改用代币数量直接比较,避免 priceUsd 为空时误 swap

之前用 priceUsd 换算 USD 比较余额,priceUsd 为空时会忽略余额直接 swap;改为用 token.amount 直接和 balance 比较

Co-authored-by: Cursor <cursoragent@cursor.com>
zhangchunrui hai 1 mes
pai
achega
4f6dae6685
Modificáronse 2 ficheiros con 57 adicións e 69 borrados
  1. 30 35
      src/core/sniper.js
  2. 27 34
      test-copy.js

+ 30 - 35
src/core/sniper.js

@@ -101,52 +101,47 @@ export class SniperEngine {
 
     const BUFFER_PCT = 1.2;
 
-    const needSwapUsd = async (token, valueUsd) => {
-      const requiredWithBuffer = valueUsd * BUFFER_PCT;
+    const checkAndSwap = async (label, token, valueUsd) => {
       const balance = await this.swapper.getTokenBalance(token.address);
-      const price = parseFloat(token.priceUsd) || 0;
-      if (price <= 0) {
-        logger.warn(`Token ${token.symbol || token.address} priceUsd is ${token.priceUsd}, cannot calculate balance value, will swap full amount`);
-        return { skip: false, swapUsd: requiredWithBuffer, currentValueUsd: 0, requiredWithBuffer };
+      const requiredAmount = parseFloat(token.amount) || 0;
+      const bufferedAmount = requiredAmount * BUFFER_PCT;
+      const bufferedValueUsd = valueUsd * BUFFER_PCT;
+
+      logger.info(`  ${label} (${token.symbol || token.address}): 余额=${balance}, 需要=${requiredAmount}, 含缓冲=${bufferedAmount.toFixed(6)}, valueUsd=${formatUsd(valueUsd)}, priceUsd=${token.priceUsd}`);
+
+      // 直接用代币数量比较余额
+      if (requiredAmount > 0 && balance >= bufferedAmount) {
+        logger.success(`  ${label}: 余额充足 ${balance} >= ${bufferedAmount.toFixed(6)},跳过 swap`);
+        return true;
+      }
+
+      // 余额不足,计算需要 swap 的 USD 金额
+      let swapUsd = bufferedValueUsd;
+      if (requiredAmount > 0 && balance > 0) {
+        // 按比例计算缺口的 USD
+        const ratio = balance / bufferedAmount;
+        swapUsd = bufferedValueUsd * (1 - ratio);
       }
-      const currentValueUsd = balance * price;
-      if (currentValueUsd >= requiredWithBuffer) {
-        return { skip: true, currentValueUsd, requiredWithBuffer };
+
+      logger.info(`  ${label}: 余额不足,需 swap ${formatUsd(swapUsd)}`);
+      const success = await this.swapper.swapIfNeeded(token.address, swapUsd);
+      if (!success) {
+        logger.error(`Failed to acquire ${label}`);
+        return false;
       }
-      const swapUsd = requiredWithBuffer - currentValueUsd;
-      return { skip: false, swapUsd, currentValueUsd, requiredWithBuffer };
+      return true;
     };
 
-    // Step 2: Token A — 检查持仓,不足则多换 20% 富裕
+    // Step 2: Token A — 检查持仓,不足则 swap
     const valueA = parseFloat(tokenA.valueUsd) || 0;
     if (valueA > 0) {
-      const planA = await needSwapUsd(tokenA, valueA);
-      if (planA.skip) {
-        logger.success(`Token A (${tokenA.symbol || ''}): 已有 ${formatUsd(planA.currentValueUsd)} >= 需要 ${formatUsd(planA.requiredWithBuffer)},跳过 swap`);
-      } else {
-        logger.info(`Token A (${tokenA.symbol || ''}): 已有 ${formatUsd(planA.currentValueUsd)},需 ${formatUsd(planA.requiredWithBuffer)}(含 20% 缓冲),swap $${planA.swapUsd.toFixed(2)}`);
-        const success = await this.swapper.swapIfNeeded(tokenA.address, planA.swapUsd);
-        if (!success) {
-          logger.error('Failed to acquire Token A');
-          return false;
-        }
-      }
+      if (!await checkAndSwap('Token A', tokenA, valueA)) return false;
     }
 
-    // Step 3: Token B — 检查持仓,不足则多换 20% 富裕
+    // Step 3: Token B — 检查持仓,不足则 swap
     const valueB = parseFloat(tokenB.valueUsd) || 0;
     if (valueB > 0) {
-      const planB = await needSwapUsd(tokenB, valueB);
-      if (planB.skip) {
-        logger.success(`Token B (${tokenB.symbol || ''}): 已有 ${formatUsd(planB.currentValueUsd)} >= 需要 ${formatUsd(planB.requiredWithBuffer)},跳过 swap`);
-      } else {
-        logger.info(`Token B (${tokenB.symbol || ''}): 已有 ${formatUsd(planB.currentValueUsd)},需 ${formatUsd(planB.requiredWithBuffer)}(含 20% 缓冲),swap $${planB.swapUsd.toFixed(2)}`);
-        const success = await this.swapper.swapIfNeeded(tokenB.address, planB.swapUsd);
-        if (!success) {
-          logger.error('Failed to acquire Token B');
-          return false;
-        }
-      }
+      if (!await checkAndSwap('Token B', tokenB, valueB)) return false;
     }
 
     // Step 4: Execute copy of PARENT position

+ 27 - 34
test-copy.js

@@ -128,51 +128,44 @@ async function testCopy() {
   const tokenB = calculation.tokenB;
   const BUFFER_PCT = 1.2;
 
-  const needSwapUsd = async (token, valueUsd) => {
-    const requiredWithBuffer = valueUsd * BUFFER_PCT;
+  const checkAndSwap = async (label, token, valueUsd) => {
     const balance = await swapper.getTokenBalance(token.address);
-    const price = parseFloat(token.priceUsd) || 0;
-    if (price <= 0) {
-      console.log(`  Warning: Token ${token.symbol || token.address} priceUsd is ${token.priceUsd}, will swap full amount`);
-      return { skip: false, swapUsd: requiredWithBuffer, currentValueUsd: 0, requiredWithBuffer };
+    const requiredAmount = parseFloat(token.amount) || 0;
+    const bufferedAmount = requiredAmount * BUFFER_PCT;
+    const bufferedValueUsd = valueUsd * BUFFER_PCT;
+
+    console.log(`  ${label} (${token.symbol || token.address}): 余额=${balance}, 需要=${requiredAmount}, 含缓冲=${bufferedAmount.toFixed(6)}, valueUsd=$${valueUsd.toFixed(2)}, priceUsd=${token.priceUsd}`);
+
+    if (requiredAmount > 0 && balance >= bufferedAmount) {
+      console.log(`  ✅ ${label}: 余额充足 ${balance} >= ${bufferedAmount.toFixed(6)},跳过 swap`);
+      return true;
     }
-    const currentValueUsd = balance * price;
-    if (currentValueUsd >= requiredWithBuffer) {
-      return { skip: true, currentValueUsd, requiredWithBuffer };
+
+    let swapUsd = bufferedValueUsd;
+    if (requiredAmount > 0 && balance > 0) {
+      const ratio = balance / bufferedAmount;
+      swapUsd = bufferedValueUsd * (1 - ratio);
     }
-    return { skip: false, swapUsd: requiredWithBuffer - currentValueUsd, currentValueUsd, requiredWithBuffer };
+
+    console.log(`  ${label}: 余额不足,需 swap $${swapUsd.toFixed(2)}`);
+    console.log('    Ready to swap. Continue? (Ctrl+C to cancel)');
+    await new Promise(resolve => setTimeout(resolve, 3000));
+    const success = await swapper.swapIfNeeded(token.address, swapUsd);
+    if (!success) {
+      console.error(`❌ Failed to acquire ${label}`);
+      process.exit(1);
+    }
+    return true;
   };
 
   const valueA = parseFloat(tokenA.valueUsd) || 0;
   if (valueA > 0) {
-    const planA = await needSwapUsd(tokenA, valueA);
-    if (planA.skip) {
-      console.log(`  Token A (${tokenA.symbol}): 已有 $${planA.currentValueUsd.toFixed(2)} >= 需要 $${planA.requiredWithBuffer.toFixed(2)},跳过 swap`);
-    } else {
-      console.log(`  Token A (${tokenA.symbol}): 已有 $${planA.currentValueUsd.toFixed(2)},需 $${planA.requiredWithBuffer.toFixed(2)}(含 20% 缓冲),swap $${planA.swapUsd.toFixed(2)}`);
-      console.log('    Ready to swap. Continue? (Ctrl+C to cancel)');
-      await new Promise(resolve => setTimeout(resolve, 3000));
-      const success = await swapper.swapIfNeeded(tokenA.address, planA.swapUsd);
-      if (!success) {
-        console.error('❌ Failed to acquire Token A');
-        process.exit(1);
-      }
-    }
+    await checkAndSwap('Token A', tokenA, valueA);
   }
 
   const valueB = parseFloat(tokenB.valueUsd) || 0;
   if (valueB > 0) {
-    const planB = await needSwapUsd(tokenB, valueB);
-    if (planB.skip) {
-      console.log(`  Token B (${tokenB.symbol}): 已有 $${planB.currentValueUsd.toFixed(2)} >= 需要 $${planB.requiredWithBuffer.toFixed(2)},跳过 swap`);
-    } else {
-      console.log(`  Token B (${tokenB.symbol}): 已有 $${planB.currentValueUsd.toFixed(2)},需 $${planB.requiredWithBuffer.toFixed(2)}(含 20% 缓冲),swap $${planB.swapUsd.toFixed(2)}`);
-      const success = await swapper.swapIfNeeded(tokenB.address, planB.swapUsd);
-      if (!success) {
-        console.error('❌ Failed to acquire Token B');
-        process.exit(1);
-      }
-    }
+    await checkAndSwap('Token B', tokenB, valueB);
   }
 
   // Step 5: Execute copy