UNPKG

flashloan-profit-calculator

Version:

A library for analyzing flashloan transactions and calculating profits

132 lines (131 loc) 5.17 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isWethLikeToken = isWethLikeToken; exports.findWethLikeTokens = findWethLikeTokens; const alchemy_sdk_1 = require("alchemy-sdk"); const dotenv_1 = __importDefault(require("dotenv")); const addresses_1 = require("../constants/addresses"); dotenv_1.default.config(); // Initialize Alchemy const settings = { apiKey: process.env.ALCHEMY_API_KEY, network: alchemy_sdk_1.Network.ETH_MAINNET, }; const alchemy = new alchemy_sdk_1.Alchemy(settings); // Common WETH function signatures const WETH_FUNCTION_SIGNATURES = [ "0xd0e30db0", // deposit() "0x2e1a7d4d", // withdraw(uint256) "0x3d18b912", // deposit(uint256) "0x7fcf532c", // Withdrawal(address,uint256) "0xe1fffcc4", // Deposit(address,uint256) ]; // Storage slot for implementation address in EIP-1967 const IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; // Storage slot for implementation address in OpenZeppelin's TransparentProxy const OZ_IMPLEMENTATION_SLOT = "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3"; /** * Get the implementation address of a proxy contract */ async function getImplementationAddress(proxyAddress) { try { // Try EIP-1967 storage slot const implementation = await alchemy.core.getStorageAt(proxyAddress, IMPLEMENTATION_SLOT); if (implementation && implementation !== "0x0000000000000000000000000000000000000000000000000000000000000000") { return `0x${implementation.slice(-40)}`; } // Try OpenZeppelin's storage slot const ozImplementation = await alchemy.core.getStorageAt(proxyAddress, OZ_IMPLEMENTATION_SLOT); if (ozImplementation && ozImplementation !== "0x0000000000000000000000000000000000000000000000000000000000000000") { return `0x${ozImplementation.slice(-40)}`; } return null; } catch (error) { console.error(`Error getting implementation address for ${proxyAddress}: ${error}`); return null; } } /** * Check if a token is WETH-like by examining its contract code */ async function isWethLikeToken(tokenAddress) { try { // Skip if already known if (addresses_1.WETH_LIKE_TOKENS.has(tokenAddress)) { return true; } // Get contract code const code = await alchemy.core.getCode(tokenAddress); if (!code || code === "0x") { return false; } // Check if this is a proxy contract const implementationAddress = await getImplementationAddress(tokenAddress); if (implementationAddress) { // If it's a proxy, check the implementation contract const implementationCode = await alchemy.core.getCode(implementationAddress); if (!implementationCode || implementationCode === "0x") { return false; } // Check for WETH-like function signatures in implementation const hasWethFunctions = WETH_FUNCTION_SIGNATURES.some((sig) => implementationCode.includes(sig)); if (hasWethFunctions) { // Add both proxy and implementation to known WETH-like tokens addresses_1.WETH_LIKE_TOKENS.add(tokenAddress); addresses_1.WETH_LIKE_TOKENS.add(implementationAddress); return true; } } else { // Not a proxy, check the contract directly const hasWethFunctions = WETH_FUNCTION_SIGNATURES.some((sig) => code.includes(sig)); if (hasWethFunctions) { addresses_1.WETH_LIKE_TOKENS.add(tokenAddress); return true; } } return false; } catch (error) { console.error(`Error checking WETH-like token ${tokenAddress}: ${error}`); return false; } } /** * Get all WETH-like tokens from a transaction's trace */ async function findWethLikeTokens(trace) { try { const processedAddresses = new Set(); // Recursive function to process trace calls const processCalls = async (calls) => { for (const call of calls) { if (call.type === "CALL" && call.to && !processedAddresses.has(call.to)) { processedAddresses.add(call.to); // Check if this is a WETH-like token if (await isWethLikeToken(call.to)) { console.log(`Found WETH-like token: ${call.to}`); } } // Process nested calls if (call.calls && call.calls.length > 0) { await processCalls(call.calls); } } }; await processCalls(trace); } catch (error) { console.error(`Error finding WETH-like tokens: ${error}`); } }