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