Skip to content

Instantly share code, notes, and snippets.

@h4x3rotab
Last active December 10, 2025 11:00
Show Gist options
  • Select an option

  • Save h4x3rotab/f0c9f279e65f9c3bb6ccf2e77880dc52 to your computer and use it in GitHub Desktop.

Select an option

Save h4x3rotab/f0c9f279e65f9c3bb6ccf2e77880dc52 to your computer and use it in GitHub Desktop.
TON root contract dashboard
{
"dependencies": {
"@ton/core": "^0.62.0",
"@ton/ton": "^16.1.0"
}
}
#!/usr/bin/env node
/**
* Mini TON dashboard: fetches the COCOON root contract state from mainnet and
* prints tokenomics plus the currently registered proxies (on-chain registry).
*
* Requirements:
* npm install @ton/ton @ton/core
*
* Usage:
* ROOT_CONTRACT_ADDRESS=EQ... TONCENTER_API_KEY=... node scripts/ton-dashboard.mjs
* node scripts/ton-dashboard.mjs --root EQ... --endpoint https://toncenter.com/api/v2/jsonRPC
*/
import { TonClient } from "@ton/ton";
import { Address, Cell, Dictionary } from "@ton/core";
const DEFAULT_ROOT =
process.env.ROOT_CONTRACT_ADDRESS ||
"EQBcXvP9DUA4k5tqUapcilt4kZnBzF0Ts7OW0Yp5FI0aN7g0";
const DEFAULT_ENDPOINT =
process.env.TONCENTER_ENDPOINT || "https://toncenter.com/api/v2/jsonRPC";
function parseArgs() {
const args = new Map();
for (let i = 2; i < process.argv.length; i++) {
const arg = process.argv[i];
if (!arg.startsWith("--")) {
args.set("_", arg);
continue;
}
const [k, v] = arg.replace(/^--/, "").split("=");
args.set(k, v ?? process.argv[++i]);
}
return args;
}
const args = parseArgs();
const rootAddress = args.get("_") || args.get("root") || DEFAULT_ROOT;
const endpoint = args.get("endpoint") || DEFAULT_ENDPOINT;
const apiKey = args.get("api-key") || process.env.TONCENTER_API_KEY || "";
function nanoToTon(n) {
return Number(n) / 1e9;
}
function parseParams(cell) {
const cs = cell.beginParse();
const paramsStructVersion = cs.loadUint(8);
const paramsVersion = cs.loadUint(32);
const uniqueId = cs.loadUint(32);
const isTest = cs.loadBit();
const pricePerToken = cs.loadCoins();
const workerFeePerToken = cs.loadCoins();
const promptMult = paramsStructVersion >= 3 ? cs.loadUint(32) : 10000;
const cachedMult = paramsStructVersion >= 2 ? cs.loadUint(32) : 10000;
const completionMult = paramsStructVersion >= 3 ? cs.loadUint(32) : 10000;
const reasoningMult = paramsStructVersion >= 2 ? cs.loadUint(32) : 10000;
const proxyDelayBeforeClose = cs.loadUint(32);
const clientDelayBeforeClose = cs.loadUint(32);
const minProxyStake = paramsStructVersion >= 1 ? cs.loadCoins() : 0n;
const minClientStake = paramsStructVersion >= 1 ? cs.loadCoins() : 0n;
return {
paramsStructVersion,
paramsVersion,
uniqueId,
isTest,
pricePerToken,
workerFeePerToken,
promptMult,
cachedMult,
completionMult,
reasoningMult,
proxyDelayBeforeClose,
clientDelayBeforeClose,
minProxyStake,
minClientStake,
};
}
function extractHashes(cell) {
if (!cell) return [];
const tryCells = [cell, ...cell.refs];
for (const candidate of tryCells) {
try {
const dict = candidate.beginParse().loadDictDirect(
Dictionary.Keys.BigUint(256),
Dictionary.Values.Cell()
);
return [...dict.keys()].map((k) => k.toString(16).padStart(64, "0"));
} catch (e) {
continue;
}
}
return [];
}
function parseProxiesDict(cell) {
if (!cell) return [];
// First, try dict form: { seqno(uint32) -> cell(addresses) }
try {
const dict = cell.beginParse().loadDictDirect(
Dictionary.Keys.Uint(32),
Dictionary.Values.Cell()
);
const proxies = [];
for (const [seq, vcell] of dict) {
const cs = vcell.beginParse();
const typeBit = cs.loadBit(); // 0=ipv4, 1=ipv6
const len = cs.loadUint(7);
const addrBuf = cs.loadBuffer(len);
const addrStr = addrBuf.toString("utf8");
const [forWorkers, forClients] = addrStr.includes(" ")
? addrStr.split(" ", 2)
: [addrStr, addrStr];
proxies.push({
seqno: Number(seq),
type: typeBit ? "ipv6" : "ipv4",
forWorkers,
forClients,
});
}
if (proxies.length) {
return proxies.sort((a, b) => a.seqno - b.seqno);
}
} catch (e) {
// fall through to legacy
}
// Legacy format: single entry with two addresses separated by space, prefixed by a count.
try {
const br = cell.beginParse();
const typeBit = br.loadBit(); // 0=ipv4, 1=ipv6
const len = br.loadUint(7);
const available = Math.floor(br.remainingBits / 8);
const toRead = Math.min(len, available);
const addrBuf = br.loadBuffer(toRead);
const text = addrBuf.toString("utf8");
const m = text.match(
/(\d{1,3}(?:\.\d{1,3}){3}:\d{2,5})(?:\s+(\d{1,3}(?:\.\d{1,3}){3}:\d{2,5}))?/
);
const forWorkers = m ? m[1] : text.trim();
const forClients = m && m[2] ? m[2] : forWorkers;
return [
{
seqno: 0,
type: typeBit ? "ipv6" : "ipv4",
forWorkers,
forClients,
},
];
} catch (e) {
return [];
}
}
async function main() {
const client = new TonClient({ endpoint, apiKey });
const root = Address.parse(rootAddress);
const state = await client.getContractState(root);
if (!state.data) {
throw new Error("Contract has no data (not active?)");
}
const rootCell = Cell.fromBoc(Buffer.from(state.data, "base64"))[0];
const slice = rootCell.beginParse();
const owner = slice.loadAddress();
const dataCell = slice.loadRef();
const version = slice.loadUint(32);
const paramsCell = slice.loadRef();
const parsed = parseParams(paramsCell);
const dataSlice = dataCell.beginParse();
const loadMaybeRefDict = () => {
const exists = dataSlice.loadBit();
return exists ? dataSlice.loadRef() : null;
};
const proxyHashesCell = loadMaybeRefDict();
const proxiesDictCell = loadMaybeRefDict();
const workerHashesCell = loadMaybeRefDict();
const modelHashesCell = loadMaybeRefDict();
const proxyHashes = extractHashes(proxyHashesCell);
const proxies = parseProxiesDict(proxiesDictCell);
const workerHashes = extractHashes(workerHashesCell);
const modelHashes = extractHashes(modelHashesCell);
console.log("Root contract:", root.toString({ bounceable: false }));
console.log("Owner:", owner.toString({ bounceable: false }));
console.log("Version:", version);
console.log("");
console.log("Tokenomics (nanoTON):");
console.log(" price_per_token :", parsed.pricePerToken.toString());
console.log(" worker_fee_per_token:", parsed.workerFeePerToken.toString());
console.log(
` multipliers : prompt=${parsed.promptMult} cached=${parsed.cachedMult} completion=${parsed.completionMult} reasoning=${parsed.reasoningMult}`
);
console.log(
` min stakes (TON) : proxy=${nanoToTon(parsed.minProxyStake)} client=${nanoToTon(parsed.minClientStake)}`
);
console.log(
` close delays (sec) : proxy=${parsed.proxyDelayBeforeClose} client=${parsed.clientDelayBeforeClose}`
);
console.log("");
console.log("Registered proxies:");
if (proxies.length === 0) {
console.log(" (none)");
} else {
proxies.forEach((p) =>
console.log(
` #${p.seqno.toString().padStart(3, " ")} workers=${p.forWorkers} clients=${p.forClients} (${p.type})`
)
);
}
console.log("");
console.log("Proxy hashes:", proxyHashes.length);
proxyHashes.forEach((h) => console.log(" ", h));
console.log("Worker hashes:", workerHashes.length);
workerHashes.forEach((h) => console.log(" ", h));
console.log("Model hashes:", modelHashes.length);
modelHashes.forEach((h) => console.log(" ", h));
}
main().catch((err) => {
console.error("Failed to fetch dashboard:", err.message || err);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment