#!/usr/bin/env node /** * Test: Calculate copy amount from target position (not parent) * Analyzes a specific transaction to get the target's position value * Transaction: 5SnXmX8T4sytTFu8xeG7RXmui1eK65no8B3eJ3knTtdXqr3PqmDma5CaY9C7NG2oycvNs7KKZ75pPNDXB2T5XYQE */ import { Connection, PublicKey } from '@solana/web3.js'; import { CONFIG } from './src/config/index.js'; import { logger, setLogLevel } from './src/utils/index.js'; setLogLevel('debug'); const TX_SIGNATURE = '5SnXmX8T4sytTFu8xeG7RXmui1eK65no8B3eJ3knTtdXqr3PqmDma5CaY9C7NG2oycvNs7KKZ75pPNDXB2T5XYQE'; async function analyzeTransaction() { console.log('═══════════════════════════════════════════'); console.log('🧪 Analyzing Target Position Transaction'); console.log(`📍 TX: ${TX_SIGNATURE.slice(0, 20)}...`); console.log('═══════════════════════════════════════════\n'); const connection = new Connection(CONFIG.RPC_URL, 'confirmed'); try { // Fetch transaction details console.log('Fetching transaction from Solana...'); const tx = await connection.getTransaction(TX_SIGNATURE, { commitment: 'confirmed', maxSupportedTransactionVersion: 0 }); if (!tx) { console.error('❌ Transaction not found'); return; } console.log('✅ Transaction found!\n'); // Extract token transfers from pre/post token balances console.log('Step 1: Extracting token transfers from balance changes...'); const transfers = []; // Compare pre and post token balances to find what was transferred if (tx.meta && tx.meta.preTokenBalances && tx.meta.postTokenBalances) { // Create a map of pre balances const preBalances = {}; tx.meta.preTokenBalances.forEach(balance => { const key = `${balance.accountIndex}-${balance.mint}`; preBalances[key] = parseFloat(balance.uiTokenAmount?.uiAmountString || 0); }); // Compare with post balances tx.meta.postTokenBalances.forEach(balance => { const key = `${balance.accountIndex}-${balance.mint}`; const preAmount = preBalances[key] || 0; const postAmount = parseFloat(balance.uiTokenAmount?.uiAmountString || 0); const diff = postAmount - preAmount; // If balance decreased significantly (transferred out), record it if (diff < -0.0001) { transfers.push({ mint: balance.mint, uiAmount: Math.abs(diff), decimals: balance.decimals, accountIndex: balance.accountIndex, owner: balance.owner }); } }); } // Also try inner instructions as fallback if (transfers.length === 0 && tx.meta && tx.meta.innerInstructions) { for (const innerInst of tx.meta.innerInstructions) { for (const inst of innerInst.instructions) { if (inst.parsed?.type === 'transferChecked') { const info = inst.parsed.info; transfers.push({ mint: info.mint, amount: info.tokenAmount?.amount, uiAmount: parseFloat(info.tokenAmount?.uiAmount || 0), decimals: info.tokenAmount?.decimals, destination: info.destination }); } } } } console.log(`Found ${transfers.length} token transfers:\n`); transfers.forEach((t, i) => { console.log(` Transfer ${i + 1}:`); console.log(` Mint: ${t.mint}`); console.log(` Amount: ${t.uiAmount} (decimals: ${t.decimals})`); console.log(` Raw: ${t.amount}`); console.log(` Destination: ${t.destination?.slice(0, 16)}...`); }); // Get token prices to calculate USD value console.log('\nStep 2: Fetching token prices...'); const mints = [...new Set(transfers.map(t => t.mint))]; console.log(`Token mints: ${mints.join(', ')}`); // Try to get prices from Jupiter price API let totalUsdValue = 0; try { const axios = (await import('axios')).default; const response = await axios.get( `https://price.jup.ag/v4/price?ids=${mints.join(',')}`, { timeout: 10000 } ); const prices = response.data.data || {}; console.log('\nToken prices:'); transfers.forEach(t => { const price = prices[t.mint]?.price || 0; const value = t.uiAmount * price; totalUsdValue += value; console.log(` ${t.mint.slice(0, 8)}...: $${price} × ${t.uiAmount} = $${value.toFixed(2)}`); }); console.log(`\n💰 Total Position Value: $${totalUsdValue.toFixed(2)}`); } catch (error) { console.log('⚠️ Could not fetch prices, showing raw amounts only'); } // Calculate what we should copy console.log('\n═══════════════════════════════════════════'); console.log('Step 3: Calculating copy amount'); console.log('═══════════════════════════════════════════'); const copyMultiplier = CONFIG.COPY_MULTIPLIER; const maxUsdValue = CONFIG.MAX_USD_VALUE; if (totalUsdValue > 0) { const copyUsdValue = Math.min(totalUsdValue * copyMultiplier, maxUsdValue); console.log(`Target Position Value: $${totalUsdValue.toFixed(2)}`); console.log(`Copy Multiplier: ${copyMultiplier}x`); console.log(`Calculated Copy: $${(totalUsdValue * copyMultiplier).toFixed(2)}`); console.log(`Max Allowed: $${maxUsdValue}`); console.log(`\n✅ Final Copy Amount: $${copyUsdValue.toFixed(2)}`); // Calculate token amounts proportionally console.log('\nToken amounts to acquire:'); transfers.forEach(t => { const ratio = (t.uiAmount * copyMultiplier) / totalUsdValue * copyUsdValue; const withBuffer = ratio * 1.1; // 10% buffer console.log(` ${t.mint.slice(0, 8)}...: ${ratio.toFixed(6)} (with 10% buffer: ${withBuffer.toFixed(6)})`); }); } // Extract position NFT and parent info from memo console.log('\n═══════════════════════════════════════════'); console.log('Step 4: Extracting position info from memo'); console.log('═══════════════════════════════════════════'); const message = tx.transaction.message; const accountKeys = message.staticAccountKeys || message.accountKeys || []; const instructions = message.compiledInstructions || message.instructions || []; let parentPosition = null; let positionNftMint = null; // Look for memo instruction const MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'; for (const instruction of instructions) { const programId = accountKeys[instruction.programIdIndex]?.toString(); if (programId === MEMO_PROGRAM_ID) { let memoData; if (instruction.data) { try { memoData = Buffer.from(instruction.data, 'base64').toString('utf8'); } catch { memoData = instruction.data; } } if (instruction.parsed) { memoData = instruction.parsed; } if (memoData) { console.log(`Memo found: ${memoData}`); // Extract parent position const match = memoData.match(/referer_position=([A-Za-z0-9]+)/); if (match) { parentPosition = match[1]; console.log(`✅ Parent Position: ${parentPosition}`); } } } // Look for Byreal program to extract position NFT const BYREAL_PROGRAM_ID = 'REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2'; if (programId === BYREAL_PROGRAM_ID) { console.log('Found Byreal program instruction'); // Position NFT is usually minted in this transaction // Look for it in postTokenBalances if (tx.meta && tx.meta.postTokenBalances) { for (const balance of tx.meta.postTokenBalances) { if (balance.uiTokenAmount?.uiAmount === 1 && balance.uiTokenAmount?.decimals === 0) { positionNftMint = balance.mint; console.log(`✅ Position NFT Mint: ${positionNftMint}`); break; } } } } } // Also check log messages if (tx.meta && tx.meta.logMessages) { for (const log of tx.meta.logMessages) { const match = log.match(/referer_position=([A-Za-z0-9]+)/); if (match && !parentPosition) { parentPosition = match[1]; console.log(`✅ Parent Position (from logs): ${parentPosition}`); } } } console.log('\n═══════════════════════════════════════════'); console.log('Summary'); console.log('═══════════════════════════════════════════'); console.log(`Transaction: ${TX_SIGNATURE}`); console.log(`Parent Position: ${parentPosition || 'Not found'}`); console.log(`Position NFT: ${positionNftMint || 'Not found'}`); console.log(`Target Position Value: $${totalUsdValue.toFixed(2)}`); console.log(`Recommended Copy: $${Math.min(totalUsdValue * copyMultiplier, maxUsdValue).toFixed(2)}`); console.log('\nNote: Copy amount is based on TARGET position value, not parent!'); console.log('═══════════════════════════════════════════'); } catch (error) { console.error('Error analyzing transaction:', error); process.exit(1); } } analyzeTransaction();