Created
September 2, 2023 20:50
-
-
Save ottosch/9edc99a97267f4bc78b2ed57ba62813b to your computer and use it in GitHub Desktop.
Identify BRC-20 sats in mempool
This file contains 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
#! /usr/bin/env node | |
const rpc = require("node-bitcoin-rpc"); | |
const fs = require("fs"); | |
const inscriptionMark = Buffer.from("0063036f7264", "hex"); | |
const keywords = ["brc-20", "op", "mint", "tick", "\"sats\"", "amt"]; | |
const op_endif = 0x68; | |
async function run() { | |
init(); | |
let txids = await getMempoolTxids(); | |
console.log(`Tx count: ${txids.length}`); | |
let count = 0; | |
let inscriptionCount = 0; | |
let tokenCount = 0; | |
for (let txid of txids) { | |
count++; | |
if (count % 100 === 0) { | |
process.stdout.write("."); | |
} | |
let tx = await getRawTx(txid); | |
if (!tx) { | |
continue; | |
} | |
let obj = { | |
raw: Buffer.from(tx, "hex"), | |
pointer: 0, | |
data: null, | |
}; | |
let inscription = getInscription(obj); | |
if (!inscription) { | |
continue; | |
} | |
inscriptionCount++; | |
if (isSatsToken(inscription)) { | |
tokenCount++; | |
} | |
} | |
console.log(`\ntx count:\t${count}`); | |
console.log(`inscriptions:\t${inscriptionCount}`); | |
console.log(`sats token:\t${tokenCount}`); | |
} | |
async function getMempoolTxids() { | |
return new Promise(resolve => { | |
rpc.call("getrawmempool", [], (e, r) => { | |
if (e) { | |
console.error(e); | |
resolve(null); | |
} | |
resolve(r.result); | |
}); | |
}); | |
} | |
async function getRawTx(txid) { | |
return new Promise(resolve => { | |
rpc.call("getrawtransaction", [txid], (e, r) => { | |
if (e) { | |
console.error(e); | |
resolve(null); | |
} | |
resolve(r.result); | |
}); | |
}); | |
} | |
function init() { | |
let cookie = fs.readFileSync(`${process.env.HOME}/.bitcoin/.cookie`).toString(); | |
let credentials = cookie.split(":"); | |
rpc.init("127.0.0.1", 8332, credentials[0], credentials[1]); | |
rpc.setTimeout(Number(process.env.TIMEOUT) || 30000); | |
} | |
function readBytes(obj, n = 1) { | |
let value = obj.raw.subarray(obj.pointer, obj.pointer + n); | |
obj.pointer += n; | |
return value; | |
} | |
function readPushdata(obj, opcode) { | |
if (opcode >= 0x01 && opcode <= 0x4b) { | |
return readBytes(obj, opcode); | |
} | |
let length; | |
switch (opcode) { | |
case 0x4c: | |
length = readBytes(obj, 1).readUint8(); | |
break; | |
case 0x4d: | |
length = readBytes(obj, 2).readUint16LE(); | |
break; | |
case 0x4e: | |
length = readBytes(obj, 4).readUint32LE(); | |
break; | |
default: | |
return null; | |
} | |
return readBytes(obj, length); | |
} | |
function getInscription(obj) { | |
let markIndex = obj.raw.indexOf(inscriptionMark); | |
if (markIndex === -1) { | |
return null; | |
} | |
obj.pointer = markIndex + inscriptionMark.length; | |
let b = readBytes(obj, 2); | |
if (b.hexSlice() != "0101") { | |
return null; | |
} | |
let contentTypeLength = readBytes(obj).readUInt8(); | |
readBytes(obj, contentTypeLength); | |
if (readBytes(obj)[0] != 0x00) { | |
return null; | |
} | |
obj.data = Buffer.alloc(0); | |
let opcode = readBytes(obj)[0]; | |
while (opcode != op_endif) { | |
let chunk = readPushdata(obj, opcode); | |
obj.data = Buffer.concat([obj.data, chunk]); | |
opcode = readBytes(obj)[0] | |
} | |
return obj.data; | |
} | |
function isSatsToken(bytes) { | |
let text = bytes.toString(); | |
for (let word of keywords) { | |
if (text.indexOf(word) === -1) { | |
return false; | |
} | |
} | |
return true; | |
} | |
run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment