test-target-value.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env node
  2. /**
  3. * Test: Copy with target position value (not parent value)
  4. * This test verifies that copy amount is calculated based on what the target wallet deposited,
  5. * not the parent position's total value.
  6. */
  7. import { Connection, PublicKey } from '@solana/web3.js';
  8. import { CONFIG } from './src/config/index.js';
  9. import { logger, setLogLevel } from './src/utils/index.js';
  10. setLogLevel('info');
  11. const TEST_TX = '5SnXmX8T4sytTFu8xeG7RXmui1eK65no8B3eJ3knTtdXqr3PqmDma5CaY9C7NG2oycvNs7KKZ75pPNDXB2T5XYQE';
  12. // Expected values from manual analysis
  13. const EXPECTED = {
  14. parentPosition: 'Cbgjt5zfXd9QTpneDdADc9cKYwXxXRfe57cyfmBbQM41',
  15. targetTokenA: 0.001521, // XAUt0
  16. targetTokenB: 6.00, // USDT
  17. targetTotalUsd: 0.001521 * 4965 + 6.00 * 1, // ~13.55
  18. positionNft: 'GUwGkK3PHnGVrLd3fPTphAMxuUFqkad7HKYyyeHzEW7p'
  19. };
  20. async function testTargetValueExtraction() {
  21. console.log('═══════════════════════════════════════════════════════');
  22. console.log('🧪 Test: Target Position Value Extraction');
  23. console.log(`📍 TX: ${TEST_TX.slice(0, 25)}...`);
  24. console.log('═══════════════════════════════════════════════════════\n');
  25. const connection = new Connection(CONFIG.RPC_URL, 'confirmed');
  26. try {
  27. // Fetch transaction
  28. console.log('1. Fetching transaction...');
  29. const tx = await connection.getTransaction(TEST_TX, {
  30. commitment: 'confirmed',
  31. maxSupportedTransactionVersion: 0
  32. });
  33. if (!tx) {
  34. console.error('❌ Transaction not found');
  35. return false;
  36. }
  37. console.log('✅ Transaction found\n');
  38. // Extract parent position from memo
  39. console.log('2. Extracting parent position...');
  40. let parentPosition = null;
  41. const message = tx.transaction.message;
  42. const accountKeys = message.staticAccountKeys || message.accountKeys || [];
  43. const instructions = message.compiledInstructions || message.instructions || [];
  44. const MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
  45. for (const instruction of instructions) {
  46. const programId = accountKeys[instruction.programIdIndex]?.toString();
  47. if (programId === MEMO_PROGRAM_ID) {
  48. let memoData = instruction.parsed || '';
  49. if (instruction.data) {
  50. try {
  51. memoData = Buffer.from(instruction.data, 'base64').toString('utf8');
  52. } catch {}
  53. }
  54. const match = memoData.match(/referer_position=([A-Za-z0-9]+)/);
  55. if (match) parentPosition = match[1];
  56. }
  57. }
  58. console.log(` Expected: ${EXPECTED.parentPosition}`);
  59. console.log(` Got: ${parentPosition}`);
  60. console.log(` ${parentPosition === EXPECTED.parentPosition ? '✅ PASS' : '❌ FAIL'}\n`);
  61. // Extract position NFT
  62. console.log('3. Extracting position NFT...');
  63. let positionNft = null;
  64. if (tx.meta?.postTokenBalances) {
  65. for (const balance of tx.meta.postTokenBalances) {
  66. if (balance.uiTokenAmount?.uiAmount === 1 && balance.uiTokenAmount?.decimals === 0) {
  67. positionNft = balance.mint;
  68. break;
  69. }
  70. }
  71. }
  72. console.log(` Expected: ${EXPECTED.positionNft}`);
  73. console.log(` Got: ${positionNft}`);
  74. console.log(` ${positionNft === EXPECTED.positionNft ? '✅ PASS' : '❌ FAIL'}\n`);
  75. // Extract target position value
  76. console.log('4. Extracting target position value...');
  77. const transfers = [];
  78. if (tx.meta?.preTokenBalances && tx.meta?.postTokenBalances) {
  79. const preBalances = {};
  80. tx.meta.preTokenBalances.forEach(balance => {
  81. const key = `${balance.accountIndex}-${balance.mint}`;
  82. preBalances[key] = parseFloat(balance.uiTokenAmount?.uiAmountString || 0);
  83. });
  84. tx.meta.postTokenBalances.forEach(balance => {
  85. const key = `${balance.accountIndex}-${balance.mint}`;
  86. const pre = preBalances[key] || 0;
  87. const post = parseFloat(balance.uiTokenAmount?.uiAmountString || 0);
  88. const diff = pre - post;
  89. if (diff > 0.0001) {
  90. transfers.push({
  91. mint: balance.mint,
  92. uiAmount: diff,
  93. decimals: balance.decimals
  94. });
  95. }
  96. });
  97. }
  98. transfers.sort((a, b) => b.uiAmount - a.uiAmount);
  99. const tokenA = transfers[0] || {};
  100. const tokenB = transfers[1] || {};
  101. // Known prices
  102. const TOKEN_PRICES = {
  103. 'AymATz4TCL9sWNEEV9Kvyz45CHVhDZ6kUgjTJPzLpU9P': 4965,
  104. 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB': 1
  105. };
  106. const tokenAPrice = TOKEN_PRICES[tokenA.mint] || 0;
  107. const tokenBPrice = TOKEN_PRICES[tokenB.mint] || 0;
  108. const totalUsd = (tokenA.uiAmount || 0) * tokenAPrice + (tokenB.uiAmount || 0) * tokenBPrice;
  109. console.log(` Token A: ${tokenA.uiAmount?.toFixed(6)} (expected: ${EXPECTED.targetTokenA})`);
  110. console.log(` Token B: ${tokenB.uiAmount?.toFixed(6)} (expected: ${EXPECTED.targetTokenB})`);
  111. console.log(` Total USD: $${totalUsd.toFixed(2)} (expected: $${EXPECTED.targetTotalUsd.toFixed(2)})`);
  112. const tolerance = 0.001;
  113. const tokenAOk = Math.abs((tokenA.uiAmount || 0) - EXPECTED.targetTokenA) < tolerance;
  114. const tokenBOk = Math.abs((tokenB.uiAmount || 0) - EXPECTED.targetTokenB) < tolerance;
  115. const totalOk = Math.abs(totalUsd - EXPECTED.targetTotalUsd) < 1;
  116. console.log(` ${tokenAOk && tokenBOk && totalOk ? '✅ PASS' : '❌ FAIL'}\n`);
  117. // Calculate copy amount
  118. console.log('5. Calculating copy amount...');
  119. const copyMultiplier = CONFIG.COPY_MULTIPLIER;
  120. const maxUsd = CONFIG.MAX_USD_VALUE;
  121. const copyUsd = Math.min(totalUsd * copyMultiplier, maxUsd);
  122. console.log(` Target Value: $${totalUsd.toFixed(2)}`);
  123. console.log(` Multiplier: ${copyMultiplier}x`);
  124. console.log(` Calculated: $${(totalUsd * copyMultiplier).toFixed(2)}`);
  125. console.log(` Max Limit: $${maxUsd}`);
  126. console.log(` ✅ Final Copy Amount: $${copyUsd.toFixed(2)}`);
  127. console.log(`\n IMPORTANT: This is based on TARGET position ($${totalUsd.toFixed(2)}),`);
  128. console.log(` NOT parent position (which might be much larger)!\n`);
  129. console.log('═══════════════════════════════════════════════════════');
  130. console.log('✅ Test completed successfully!');
  131. console.log('═══════════════════════════════════════════════════════');
  132. return true;
  133. } catch (error) {
  134. console.error('❌ Test failed:', error);
  135. return false;
  136. }
  137. }
  138. testTargetValueExtraction();