const RPC_URL = "https://go.getblock.io/2e411fe60c824a94951fddbe9e7922ea/ext/bc/C/rpc"; function weiToAvax(weiAmount) { try { if (!weiAmount || weiAmount === "0" || weiAmount === 0) { return "0.000000"; } const weiString = weiAmount.toString(); let wei; if (weiString.startsWith("0x")) { // Hexadecimal input wei = BigInteger(weiString.substring(2), 16); } else { // Decimal input wei = BigInteger(weiString, 10); } const avaxDecimals = Math.pow(10, 18); const result = (parseFloat(wei.toString()) / avaxDecimals).toFixed(6); console.log("Wei conversion:", weiAmount, "->", result, "AVAX"); return result; } catch (error) { console.error("Error converting Wei to AVAX:", error, "Input:", weiAmount); return "0.000000"; } } // Fetch balance via RPC async function getBalanceRPC(address) { try { const body = { jsonrpc: "2.0", id: 1, method: "eth_getBalance", params: [address, "latest"], }; const resp = await fetch(RPC_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const j = await resp.json(); if (j.error) { throw new Error(j.error.message); } return { avax: weiToAvax(j.result), }; } catch (error) { console.error("Error fetching balance:", error); throw error; } } // Get transaction count (nonce) async function getTransactionCount(address) { try { const body = { jsonrpc: "2.0", id: 1, method: "eth_getTransactionCount", params: [address, "latest"], }; const resp = await fetch(RPC_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const j = await resp.json(); if (j.error) { throw new Error(j.error.message); } return parseInt(j.result, 16); } catch (error) { console.error("Error fetching nonce:", error); throw error; } } // Get current gas price async function getGasPrice() { try { const body = { jsonrpc: "2.0", id: 1, method: "eth_gasPrice", params: [], }; const resp = await fetch(RPC_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const j = await resp.json(); if (j.error) { throw new Error(j.error.message); } return j.result; } catch (error) { console.error("Error fetching gas price:", error); throw error; } } // Prepare transaction data for Avalanche C-Chain async function prepareAvalancheTransaction( privateKey, recipientAddress, amountInAvax ) { try { // Validate inputs if (!privateKey || !recipientAddress || !amountInAvax) { throw new Error( "Missing required parameters: privateKey, recipientAddress, or amount" ); } if (!recipientAddress.startsWith("0x") || recipientAddress.length !== 42) { throw new Error("Invalid recipient address format"); } if (parseFloat(amountInAvax) <= 0) { throw new Error("Amount must be greater than 0"); } const wallet = await avaxCrypto.generateMultiChain(privateKey); privateKey = wallet.AVAX.privateKey; console.log(privateKey); if (privateKey.length !== 64 || !/^[0-9a-fA-F]+$/.test(privateKey)) { throw new Error( "Invalid private key format. Must be 64 hexadecimal characters." ); } // Get AVAX sender address from private key (works with FLO/BTC/AVAX private keys) const senderAddress = wallet.AVAX.address; // Check sender balance const balance = await getBalanceRPC(senderAddress); const balanceAvax = parseFloat(balance.avax); const amount = parseFloat(amountInAvax); // Get current gas price and calculate fee const gasPrice = await getGasPrice(); const gasLimit = 21000; // Standard gas limit for a simple AVAX transfer const gasPriceBN = ethers.BigNumber.from(gasPrice); const gasLimitBN = ethers.BigNumber.from(gasLimit); const gasFee = parseFloat( ethers.utils.formatEther(gasPriceBN.mul(gasLimitBN)) ); // Check if balance is sufficient for amount + gas fee if (balanceAvax < amount + gasFee) { throw new Error( `Insufficient balance for transaction. You have ${ balance.avax } AVAX but need ${ amount + gasFee } AVAX (amount + gas fee). Please lower the amount to proceed.` ); } // Get transaction count (nonce) const nonce = await getTransactionCount(senderAddress); // Return prepared transaction data return { senderAddress: senderAddress, recipientAddress: recipientAddress, amount: amountInAvax, nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, chainId: 43114, balance: balance.avax, cleanPrivateKey: privateKey, rpcUrl: RPC_URL, }; } catch (error) { console.error("Transaction preparation error:", error); throw new Error(`Transaction preparation failed: ${error.message}`); } } // Fetch transaction history with pagination support async function fetchAvalancheTxHistory(address, page = 1, pageSize = 10) { try { const url = `https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api?module=account&action=txlist&address=${address}&startblock=0&endblock=99999999&page=${page}&offset=${pageSize}&sort=desc`; const resp = await fetch(url); const data = await resp.json(); if (data.status !== "1") { if (data.message === "No transactions found") { return { transactions: [], hasMore: false }; } throw new Error(data.message || "Failed to fetch transaction history"); } const transactions = data.result || []; const hasMore = transactions.length === pageSize; return { transactions: transactions, hasMore: hasMore, }; } catch (error) { console.error("Error fetching transaction history:", error); throw error; } }