Skip to content

Instantly share code, notes, and snippets.

@tunnckoCore
Last active March 17, 2024 08:45
Show Gist options
  • Save tunnckoCore/f94fe835a12512287a44e0e8bad4a069 to your computer and use it in GitHub Desktop.
Save tunnckoCore/f94fe835a12512287a44e0e8bad4a069 to your computer and use it in GitHub Desktop.
Track Ethereum Blob Transactions with Viem

Just import and call trackBlobs(handler).

import { trackBlobs, onlyDataURIBlobs } from './pkg';

await trackBlobs(({ parentBlockSlotHash, block, blobTransactions, blobs }) => {
  const { dataBytes } = onlyDataURIBlobs({ blobs });
  
  if (dataBytes) {
    console.log('Data URI:', new TextDecoder().decode(dataBytes));
  }
});
export const client = createPublicClient({
chain: mainnet,
transport: transport,
});
export async function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export async function getBlobsFromTx(tx, hostname = "eth.blockscout.com") {
// const json = await fetch(`https://${hostname}/api/v2/transactions/` + id)
// .then((x) => x.json());
await delay(15_000);
// console.log(json)
const hashes = tx.blobVersionedHashes;
const getBlob = async (hash) => {
const res = await fetch(`https://${hostname}/api/v2/blobs/` + hash).then((x) => x.json());
res.blob_hash = hash;
// the 0-th is the first transaction that this specific blob content/data is found in,
// if the same exact blob content is found multiple times, there will be more transaction in that array
res.transaction_hash = res.transaction_hashes[0].transaction_hash;
res.duplicates = res.transaction_hashes.slice(1).map((x) => x.transaction_hash);
delete res.transaction_hashes;
delete res.hash;
return res;
};
if (hashes.length === 1) {
const res = await getBlob(hashes[0]);
return [res];
} else if (hashes.length > 1) {
const data = await Promise.all(
hashes.map(async (hash) => {
const blob = await getBlob(hash);
return blob;
}),
);
return data;
}
return [];
}
export async function trackBlobs(handler, log = false) {
client.watchBlocks({
pollingInterval: 5_500,
emitMissed: true,
blockTag: "latest",
includeTransactions: true,
onBlock: async (block) => {
const blobTransactions = block.transactions.filter((tx) => tx.type === "eip4844");
const blobsTxs = await Promise.all(
blobTransactions.map(async (tx) => {
// console.log(tx)
tx.blobs = await getBlobsFromTx(tx);
return tx;
}),
);
const slotHash = block.parentBeaconBlockRoot;
const blobs = blobsTxs.map((x) => x.blobs).flat();
if (log) {
console.log(blobsTxs.map((x) => x.blobs).flat());
console.log("Blob Txs:", blobsTxs.length);
console.log(
"Blobs Count:",
blobsTxs.reduce((acc, x) => acc + x.blobVersionedHashes.length, 0),
);
console.log("Block:", block.number);
console.log("====================\n");
}
handler({ block, blobTransactions: blobsTxs, slotHash, blobs });
},
});
}
export function onlyDataURIBlobs({ blobs }, log = false) {
// convert blobs into proper hex data values
const blobsDataHexed = blobs.map((x) => fromBlobs({ blobs: [x.blob_data] }));
if (blobsDataHexed.length > 0 && blobsDataHexed[0].startsWith('0x646174613a')) {
// combine all hex data values into one
const combinedDataHex = '0x' + (blobsDataHexed.map(x => x.slice(2)).join(''));
// convert to bytes (uint8array)
const dataBytes = hexToBytes(combinedDataHex as `0x${string}`);
if (log) {
console.log('bytes:', dataBytes.length);
console.log('data uri:', new TextDecoder().decode(dataBytes));
}
return { dataBytes, combinedDataHex }
}
return {};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment