flashloan-profit-calculator
Version:
A library for analyzing flashloan transactions and calculating profits
229 lines (228 loc) • 9.4 kB
JavaScript
;
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;
}
}