#!/usr/bin/env node /** * Test Copy Script * Directly test the copy functionality for a specific parent position * * Usage: * node test-copy.js [target_usd_value] * * Examples: * # Use parent position value (default) * node test-copy.js Cbgjt5zfXd9QTpneDdADc9cKYwXxXRfe57cyfmBbQM41 * * # Use specific target value (e.g., from transaction analysis) * node test-copy.js Cbgjt5zfXd9QTpneDdADc9cKYwXxXRfe57cyfmBbQM41 13.55 */ import { ByrealAPI, JupiterSwapper } from './src/services/index.js'; import { CONFIG } from './src/config/index.js'; import { logger, setLogLevel, formatUsd } from './src/utils/index.js'; // Set log level to see detailed info setLogLevel('info'); const PARENT_POSITION = process.argv[2] || '7rxWLdhk6Hk914QSBBztXJ35DUs4exr7mFJJdYWR2YNw'; const MANUAL_TARGET_VALUE = parseFloat(process.argv[3]) || 0; // Optional: manual target value async function testCopy() { console.log('═══════════════════════════════════════════'); console.log('🧪 Test Copy Script'); console.log(`📍 Parent Position: ${PARENT_POSITION}`); if (MANUAL_TARGET_VALUE > 0) { console.log(`💰 Using Manual Target Value: $${MANUAL_TARGET_VALUE}`); } console.log('═══════════════════════════════════════════\n'); // Step 1: Fetch parent position details console.log('Step 1: Fetching parent position details...'); const parentDetail = await ByrealAPI.getPositionDetail(PARENT_POSITION); if (!parentDetail) { console.error('❌ Failed to fetch position detail'); process.exit(1); } const poolInfo = parentDetail.pool; if (!poolInfo) { console.error('❌ Pool info not found'); process.exit(1); } console.log('✅ Position details:'); console.log(` Pool: ${poolInfo.mintA?.symbol}/${poolInfo.mintB?.symbol}`); console.log(` Token A: ${poolInfo.mintA?.address}`); console.log(` Token B: ${poolInfo.mintB?.address}`); // Get correct field names const nftMint = parentDetail.nftMintAddress || parentDetail.nftMint; const poolAddress = parentDetail.pool?.poolAddress || parentDetail.poolAddress; // Get base value for calculation const parentUsdValue = parseFloat(parentDetail.totalDeposit || parentDetail.totalUsdValue || parentDetail.liquidityUsd || 0); // Use manual target value if provided, otherwise use parent value const targetUsdValue = MANUAL_TARGET_VALUE > 0 ? MANUAL_TARGET_VALUE : parentUsdValue; console.log(` NFT Mint: ${nftMint}`); console.log(` Pool Address: ${poolAddress}`); console.log(` Parent Position Value: $${parentUsdValue}`); if (MANUAL_TARGET_VALUE > 0) { console.log(` ⚠️ Using Target Value: $${targetUsdValue} (not parent value!)`); } else { console.log(` Using Value: $${targetUsdValue}`); } // Step 2: Calculate copy amount console.log('\nStep 2: Calculating copy amount...'); const copyUsdValue = Math.min( targetUsdValue * CONFIG.COPY_MULTIPLIER, CONFIG.MAX_USD_VALUE ); console.log(` Base Value: $${targetUsdValue}`); console.log(` Multiplier: ${CONFIG.COPY_MULTIPLIER}x`); console.log(` Calculated: $${(targetUsdValue * CONFIG.COPY_MULTIPLIER).toFixed(2)}`); console.log(` Max Allowed: $${CONFIG.MAX_USD_VALUE}`); console.log(` ✅ Final Copy Amount: $${copyUsdValue}`); if (copyUsdValue < CONFIG.MIN_USD_VALUE) { console.error(`❌ Copy value $${copyUsdValue} below minimum $${CONFIG.MIN_USD_VALUE}`); process.exit(1); } // Step 3: Calculate token amounts console.log('\nStep 3: Calculating token amounts...'); console.log(` Calling Byreal API with maxUsdValue: $${copyUsdValue}`); const calculation = await ByrealAPI.calculatePosition( PARENT_POSITION, copyUsdValue, nftMint ); if (!calculation) { console.error('❌ Calculation failed'); process.exit(1); } console.log('✅ Calculation result from Byreal API:'); console.log(` Token A: ${calculation.tokenA?.amount} ${calculation.tokenA?.symbol} ($${calculation.tokenA?.valueUsd})`); console.log(` Token B: ${calculation.tokenB?.amount} ${calculation.tokenB?.symbol} ($${calculation.tokenB?.valueUsd})`); console.log(` Estimated Total: $${calculation.estimatedValue}`); console.log(`\n ⚠️ Note: These amounts are based on copy value $${copyUsdValue},`); console.log(` not the full parent position value ($${parentUsdValue})!`); // Debug: Check if amount is token quantity or USD value console.log('\n 🔍 Checking token amounts:'); console.log(` Token A amount field: ${calculation.tokenA?.amount} (type: ${typeof calculation.tokenA?.amount})`); console.log(` Token A valueUsd field: ${calculation.tokenA?.valueUsd}`); console.log(` Token B amount field: ${calculation.tokenB?.amount} (type: ${typeof calculation.tokenB?.amount})`); console.log(` Token B valueUsd field: ${calculation.tokenB?.valueUsd}`); console.log(` If amount ≈ valueUsd, then amount is USD value, not token quantity!`); // Step 4: Check balances and swap console.log('\nStep 4: Checking balances and swapping...'); const swapper = new JupiterSwapper(); const tokenA = calculation.tokenA; const tokenB = calculation.tokenB; // Token A console.log(`\n Token A (${tokenA.symbol}):`); const tokenABalance = await swapper.getTokenBalance(tokenA.address); const requiredAmountA = parseFloat(tokenA.amount); const swapAmountA = requiredAmountA * 1.1; console.log(` Balance: ${tokenABalance.toFixed(6)}`); console.log(` Required: ${requiredAmountA.toFixed(6)}`); console.log(` With 10% buffer: ${swapAmountA.toFixed(6)}`); if (tokenABalance < requiredAmountA * 0.95) { console.log(` ⚠️ Insufficient balance, need to swap from USDC`); // Ask for confirmation console.log('\n Ready to swap. Continue? (Ctrl+C to cancel)'); await new Promise(resolve => setTimeout(resolve, 3000)); const success = await swapper.swapIfNeeded( 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', tokenA.address, swapAmountA, tokenA.decimals ); if (!success) { console.error('❌ Failed to acquire Token A'); process.exit(1); } } else { console.log(` ✅ Balance sufficient`); } // Token B console.log(`\n Token B (${tokenB.symbol}):`); const tokenBBalance = await swapper.getTokenBalance(tokenB.address); const requiredAmountB = parseFloat(tokenB.amount); const swapAmountB = requiredAmountB * 1.1; console.log(` Balance: ${tokenBBalance.toFixed(6)}`); console.log(` Required: ${requiredAmountB.toFixed(6)}`); console.log(` With 10% buffer: ${swapAmountB.toFixed(6)}`); if (tokenBBalance < requiredAmountB * 0.95) { console.log(` ⚠️ Insufficient balance, need to swap from USDC`); const success = await swapper.swapIfNeeded( 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', tokenB.address, swapAmountB, tokenB.decimals ); if (!success) { console.error('❌ Failed to acquire Token B'); process.exit(1); } } else { console.log(` ✅ Balance sufficient`); } // Step 5: Execute copy console.log('\n═══════════════════════════════════════════'); console.log('Step 5: Ready to execute copy!'); console.log('═══════════════════════════════════════════'); console.log(`Parent Position: ${PARENT_POSITION}`); console.log(`NFT Mint: ${nftMint}`); console.log(`Copy Amount: $${copyUsdValue}`); console.log('\n⚠️ This will execute the actual copy transaction!'); console.log('Executing copy...'); const success = await ByrealAPI.copyPosition( nftMint, PARENT_POSITION, copyUsdValue ); if (success) { console.log('\n✅ Copy executed successfully!'); } else { console.log('\n❌ Copy failed!'); process.exit(1); } console.log('\n═══════════════════════════════════════════'); console.log('Test completed'); console.log('═══════════════════════════════════════════'); } testCopy().catch(error => { console.error('Fatal error:', error); process.exit(1); });