UNPKG

flashloan-profit-calculator

Version:

A library for analyzing flashloan transactions and calculating profits

180 lines (179 loc) 6.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.updateBuilderAddress = exports.blockTimestamp = exports.addressParticipation = exports.costBalanceChanges = exports.revenueBalanceChanges = void 0; exports.initializeBalanceChanges = initializeBalanceChanges; exports.trackAddressParticipation = trackAddressParticipation; exports.processTransfer = processTransfer; exports.processTransferLog = processTransferLog; exports.isBuilder = isBuilder; exports.setupProcessor = setupProcessor; exports.resetState = resetState; const alchemy_1 = require("../services/alchemy"); // State management exports.revenueBalanceChanges = {}; exports.costBalanceChanges = {}; exports.addressParticipation = {}; // Configuration variables let builderAddress = ""; let contractAddress = ""; let senderAddress = ""; exports.blockTimestamp = 0; /** * Update the builder address by fetching block information */ const updateBuilderAddress = async (blockNumber) => { const block = await alchemy_1.alchemy.core.getBlock(blockNumber); builderAddress = block.miner; exports.blockTimestamp = block.timestamp; }; exports.updateBuilderAddress = updateBuilderAddress; /** * Initialize balance changes for a token and address */ function initializeBalanceChanges(token, address, isRevenue, isSenderOrContract) { if (!exports.revenueBalanceChanges[token]) { exports.revenueBalanceChanges[token] = {}; } if (!exports.costBalanceChanges[token]) { exports.costBalanceChanges[token] = {}; } if (!exports.revenueBalanceChanges[token][address]) { exports.revenueBalanceChanges[token][address] = { amount: 0n, type: isSenderOrContract ? "Revenue" : "TokenTransfer", }; } if (!exports.costBalanceChanges[token][address]) { exports.costBalanceChanges[token][address] = { amount: 0n, type: isSenderOrContract ? "Cost" : "TokenTransfer", }; } } /** * Track address participation in transfers */ function trackAddressParticipation(from, to, token) { // Initialize address data if not exists if (!exports.addressParticipation[from]) exports.addressParticipation[from] = {}; if (!exports.addressParticipation[to]) exports.addressParticipation[to] = {}; // Initialize token data for addresses if not exists if (!exports.addressParticipation[from][token]) { exports.addressParticipation[from][token] = { toCount: 0, fromCount: 0, participation: 0, }; } if (!exports.addressParticipation[to][token]) { exports.addressParticipation[to][token] = { toCount: 0, fromCount: 0, participation: 0, }; } // Update counts exports.addressParticipation[from][token].fromCount++; exports.addressParticipation[from][token].participation++; exports.addressParticipation[to][token].toCount++; exports.addressParticipation[to][token].participation++; } /** * Process a transfer of tokens between addresses */ function processTransfer(token, from, to, amount) { trackAddressParticipation(from, to, token); // Initialize balance changes const isFromSenderOrContract = from.toLowerCase() === senderAddress || from.toLowerCase() === contractAddress; const isToSenderOrContract = to.toLowerCase() === senderAddress || to.toLowerCase() === contractAddress; initializeBalanceChanges(token, to, true, isToSenderOrContract); initializeBalanceChanges(token, from, false, isFromSenderOrContract); // Update balances if (to !== "0x0") { exports.revenueBalanceChanges[token][to].amount += amount; } if (isBuilder(to)) { exports.revenueBalanceChanges[token][to].amount *= -1n; exports.revenueBalanceChanges[token][to].type = "Cost"; } else { exports.costBalanceChanges[token][from].amount -= amount; } } /** * Process a transfer log with error handling */ function processTransferLog(log) { try { console.log(`Processing ${log.name} log from ${log.raw.address}`); const token = log.raw.address.toLowerCase(); if (log.name === "Transfer") { try { const from = typeof log.inputs[0].value === "string" ? log.inputs[0].value.toLowerCase() : log.inputs[0].value.toString().toLowerCase(); const to = typeof log.inputs[1].value === "string" ? log.inputs[1].value.toLowerCase() : log.inputs[1].value.toString().toLowerCase(); const amount = BigInt(log.inputs[2].value); // console.log(`Transfer: ${from} -> ${to}, amount: ${amount.toString()}`); processTransfer(token, from, to, amount); } catch (error) { console.error(`Error processing Transfer log: ${error}`); } } // Wrapped tokens deduct from the sender's balance during withdrawals but it's not logged // So we need to manually deduct the cost from the sender's balance if (log.name === "Withdrawal") { try { const from = typeof log.inputs[0].value === "string" ? log.inputs[0].value.toLowerCase() : log.inputs[0].value.toString().toLowerCase(); const amount = BigInt(log.inputs[1].value); console.log(`Withdrawal: ${from}, amount: ${amount.toString()}`); // Treat it like a WETH transfer out processTransfer(token, from, "0x0", amount); } catch (error) { console.error(`Error processing Withdrawal log: ${error}`); } } } catch (error) { console.error(`Failed to process log: ${error}`); } } /** * Check if an address is a builder */ function isBuilder(address) { return address.toLowerCase() === builderAddress.toLowerCase(); } /** * Setup the processor with contract and sender addresses */ function setupProcessor(contract, sender) { contractAddress = contract.toLowerCase(); senderAddress = sender.toLowerCase(); } /** * Reset all state */ function resetState() { for (const key of Object.keys(exports.revenueBalanceChanges)) { delete exports.revenueBalanceChanges[key]; } for (const key of Object.keys(exports.costBalanceChanges)) { delete exports.costBalanceChanges[key]; } for (const key of Object.keys(exports.addressParticipation)) { delete exports.addressParticipation[key]; } builderAddress = ""; contractAddress = ""; senderAddress = ""; }