-
-
Save Danijel-Enoch/63b720c91000bf26838c95c0f3326420 to your computer and use it in GitHub Desktop.
parse script for eth pending tranasctions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as ethers from "ethers"; | |
import { TransactionReceipt } from "ethers"; | |
// Connect to an Ethereum node (replace with your own node URL) | |
const provider = new ethers.JsonRpcProvider( | |
"https://mainnet.infura.io/v3/" | |
); | |
// ERC20 Transfer event signature | |
const TRANSFER_EVENT_SIGNATURE = | |
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; | |
async function getERC20Transfers(txHash) { | |
try { | |
console.log("Getting transaction receipt..."); | |
// Try to get transaction receipt (for mined transactions) | |
const txReceipt = await provider.getTransactionReceipt(txHash); | |
if (txReceipt) { | |
console.log("Transaction receipt found"); | |
return processLogs(txReceipt.logs); | |
} else { | |
console.log( | |
"Transaction receipt not found, simulating transaction..." | |
); | |
// If transaction is pending, simulate it | |
const tx = await provider.getTransaction(txHash); | |
const block = await provider.getBlock("latest"); | |
//console.log({ tx }); | |
const parsedData = parseTransactionData(tx.data); | |
const interpretedData = interpretData(parsedData); | |
const { possibleAddresses, possibleAmounts, otherParams } = | |
interpretedData; | |
if (possibleAddresses.length > 0) { | |
console.log( | |
txHash, | |
tx.from, | |
tx.to, | |
"Possible addresses:", | |
possibleAddresses.map((p) => p.address) | |
); | |
} | |
// Extract logs from simulation result | |
// console.log({ decodedResult }); | |
//const decodedResult = await provider.getTransactionReceipt(result); | |
return; | |
} | |
} catch (error) { | |
console.error("Error processing transaction:", error); | |
return []; | |
} | |
} | |
function processLogs(logs) { | |
console.log("Processing logs..."); | |
return logs | |
.filter((log) => log.topics[0] === TRANSFER_EVENT_SIGNATURE) | |
.map((log) => { | |
//console.log(log); | |
return { | |
tokenAddress: log.address, | |
from: convertHexToEVMAddress(log.topics[1].slice(-40)), | |
to: convertHexToEVMAddress(log.topics[2].slice(-40)), | |
value: ethers.formatUnits(log.data, 18) | |
}; | |
}); | |
} | |
// Example usage | |
async function main() { | |
try { | |
const provider = new ethers.WebSocketProvider( | |
"wss://ws-rpc.graffiti.farm" | |
); | |
console.log("Getting ERC20 transfers..."); | |
const txHash = | |
"0x779ba339069cdbafc75099b852e4bcbbdaa9318398616961857bc28c1731a0e1"; // Replace with an actual transaction hash (can be pending or mined) | |
provider.on("pending", async (txHash) => { | |
const tx = await provider.getTransaction(txHash); | |
console.log("New pending transaction for contract:", txHash); | |
const erc20Transfers = await getERC20Transfers(txHash); | |
console.log(erc20Transfers); | |
// Process the transaction here | |
}); | |
// return erc20Transfers.forEach((transfer) => { | |
// console.log(`Token: ${transfer.tokenAddress}`); | |
// console.log(`From: ${transfer.from}`); | |
// console.log(`To: ${transfer.to}`); | |
// console.log(`Value: ${transfer.value.toString()}`); | |
// console.log("---"); | |
// }); | |
} catch (error) { | |
console.error("Error processing transaction:", error); | |
return []; | |
} | |
} | |
function parseTransactionData(data) { | |
if (!data.startsWith("0x")) { | |
data = "0x" + data; | |
} | |
const parsedData = { | |
functionSelector: data.slice(0, 10), | |
parameters: [] | |
}; | |
// Remove function selector | |
data = "0x" + data.slice(10); | |
// Parse parameters (each parameter is 32 bytes / 64 characters) | |
while (data.length >= 66) { | |
// 0x + 64 characters | |
const param = data.slice(0, 66); | |
parsedData.parameters.push(param); | |
data = "0x" + data.slice(66); | |
} | |
return parsedData; | |
} | |
function interpretData(parsedData) { | |
const interpreted = { | |
functionSelector: parsedData.functionSelector, | |
possibleAddresses: [], | |
possibleAmounts: [], | |
otherParams: [] | |
}; | |
parsedData.parameters.forEach((param, index) => { | |
// Check if it might be an address (20 bytes) | |
if (param.slice(-40).match(/^[0-9a-fA-F]{40}$/)) { | |
const address = "0x" + param.slice(-40); | |
interpreted.possibleAddresses.push({ | |
index: index, | |
address: address | |
}); | |
// Check if the address could be a number | |
const addressAsNumber = BigInt(address); | |
if (!isNaN(Number(addressAsNumber))) { | |
interpreted.possibleAmounts.push({ | |
index: index, | |
original: address, | |
asNumber: addressAsNumber.toString(), | |
asEther: ethers.formatEther(addressAsNumber) | |
}); | |
} | |
} | |
// Check if it might be an amount (try to convert to a readable number) | |
else if (param.match(/^0x[0-9a-fA-F]{64}$/)) { | |
const bigInt = BigInt(param); | |
interpreted.possibleAmounts.push({ | |
index: index, | |
original: param, | |
asNumber: bigInt.toString(), | |
asEther: ethers.formatEther(bigInt) | |
}); | |
} | |
// Otherwise, just store as other param | |
else { | |
interpreted.otherParams.push({ | |
index: index, | |
value: param | |
}); | |
} | |
}); | |
return interpreted; | |
} | |
function convertHexToEVMAddress(hexString) { | |
// Ensure the hex string starts with '0x' | |
if (!hexString.startsWith("0x")) { | |
hexString = "0x" + hexString; | |
} | |
// An Ethereum address is 20 bytes (40 hexadecimal characters) | |
// We need to take the last 40 characters of the hex string | |
const address = "0x" + hexString.slice(-40); | |
// Validate the result | |
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) { | |
throw new Error("Invalid hexadecimal input"); | |
} | |
return address; | |
} | |
main().catch(console.error); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment