Skip to content

Instantly share code, notes, and snippets.

@Danijel-Enoch
Created October 8, 2024 19:27
Show Gist options
  • Save Danijel-Enoch/63b720c91000bf26838c95c0f3326420 to your computer and use it in GitHub Desktop.
Save Danijel-Enoch/63b720c91000bf26838c95c0f3326420 to your computer and use it in GitHub Desktop.
parse script for eth pending tranasctions
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