Why look at this now

Threshold Network paused its tBTC bridge on October 30, 2026 so operators could patch a critical Linux kernel flaw. During the pause nobody could mint tBTC or redeem it for BTC. Threshold reported about 4,320 BTC in bridge wallets against about 4,185 tBTC in circulation. Aave froze its tBTC reserve on Ethereum Core the next day.

A circulating-supply number like "about 4,185" is easy to check yourself. The token is an ERC-20, so its supply is one eth_call away. Here's a script that does it with no dependencies.

The script

Node 18 or later has fetch built in, so this runs as is. Save it as wrapped-supply.mjs.

// Read the on-chain supply of wrapped-BTC tokens on Ethereum with plain JSON-RPC (Node 18+, no dependencies). // Usage: node wrapped-supply.mjs [rpcUrl] const RPC = process.argv[2] || 'https://ethereum-rpc.publicnode.com'; const TOKENS = { tBTC: '0x18084fbA666a33d37592fA2633fD49a74DD93a88', WBTC: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', cbBTC: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', }; const SEL = { symbol: '0x95d89b41', decimals: '0x313ce567', totalSupply: '0x18160ddd' }; async function call(to, data) { const res = await fetch(RPC, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_call', params: [{ to, data }, 'latest'] }), }); const j = await res.json(); if (j.error) throw new Error(j.error.message); return j.result; } function decodeString(hex) { const b = Buffer.from(hex.slice(2), 'hex'); const len = Number(BigInt('0x' + b.subarray(32, 64).toString('hex'))); return b.subarray(64, 64 + len).toString('utf8'); } function format(raw, decimals) { const s = raw.toString().padStart(decimals + 1, '0'); return `${s.slice(0, -decimals)}.${s.slice(-decimals, -decimals + 2)}`; } for (const [name, addr] of Object.entries(TOKENS)) { const [sym, dec, sup] = await Promise.all([call(addr, SEL.symbol), call(addr, SEL.decimals), call(addr, SEL.totalSupply)]); const decimals = Number(BigInt(dec)); console.log(`${name.padEnd(6)} symbol=${decodeString(sym).padEnd(6)} decimals=${String(decimals).padStart(2)} supply=${format(BigInt(sup), decimals)}`); } 

The three hex strings are function selectors, the first four bytes of the Keccak-256 hash of symbol(), decimals() and totalSupply(). symbol() returns an ABI-encoded string: a 32-byte offset, a 32-byte length, then the bytes, which is what decodeString unpacks. The supply comes back as a raw integer, so the script divides by 10^decimals with string slicing to stay exact. Floating point would round a number this size.

Output

Run on November 10, 2026 against the public node:

tBTC symbol=tBTC decimals=18 supply=4185.18 WBTC symbol=WBTC decimals= 8 supply=116132.18 cbBTC symbol=cbBTC decimals= 8 supply=45609.65 

tBTC's 4,185.18 matches the circulating figure Threshold gave. Note the decimals: tBTC uses 18 like ETH, while WBTC and cbBTC use 8 like bitcoin itself. Code that assumes one value for "BTC-like" tokens will be off by ten orders of magnitude on one of them.

What the number doesn't tell you

totalSupply() tells you how many tokens exist on this chain. It says nothing about:

  • The BTC behind them. That sits on the Bitcoin chain, in wallets the bridge controls. Checking it means reading those Bitcoin addresses, which is a separate job.
  • Supply on other chains. Wrapped tokens are often bridged again to L2s. Their supply there is counted by different contracts.
  • Whether you can redeem. During the pause the supply was unchanged and redemption was closed. A frozen exit doesn't show up in any ERC-20 view function.

The last point is the one the pause made concrete. In Aave's governance thread, one participant reported tBTC trading within 0.2% of BTC after October 31, while a trader noted that pool liquidity for exiting got expensive after the first million or two dollars. Supply and peg both looked fine. The exit was the constraint.

Taking it further

Two easy extensions: pass a block number instead of 'latest' (an archive node is needed for old blocks) to chart supply over the pause, or add the paused() selector for tokens that expose one. tBTC's minting and redemption are controlled by the bridge contracts rather than the token, so for it you'd watch the bridge's events instead.

Sources: Cointelegraph, "tBTC bridge partially resumes as $120M Aave reserve remains frozen" (Nov 9, 2026); Aave governance forum, "tBTC mint/redeem pause: Aave's response" (Oct 31, 2026, and replies).