UNPKG

flashloan-profit-calculator

Version:

A library for analyzing flashloan transactions and calculating profits

229 lines (228 loc) 9.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTransactionDetails = getTransactionDetails; exports.parseLogsFromReceipt = parseLogsFromReceipt; const alchemy_sdk_1 = require("alchemy-sdk"); const alchemy_1 = require("./alchemy"); const events_1 = require("../constants/events"); const parsing_1 = require("../utils/parsing"); /** * Get transaction details from a transaction hash */ async function getTransactionDetails(txHash) { try { // Get transaction details let transaction = null; try { transaction = await alchemy_1.alchemy.core.getTransaction(txHash); if (!transaction) { throw new Error(`Transaction ${txHash} not found`); } } catch (error) { console.error(`Failed to get transaction: ${error}`); throw error; } // Get transaction receipt for logs let receipt = null; try { receipt = await alchemy_1.alchemy.core.getTransactionReceipt(txHash); if (!receipt) { throw new Error(`Transaction receipt for ${txHash} not found`); } console.log(`Got receipt with ${receipt.logs.length} logs`); } catch (error) { console.error(`Failed to get receipt: ${error}`); throw error; } // Get block number const blockNumber = receipt.blockNumber; // Get sender address const senderAddress = transaction.from.toLowerCase(); // Get contract address (the "to" address of the transaction) const contractAddress = transaction.to?.toLowerCase() || ""; // Get trace data let trace = null; try { trace = await alchemy_1.alchemy.debug.traceTransaction(txHash, { type: alchemy_sdk_1.DebugTracerType.CALL_TRACER, }); console.log(`Got trace data: ${JSON.stringify(trace).substring(0, 200)}...`); } catch (error) { console.error(`Failed to get trace data: ${error}`); console.error(`Error details: ${JSON.stringify(error)}`); throw error; } // Process trace calls safely let traceCalls = []; try { if (Array.isArray(trace)) { traceCalls = trace; } else if (typeof trace === "object" && trace !== null) { const traceObj = trace; if (Array.isArray(traceObj.calls)) { traceCalls = traceObj.calls; } } console.log(`Extracted ${traceCalls.length} trace calls`); } catch (error) { console.error(`Failed to extract trace calls: ${error}`); // Continue with empty trace calls } // Parse logs from receipt let parsedLogs; try { parsedLogs = parseLogsFromReceipt(receipt); console.log(`Parsed ${parsedLogs.length} logs from receipt`); } catch (error) { console.error(`Failed to parse logs: ${error}`); throw error; } return { blockNumber, senderAddress, contractAddress, trace: traceCalls, logs: parsedLogs, }; } catch (error) { console.error(`Failed to get transaction details: ${error}`); console.error(`Stack trace: ${error.stack}`); throw error; } } /** * Parse logs from transaction receipt without relying on external ABIs */ function parseLogsFromReceipt(receipt) { try { const parsedLogs = []; for (const log of receipt.logs) { try { // Handle Transfer events (ERC20/ERC721) if (log.topics[0] === events_1.TRANSFER_EVENT_TOPIC) { try { // Create a TransferLog object for the Transfer event const transferLog = { raw: { address: log.address.toLowerCase(), topics: log.topics, data: log.data, }, name: "Transfer", anonymous: false, inputs: [ { name: "from", value: (0, parsing_1.padAddress)(log.topics[1]), type: "address", indexed: true, }, { name: "to", value: (0, parsing_1.padAddress)(log.topics[2]), type: "address", indexed: true, }, { name: "value", value: (0, parsing_1.parseAmountSafely)(log.data), type: "uint256", indexed: false, }, ], }; // console.log( // ` Parsed Transfer: ${transferLog.inputs[0].value} -> ${transferLog.inputs[1].value}, amount: ${transferLog.inputs[2].value}` // ); parsedLogs.push(transferLog); } catch (error) { console.error(` Failed to parse Transfer event: ${error}`); } } // Handle Withdrawal events (WETH) if (log.topics[0] === events_1.WITHDRAWAL_EVENT_TOPIC) { try { const withdrawalLog = { raw: { address: log.address.toLowerCase(), topics: log.topics, data: log.data, }, name: "Withdrawal", anonymous: false, inputs: [ { name: "src", value: (0, parsing_1.padAddress)(log.topics[1]), type: "address", indexed: true, }, { name: "wad", value: (0, parsing_1.parseAmountSafely)(log.data), type: "uint256", indexed: false, }, ], }; console.log(` Parsed Withdrawal: from ${withdrawalLog.inputs[0].value}, amount: ${withdrawalLog.inputs[1].value}`); parsedLogs.push(withdrawalLog); } catch (error) { console.error(` Failed to parse Withdrawal event: ${error}`); } } // Handle Deposit events (WETH) if (log.topics[0] === events_1.DEPOSIT_EVENT_TOPIC) { try { const depositLog = { raw: { address: log.address.toLowerCase(), topics: log.topics, data: log.data, }, name: "Deposit", anonymous: false, inputs: [ { name: "dst", value: (0, parsing_1.padAddress)(log.topics[1]), type: "address", indexed: true, }, { name: "wad", value: (0, parsing_1.parseAmountSafely)(log.data), type: "uint256", indexed: false, }, ], }; console.log(` Parsed Deposit: to ${depositLog.inputs[0].value}, amount: ${depositLog.inputs[1].value}`); parsedLogs.push(depositLog); } catch (error) { console.error(` Failed to parse Deposit event: ${error}`); } } } catch (error) { // Skip logs that can't be parsed console.warn(`Failed to process log: ${error}`); } } return parsedLogs; } catch (error) { console.error(`Fatal error in parseLogsFromReceipt: ${error}`); throw error; } }