Quantcast
Channel: Recent Questions - Solana Stack Exchange
Viewing all articles
Browse latest Browse all 7906

Recursively identify associated wallet addresses

$
0
0

I'm looking for guidance or tools to help with a project involving the recursive exploration of blockchain addresses. My goal is to trace all associated wallet addresses by following every transaction linked to a particular wallet address, up to a recursive depth of 15-20 hops.

Objective:Create list of all coins associated (or loosely associated) with a specific wallet address by following the transaction trail from the given address to any interacting addresses, continuing this process recursively up to 20 hops.

Specific Example:

  1. Start with the wallet address used for minting.
  2. Identify all wallets that have transferred to this minting address.
  3. Identify all wallets that have transferred to those wallets.
  4. Continue this process recursively for all subsequent wallets, working my way outwards up to a depth of 15-20 hops

I attempted to implement this in both Rust and Python (for some reason I could only get my rust code to return signatures but not transactions/transfer address details such as source/destination address and amount SOL transferred)

Below is a JavaScript implementation I tried using the Solana Web3.js library:

    const solanaWeb3 = require('@solana/web3.js');    const fs = require('fs');    // Establish a connection to the Solana mainnet    const connection = new solanaWeb3.Connection(solanaWeb3.clusterApiUrl('mainnet-beta'), 'confirmed');    const processedAddresses = new Set();    let transactions = [];    const MAX_BATCH_SIZE = 100; // Maximum number of transactions to fetch per request    const INITIAL_RETRY_DELAY_MS = 500; // Initial delay for retrying requests    const MAX_RETRIES = 5; // Maximum number of retries for a request    const DELAY_MS = 6000; // Delay between requests to avoid rate limits    // Utility function to pause execution for a given amount of time    async function sleep(ms) {        return new Promise(resolve => setTimeout(resolve, ms));    }    // Function to fetch transactions for a given wallet address, with recursion to explore linked addresses    async function fetchTransactions(walletAddress, depth, retryCount = 0) {        if (depth === 0 || processedAddresses.has(walletAddress)) return; // Stop recursion if depth is 0 or address is already processed        processedAddresses.add(walletAddress);        console.log(`Fetching transactions for wallet: ${walletAddress} at depth: ${depth}`);        let before = undefined;        let allSignatures = [];        // Fetch transaction signatures in smaller batches with rate limiting        while (true) {            try {                let transactionList = await connection.getConfirmedSignaturesForAddress2(                    new solanaWeb3.PublicKey(walletAddress),                    { limit: MAX_BATCH_SIZE, before }                );                if (transactionList.length === 0) break; // No more transactions to fetch                let signaturesList = transactionList.map(transaction => transaction.signature);                allSignatures = allSignatures.concat(signaturesList);                before = transactionList[transactionList.length - 1].signature;                console.log(`Fetched ${transactionList.length} signatures, total: ${allSignatures.length}`);                process.stdout.write('');                // Delay between requests to avoid rate limiting                await sleep(DELAY_MS);            } catch (error) {                if (error.message.includes("Too Many Requests")) {                    console.log("Rate limit hit, retrying after delay...");                    await sleep(DELAY_MS * 2); // Exponential backoff                } else {                    throw error;                }            }        }        // Process transactions in batches to avoid rate limits        for (let i = 0; i < allSignatures.length; i += MAX_BATCH_SIZE) {            let batchSignatures = allSignatures.slice(i, i + MAX_BATCH_SIZE);            try {                let txDetails = await connection.getParsedTransactions(batchSignatures, {                    maxSupportedTransactionVersion: 0,                });                console.log(`Processing batch ${i / MAX_BATCH_SIZE + 1}: ${batchSignatures.length} signatures`);                process.stdout.write('');                for (let tx of txDetails) {                    if (tx && tx.transaction) {                        transactions.push(tx);                        extractAndFetchNewAddresses(tx, depth - 1); // Recursively fetch new addresses                    }                }                // Delay between requests to avoid rate limiting                await sleep(DELAY_MS);            } catch (error) {                if (error.message.includes("Too Many Requests")) {                    console.log("Rate limit hit, retrying after delay...");                    await sleep(DELAY_MS * 2); // Exponential backoff                } else {                    throw error;                }            }        }    }    // Extract addresses from a transaction and fetch transactions for new addresses recursively    function extractAndFetchNewAddresses(transaction, depth) {        if (!transaction || !transaction.transaction || !transaction.transaction.message) {            console.error('Invalid transaction structure:', transaction);            return;        }        const involvedAddresses = new Set();        // Collect all addresses involved in the transaction        transaction.transaction.message.accountKeys.forEach(accountKey => {            involvedAddresses.add(accountKey.toString());        });        // Recursively fetch transactions for each new address        involvedAddresses.forEach(address => {            if (!processedAddresses.has(address)) {                fetchTransactions(address, depth);            }        });    }    // Main function to start the transaction fetching process    async function main() {        const initialWalletAddress = 'PASTE_WALLET_ADDRESS_TO_ANALYZE_HERE';        const recursionDepth = 10;  // Adjust this value based on how deep you want to fetch        await fetchTransactions(initialWalletAddress, recursionDepth);        fs.writeFileSync('transactionDetails.json', JSON.stringify(transactions, null, 2)); // Save transaction details to a file        console.log('Transaction details saved to transactionDetails.json');    }    // Execute the main function and handle any errors    main().catch(err => {        console.error('Error in main:', err);    });

I'm open to switching back to rust or python instead of JS if that will make it easier. Also, I'm open to paying for an existing solutions that can do this (if there is one already available). Thanks in advance for your help!


Viewing all articles
Browse latest Browse all 7906

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>