| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- #!/usr/bin/env node
- /**
- * Test: Parse transaction and extract copy parameters
- * Usage: node test-tx-parse.js <transaction_signature>
- *
- * Example: node test-tx-parse.js 5SnXmX8T4sytTFu8xeG7RXmui1eK65no8B3eJ3knTtdXqr3PqmDma5CaY9C7NG2oycvNs7KKZ75pPNDXB2T5XYQE
- */
- import { Connection, PublicKey } from '@solana/web3.js';
- import { CONFIG } from './src/config/index.js';
- import { ByrealAPI } from './src/services/byreal.js';
- import { logger, setLogLevel } from './src/utils/index.js';
- setLogLevel('info');
- const TX_SIGNATURE = process.argv[2];
- if (!TX_SIGNATURE) {
- console.error('Usage: node test-tx-parse.js <transaction_signature>');
- console.error('Example: node test-tx-parse.js 5SnXmX8T4sytTFu8xeG7RXmui1eK65no8B3eJ3knTtdXqr3PqmDma5CaY9C7NG2oycvNs7KKZ75pPNDXB2T5XYQE');
- process.exit(1);
- }
- const BYREAL_PROGRAM_ID = 'REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2';
- const MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
- async function testTransactionParse() {
- console.log('═══════════════════════════════════════════════════');
- console.log('🧪 Testing Transaction Parse for Copy Parameters');
- console.log(`📍 Transaction: ${TX_SIGNATURE.slice(0, 20)}...`);
- console.log('═══════════════════════════════════════════════════\n');
- const connection = new Connection(CONFIG.RPC_URL, 'confirmed');
- try {
- // Step 1: Fetch transaction
- console.log('Step 1: Fetching transaction from Solana...');
- const tx = await connection.getTransaction(TX_SIGNATURE, {
- commitment: 'confirmed',
- maxSupportedTransactionVersion: 0
- });
- if (!tx || !tx.transaction || !tx.meta) {
- console.error('❌ Transaction not found or invalid');
- process.exit(1);
- }
- console.log('✅ Transaction found\n');
- // Step 2: Check Byreal program
- console.log('Step 2: Checking Byreal program involvement...');
- const message = tx.transaction.message;
- const accountKeys = message.staticAccountKeys || message.accountKeys || [];
-
- const hasByrealProgram = accountKeys.some(
- key => key.toString() === BYREAL_PROGRAM_ID
- );
- if (!hasByrealProgram) {
- console.error('❌ Transaction does not involve Byreal program');
- process.exit(1);
- }
- console.log('✅ Byreal program found\n');
- // Step 3: Extract parent position from memo
- console.log('Step 3: Extracting parent position from memo...');
- let parentPosition = null;
- const instructions = message.compiledInstructions || message.instructions || [];
- 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 data: ${memoData}`);
- const match = memoData.match(/referer_position=([A-Za-z0-9]+)/);
- if (match) {
- parentPosition = match[1];
- }
- }
- }
- }
- // Check log messages as fallback
- if (!parentPosition && tx.meta.logMessages) {
- for (const log of tx.meta.logMessages) {
- const match = log.match(/referer_position=([A-Za-z0-9]+)/);
- if (match) {
- parentPosition = match[1];
- console.log(` Found in logs: ${memoData}`);
- break;
- }
- }
- }
- if (!parentPosition) {
- console.error('❌ No parent position found in memo');
- process.exit(1);
- }
- console.log(`✅ Parent Position: ${parentPosition}\n`);
- // Step 4: Fetch parent position details
- console.log('Step 4: Fetching parent position details from Byreal API...');
- const parentDetail = await ByrealAPI.getPositionDetail(parentPosition);
- if (!parentDetail || !parentDetail.pool) {
- console.error('❌ Failed to fetch parent position details');
- process.exit(1);
- }
- console.log('✅ Parent position details:');
- console.log(` Pool: ${parentDetail.pool.mintA?.symbol}/${parentDetail.pool.mintB?.symbol}`);
- console.log(` Token A: ${parentDetail.pool.mintA?.address}`);
- console.log(` Token B: ${parentDetail.pool.mintB?.address}`);
- console.log(` NFT Mint: ${parentDetail.nftMintAddress || parentDetail.nftMint}`);
- console.log(` Pool Address: ${parentDetail.pool?.poolAddress || parentDetail.poolAddress}\n`);
- const tokenAMint = parentDetail.pool.mintA?.address;
- const tokenBMint = parentDetail.pool.mintB?.address;
- // Step 5: Parse transaction for token transfers
- console.log('Step 5: Parsing transaction for token transfers...');
-
- if (!tx.meta.preTokenBalances || !tx.meta.postTokenBalances) {
- console.error('❌ No token balance data in transaction');
- process.exit(1);
- }
- // Build pre-balance map
- const preBalances = {};
- tx.meta.preTokenBalances.forEach(balance => {
- if (balance.mint === tokenAMint || balance.mint === tokenBMint) {
- const key = `${balance.accountIndex}-${balance.mint}`;
- preBalances[key] = {
- amount: parseFloat(balance.uiTokenAmount?.uiAmountString || 0),
- decimals: balance.decimals
- };
- }
- });
- console.log(` Pre-balances found: ${Object.keys(preBalances).length}`);
- // Find transfers by comparing pre/post
- const transfers = [];
- tx.meta.postTokenBalances.forEach(balance => {
- if (balance.mint === tokenAMint || balance.mint === tokenBMint) {
- const key = `${balance.accountIndex}-${balance.mint}`;
- const pre = preBalances[key];
-
- if (pre) {
- const diff = pre.amount - parseFloat(balance.uiTokenAmount?.uiAmountString || 0);
- if (diff > 0.000001) {
- transfers.push({
- mint: balance.mint,
- amount: diff,
- decimals: balance.decimals || pre.decimals || 6,
- symbol: balance.mint === tokenAMint ? parentDetail.pool.mintA?.symbol : parentDetail.pool.mintB?.symbol
- });
- console.log(` Transfer detected: ${diff} ${balance.mint === tokenAMint ? parentDetail.pool.mintA?.symbol : parentDetail.pool.mintB?.symbol}`);
- }
- }
- }
- });
- if (transfers.length === 0) {
- console.error('❌ No token transfers found for target tokens');
- process.exit(1);
- }
- // Assign to tokenA and tokenB
- const tokenA = transfers.find(t => t.mint === tokenAMint) || { mint: tokenAMint, amount: 0, decimals: 6, symbol: parentDetail.pool.mintA?.symbol };
- const tokenB = transfers.find(t => t.mint === tokenBMint) || { mint: tokenBMint, amount: 0, decimals: 6, symbol: parentDetail.pool.mintB?.symbol };
- // Calculate USD value (simplified)
- const TOKEN_PRICES = {
- 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v': 1, // USDC
- 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB': 1, // USDT
- };
- const priceA = TOKEN_PRICES[tokenAMint] || 0;
- const priceB = TOKEN_PRICES[tokenBMint] || 0;
- const totalUsdValue = (tokenA.amount * priceA) + (tokenB.amount * priceB);
- if (totalUsdValue === 0) {
- // For non-stable pairs, use sum
- console.log(' ⚠️ No price data available, using sum of amounts');
- }
- console.log(`✅ Token transfers extracted:\n`);
- // Step 6: Print copy parameters
- console.log('═══════════════════════════════════════════════════');
- console.log('📋 COPY PARAMETERS');
- console.log('═══════════════════════════════════════════════════\n');
- console.log('Parent Position:');
- console.log(` Address: ${parentPosition}`);
- console.log(` NFT Mint: ${parentDetail.nftMintAddress || parentDetail.nftMint}`);
- console.log(` Pool Address: ${parentDetail.pool?.poolAddress || parentDetail.poolAddress}`);
- console.log(` Pool: ${parentDetail.pool.mintA?.symbol}/${parentDetail.pool.mintB?.symbol}\n`);
- console.log('Token Transfers (Target deposited):');
- console.log(` Token A: ${tokenA.amount} ${tokenA.symbol}`);
- console.log(` Mint: ${tokenA.mint}`);
- console.log(` Decimals: ${tokenA.decimals}`);
- console.log(` Token B: ${tokenB.amount} ${tokenB.symbol}`);
- console.log(` Mint: ${tokenB.mint}`);
- console.log(` Decimals: ${tokenB.decimals}\n`);
- console.log('Calculated Values:');
- console.log(` Token A USD: $${(tokenA.amount * priceA).toFixed(2)}`);
- console.log(` Token B USD: $${(tokenB.amount * priceB).toFixed(2)}`);
- console.log(` Total USD Value: $${totalUsdValue.toFixed(2)}\n`);
- const copyMultiplier = CONFIG.COPY_MULTIPLIER;
- const maxUsd = CONFIG.MAX_USD_VALUE;
- const copyUsd = Math.min(totalUsdValue * copyMultiplier, maxUsd);
- console.log('Copy Calculation:');
- console.log(` Target Value: $${totalUsdValue.toFixed(2)}`);
- console.log(` Multiplier: ${copyMultiplier}x`);
- console.log(` Calculated: $${(totalUsdValue * copyMultiplier).toFixed(2)}`);
- console.log(` Max Limit: $${maxUsd}`);
- console.log(` ✅ Final Copy Amount: $${copyUsd.toFixed(2)}\n`);
- console.log('═══════════════════════════════════════════════════');
- console.log('✅ Test completed successfully!');
- console.log('═══════════════════════════════════════════════════');
- // Return data structure matching what sniper expects
- return {
- parentPositionAddress: parentPosition,
- transactionSignature: TX_SIGNATURE,
- parentDetail: parentDetail,
- tokenTransfers: {
- tokenA: tokenA,
- tokenB: tokenB,
- totalUsdValue: totalUsdValue
- },
- targetUsdValue: totalUsdValue,
- copyUsdValue: copyUsd
- };
- } catch (error) {
- console.error('❌ Test failed:', error);
- process.exit(1);
- }
- }
- testTransactionParse().then(data => {
- console.log('\n📦 Full data structure (as passed to sniper):');
- console.log(JSON.stringify({
- parentPositionAddress: data.parentPositionAddress,
- transactionSignature: data.transactionSignature,
- targetUsdValue: data.targetUsdValue,
- copyUsdValue: data.copyUsdValue
- }, null, 2));
- });
|