A firmware bug turned a 128-bit seed into a ~2³⁴ lookup. We swept that keyspace, recovered 2,145 affected wallets, and measured the drain block by block.
Built by @LLFourn on X · llfourn@frostsnap.com on nostr · prose written by Claude
Coldcard Mk3's random number generator collapsed to pad = UID ⊕ SysTick, which made the seeds it produced enumerable. We swept that keyspace — 2²⁵ pad values × 512 key-press counts, seeds as the device generated them, with no BIP39 passphrase and no added dice entropy — against every funded Bitcoin address, and found 2,145 wallets. They held 2,045 ₿ in the block before the first sweep. It was emptied to effectively nothing.
2,045 ₿→0
held on the vulnerable fleet the day the attack opened (~$131M at the time) — swept to near-zero in days · the mass theft took ~340 ₿ in its first 20 min and ~700 ₿ inside the hour
Of the 2,054 ₿ that left, a hand-built classifier predicts 1,555 ₿ taken and 499 ₿ recovered by owners — a speculative estimate. Change one assumption about the quiet spends and it moves between 1,324 and 2,043 ₿ — 64 to 99% of everything that left.
The holders — how much each victim held
The fleet is whale-skewed: the mean is dragged up by a handful of large wallets while the typical (median) victim held under half a coin. Two views — each wallet's all-time peak, and the snapshot the moment the attack opened.
All-time peak, per wallet
Ever-funded wallets
2,145
Largest ever
106.7 ₿
Mean holder
2.04 ₿
Median holder
0.35 ₿
Peak pool
~2,525 ₿ · ~$280M
At the attack · 30 Jul, pre-drain
Still holding
1,329
Largest
106.7 ₿
Mean holder
1.54 ₿
Median holder
0.28 ₿
Pool at attack
2,045 ₿
The fleet, the peak & the value
How much was ever at stake?
pool ₿ pool $ (value) hardware release
Confirmed balance across all recovered wallets since the vulnerable firmware. The coin count plateaued near 2,516 ₿ through 2024 — Feb and Nov sit within half a coin of each other, so no single date is the maximum, but the dollar value peaked at ~$280M in Oct 2025 — when BTC hit its price high while the fleet still held ~2,215 ₿. Safer reseeded hardware (Mk4, Q, Mk5) shipped throughout, yet the vulnerable Mk3 pool kept holding.
Vulnerable seeds funded
≥ 2,145
Peak coin count
~2,525 ₿ · Nov 2024
Peak dollar value
$280M · Oct 2025
Survival to the attack
How many seeds still held funds when the attack hit?
wallets funded hardware release
Wallets holding a positive balance, on the same timeline as above. Adoption climbed to 1,392 funded wallets by 2024 and sat near 1,329 right up to the attack — owners were not leaving — then collapsed in three days.
Peak funded wallets
1,392
Still holding at attack
1,329
The drain, block by block
How fast did it leave — and how many wallets?
pool ₿ remaining >50 spends ≤50 spends disclosure
Bitcoin remaining in the fleet through the attack — the pool fell from ~2,045 ₿ to near zero by 02 Aug. Bars show how many separate spends came out of tracked wallets each block, one unit per transaction, so a wallet drained coin-by-coin appears many times.
That repetition is the point: an owner rescuing their own funds sweeps everything in one transaction, while the drain ran 3.2 spends for every wallet it emptied — and 29× in the opening blocks (960,185: 403 spends, 14 wallets emptied).
The busiest block ran 962 spends and emptied 162 wallets. Red marks blocks with more than 50 spends, green 50 or fewer. Across the window 1,344 wallets went to zero.
The theft opened at block 960,183 · 29 Jul 21:10 EDT; the first public warning and Coinkite’s admission came ~19–22 h later — by which point 924 ₿, 45% of the opening pool, had left, across 91 wallets emptied.
Key presses: every press reshuffles the Mk3’s keypad and advances its random number generator, so the count of presses before the seed was drawn is half of what identifies a wallet in the collapsed keyspace. It is not visible on the chain — we know it only because we reconstructed the seeds. The bottom line falls through the opening wave, then climbs about tenfold, from 14 to 137, before falling back. That is what a search working outward from the cheapest coordinates would look like. Each point is the mean over the 50 wallets swept most recently at that block. All three panes share the x axis above, so a spike sits under its own point on the pool line. Net outflow is what left the pool — coins landing back on another recovered wallet are not counted. Touched is not emptied: a wallet partly spent in a block is touched, so touched is always the larger of the two.
Three published accounts of the theft — and where ours sits
Three published totals over different populations and windows — not a ranking. Galaxy publishes no address list, so no set relation is known between Galaxy and coldcardwatch, nor between Galaxy and ours. One relation IS measured: the theft our own evidence proves sits entirely inside coldcardwatch’s set.
Our 1,555 ₿, taken apart. The classifier divides the window's net outflow — 2,054 ₿, up 1.7 ₿ on the 2,053 ₿ August figure after our address index was rescanned deeper — over the same blocks as the drain above.
105
inferred · 1,450 ₿
predicted recovered · 499 ₿
Predicted taken · 1,555 ₿ is the red and ember bands together — the headline above, not a rival to it. Only the red part is evidence:
Evidenced · 105 ₿ — spends of several distinct seeds at once, which one key holder cannot make. Proves shared control, not identity.Inferred · 1,450 ₿ — the rest of the taken band, and a guess. Mostly burst-constant fee rates (30, 50, 201 sat/vB) and spends landing inside a burst.Predicted recovered · 499 ₿ — a guess, and mostly an assumption: outside a burst, cheap, nothing else fired. Not evidence of an owner.coldcardwatch · 1,403 ₿ verified — their own published rows, cumulative by block: 4,925 addresses they verify as drained, each carrying the value taken from it. Their full total is 1,405 ₿ — the last 2.46 ₿ lands after this window closes.Galaxy · 1,789 ₿ — their estimate, from direct victim contact. The likeliest reason it sits above ours: victim reports can reach losses from wallets no enumeration has found, including any outside the keyspace we swept. We cannot check that — they publish no address list. Flat because they publish no address list, so it cannot be resolved block by block.
The two lines reach nearly the same height by different routes, and neither is a check on the other. Their cyan line verifies faster than our classifier attributes through the opening blocks; our taken band crosses above it later, carrying 1,450 ₿ it only infers.
Where we actually agree, joined address-by-address: 1,205 ₿ of our outflow sits on addresses they verify. That is the overlap — not either total.
Set against coldcardwatch. They verified 1,405.00526537 ₿ over their own published address list. We find 105.06395353 ₿ more, in a final shared-control sweep their set does not contain — spends of several distinct seeds at once, which only the seed→address map can see. All 21 are assumed to be the attacker’s, so the two accounts together come to 1,510.06921890 ₿. That is a count of a different set of coins from our 1,555 ₿, not a rival estimate of it: 200 ₿ of their total sits on 592 addresses our sweep never found, and their list stops at the addresses they could verify while ours covers every wallet we recovered.
How we get to 1,555 ₿.
We swept the collapsed keyspace and recovered 2,145 wallets. Every figure here is measured on those wallets and no others.
2,054 ₿ left them inside the window. That is the whole quantity being split — what moved, before anyone decides who moved it.
105 ₿ of it is evidence rather than inference: 21 transactions that each spend several distinct seeds at once. Four of them spend 88, 43, 100 and 68 distinct recovered seeds in a single transaction — nobody holding one seed can sign that.
1,450 ₿ is a guess from conduct — burst-constant fee rates (30, 50, 201 sat/vB) and spends landing inside a burst. It is the larger half of the answer and the weaker half of the evidence.
When none of those signals fire, the tree still has to decide. It assumes a spend inside a burst was theft, and a quiet spend outside one was an owner moving their own coins. Those two assumptions decide more of the answer than any measurement does: assume the quiet spends were theft too and the total is 2,043 ₿; assume the in-burst ones were owners and it is 1,324 ₿.
The 105 ₿ is a floor, not an estimate. Those same transactions also spend coins from addresses we cannot tie to any Mk3 wallet, and we count none of them — so whoever did this moved more than 105 ₿, and we cannot say how much more.
One wallet, in full
SAD STORY: Rescued a multisig onto a vulnerable seed 😱
Someone was worried about a 2-of-3 multisig — plausibly one with Mk3s among its signers — so they moved it into their single-sig wallet. It looks like they did not realise that seed had been generated on an Mk3 too. On 01 Aug at 10:52 a spend carrying six seeds had already taken 0.00607993 ₿ out of it — one coin of the 9 in the wallet, 7.9% of a 0.07669071 ₿ balance, small enough to miss. Ten hours later 6.80677623 ₿arrived from the multisig. Eighteen hours after that everything was gone, across 26 new addresses, where 6.87512000 ₿ still sits untouched a month on.
The attacker was working from a stale list of coins. To test billions of candidate seeds you need the set of funded addresses in memory — you cannot ask a node about each one. So the sweep ran against a snapshot, and the snapshot had an edge. Across all six wallets it touched, every coin it took was funded at or before block 949,432, and it took nothing funded after. Wallet 1532’s next coin arrived at block 952,928. Everything from there on was invisible to it.
Which is why the wallet still looked fine. The sweep took one old coin and walked past 0.07061078 ₿ in eight newer ones — 92% of everything the wallet held — not because they were too small, but because they postdated its snapshot. Everything the owner had received recently was still sitting there. The only thing missing was a coin from months earlier, worth six thousandths of a bitcoin.
So they consolidated into it — plausibly to get clear of the attack. Four 2-of-3 multisig outputs, no change, minimum fee. And the 6.80677623 ₿ was invisible too, for the same reason: it arrived on 01 Aug, long past the edge of the list the sweep was reading.
Then all of it left, in a shape this wallet had never made.52 spends since 2021, never more than 2 outputs; on 02 Aug, 16 and then 11, taking the whole run 540–548 — every coin the morning spend had left. It went in four spends, each sending part out and returning the rest to the same vulnerable wallet on its change chain: 0.234 out and 6.57 back, then 3.61 out and 2.96 back, then 3.03 out and dust back. The wallet was emptied in stages, with the remainder on compromised keys throughout — and the 26 addresses that received are the only part outside the recovered seeds.
SPECULATION. The cruelty of it is that the stale snapshot cut both ways. It is why the wallet looked untouched — every recent coin still there, one old one quietly gone — and it is why nobody stopped the 6.8 ₿ going in. Then somebody looked at the chain as it actually was, rather than as the snapshot remembered it, and took the whole run 540 to 548 by hand. The eighteen-hour gap is a machine reading old data and a person reading current data. If so, the owner moved their savings out of multisig to escape the Coldcard attack and put them in a Coldcard wallet the attack had already reached.
The alternative is that the owner did it themselves: unlucky in the morning, migrating to a new wallet by evening, fanning out twenty-six ways for no reason they ever had before. Possible. But nobody funds a wallet they believe is compromised and then empties it eighteen hours later, and nobody rescuing money splits it twenty-six ways to do it. Neither reading identifies anyone. The one measured fact is the signature at 10:52: six seeds, one transaction, one signer holding all six.
Where the wallets actually were
What the sweep found, and how deep it had to look
Three separate measurements over three different populations, kept apart on purpose: a wallet is not an address, and an (account, wallet) pair is neither.
What the sweep cannot see. The swept keyspace is finite and its edges are measured rather than assumed away.
Key-press counts 513-768 were probed at address index 0 across all six script/branch cells and returned 0 wallets — but only there: index 1 in that tail was never run, and above 768 is untested at any index.
That gap is not theoretical: over the main domain, detection ran at indexes 0 and 1, and 41 of the wallets we found were reachable only at index 1 — so an index-0 null over the tail does not carry to it.
A wallet first funded at index 2 or beyond is outside detection altogether, however much it holds. Rerunning 513-768 at index 1 is the next probe.
What detection found
population: distinct wallets
index 0
2,104
found by the original sweep
index 1
41
reachable only after widening; index 0 was already exhausted
2,161 raw candidates minus 16 prefix false positives leaves 2,145. Counted once per wallet: the 5,409 position rows include the same wallet found at several positions, which is why positional totals cannot produce this split.
How deep the addresses go
population: source addresses
0
3,139
1
2,253
2-9
9,550
10-99
16,714
100-233
1,545
234+
756
deepest observed: 548
Over 33,957 source ADDRESSES, not wallets — one wallet spends from many. Their Wave-1 deepest is 234; ours over the same wave is 234.
Sibling accounts in use
population: (account, wallet) pairs
The account index is a hardened derivation step, so a sibling account cannot be reached from a published account xpub — only from the seed. Recovering the RNG coordinates made these reachable for the first time.
account 1
58
of 2,145 derived · 2.7% · peak 54.47 ₿
account 2
19
of 2,145 derived · 0.9% · peak 12.11 ₿
account 3
15
of 2,145 derived · 0.7% · peak 5.26 ₿
account 4
8
of 64 derived · 12.5% · peak 8.84 ₿
account 5
5
of 23 derived · 21.7% · peak 0.76 ₿
account 6
3
of 15 derived · 20.0% · peak 0.73 ₿
account 7
2
of 8 derived · 25.0% · peak 0.01 ₿
64 wallets used at least one sibling account, across 110 (account, wallet) pairs — the pairs overlap, so these bars are not a partition and do not sum to a wallet count.
Discovery is by BIP44 gap limit: a wallet's chain ends after 3 consecutive unused accounts. It stopped because the next came back derived and entirely unused — account 8 (5 wallets), account 9 (3 wallets), account 10 (2 wallets) — and a wallet that skipped 3 or more in a row would not have been reached at all.
CONJECTURE: deeper cohorts use siblings at a higher rate (12-25% against 2.7% at account 1) — a selection effect would look like this, since a wallet only reaches account 5 by having used one before it. Denominators of 8-64 wallets, so a lead, not a finding.
Reading across. These panels count different things, so nothing here is a joint distribution — no wallet is counted in both at once, and the cross-tab of DETECTION POSITION against deepest spent index stays unmeasured.
The wallet-level join IS built: per wallet, the deepest index it ever spent from — 735 never past index 1, 692 out to 9, 682 into the tens, 36 past 100, deepest 548.
A shallow sweep recovers deep-usage wallets BY CONSTRUCTION: the search matches a shallow address 0 or 1, and every later one follows from the same seed. What it MISSES is the open question — indexes at or above 2 were never swept, so this is a floor on wallets, not a count of them.
Against other published accounts
What everyone else found, and what our data says under it
Reconstructed 328 seeds from the RNG collapse and published the 153 addresses it could not explain with their sha256 — which is what made this checkable at all.
Two reconstructions of similar reach, arrived at differently. Of the wave's 1,195 source addresses they matched 1,042 covering 949.7 ₿; we reach 1,005 and 922 ₿ over the same blocks. Their list is unpublished, so containment either way is UNTESTED.
Their 153 unresolved addresses stay unresolved by us: searched against all 8,700 descriptors, accounts 0-10, both keychains, below index 2,000, and matched none. That corroborates the claim without explaining it. Still open, all of them: unswept keyspace, indexes at or above 2,000, a wallet skipping three or more consecutive accounts, another derivation path, and a misattribution on their side.
They report a deepest observed index of 234. Ours over the same wave, across 1,005 addresses, is 234 — the same number, reached independently, with 0 above it. All-time: 548 over 33,957 addresses, 750 of them past 234 — a wider scope than their figure describes, not a correction to it.
They see accounts 0 through 4, as far as chain observation reaches. We derive siblings from recovered seeds and find usage through account 7 — 58/19/15/8/5/3/2 wallets in accounts 1-7, with 8-10 derived and unused. The account index is hardened, so only the seed reaches them.
Better placed for: The Wave-1 seeds, and an address list we cannot join against.
Forward-traces sweep proceeds downstream to collector addresses and corroborates against victim reports — a method our wallet-only graph cannot run.
We joined their 4,925 published addresses against our sweep rather than subtracting totals. Every ₿ of the theft we can fingerprint sits on an address they verify as drained, and all 1,005 of our Wave-1 source addresses are in their set.
Of the outflow we can neither fingerprint as theft nor tie to shared control, 207 ₿ (22%) is in their set.
Our shared-control bucket overlaps by 0.019 ₿ across 5 addresses — small, but not zero.
592 of their addresses are outside anything our sweep reached, accounting for 200 ₿ drained by their rows — a coverage gap no total could have shown us.
Better placed for: Where the coins went. Theirs is the better figure for theft, ours for what the affected fleet held.
Wave-by-wave attribution from direct victim contact, published as a running tally — so every figure of theirs needs its date.
Their Wave-1 block range, 960,183 to 960,191, is exactly the window our own constant defines, so metric and window align. Population does NOT: they publish no address list, so every ratio below is untested.
Both sides resolve Wave 1 to four destinations; three differ. Following our 4 forward settles it: they spend into 3 of their 4 collectors across 3 transactions inside Wave 1. All 4 of their per-address figures are what those addresses HOLD, not what they received — the shared one forwarded 562.03 ₿ on and kept the 32.45 ₿ they list. That gap is the hop a wallet-only method cannot follow.
Derivation split: theirs 1,183 BIP-84 / 7 BIP-49 / 6 BIP-44, ours 996 / 3 / 6 over the same blocks. A HYPOTHESIS, not a measurement: IF our addresses are a subset of theirs, our reach is uneven across derivation kinds rather than uniform. Testing it needs their list.
Against their 1,778.84 ₿ total: we positively fingerprint 998 ₿ of 2,053 ₿ net outflow over 2,145 wallets in a 900-block window. That measures our reach, not a share of their total — our population and window are narrower.
Their 'more than 8,600 addresses' is a LOWER bound; ours over that same 900-block window is 10,953 distinct source addresses, owners included. No bound follows from combining them, and the sets have never been joined. SPECULATION worth testing: the excess may be owner rescues.
Their totals are dated snapshots, not competing claims: 1,367 ₿ on 1 Aug, 1,596 on 3 Aug, 1,719 on 7 Aug, 1,778.84 on 14 Aug, 1,789.28 on 24 Aug.
Better placed for: Victim contact, wave attribution beyond the first, and where the proceeds went afterwards.
Entity clustering over the sweep: how fast value accumulated, and in what order.
Their 500 is a WALLET count; Galaxy's 1,196 for the same wave counts ADDRESSES. Ours: 326 wallets across 1,005 addresses — not like-for-like, since theirs is entity-clustered and the populations were never joined.
Their “$30M inside ten minutes” we cannot resolve: our clock is confirmation time, and our first two Wave-1 blocks sit 22.5 minutes apart.
Their high-value-first ordering does not appear in our cohort: across 326 wallets the rank correlation between size and block is essentially zero (-0.0226, and -0.0042 without the largest 10). No MONOTONIC ordering in our recovered cohort; pre-registered, and descriptive rather than a significance test. CONJECTURE: per-block median value peaks MID-WAVE rather than at the open, which would fit an initial small-wallet probe before the value-bearing batches, or simply a different batching policy early on.
Better placed for: Attacker-side clustering, exchange exposure and geography.
The firmware root cause reasoned from the code: which RNG fallback the build selected, what the pad is made of, and how much of the keyspace that leaves reachable. Their model of the keyspace is the part our sweep can test.
Their pad = UID ⊕ SysTick model predicts a narrow reachable band inside the 33.5M we searched. We swept the flat range [0, 2²⁵) rather than trusting the structure, so nothing in our result rests on it — and every pad we recovered lands inside their band anyway: 2,113 distinct pads across 2,145 wallets, all between 983,753 and 4,978,069.
Their pad high word runs 15–75. Ours runs 15 to 75 across 61 distinct values, with none outside. Both ends exact.
Their RTC sub-second is structurally zero on Mk2 and Mk3 because the oscillator never starts. Ours agrees: ssr = 0 in every wallet we reconstructed, which is why our sweep fixes that factor rather than searching it.
Better placed for: The device and the firmware. Everything upstream of the keyspace is theirs.
The firmware root cause: the RNG fallback, the build guard that selected it, and the arithmetic bounding the seed count.
Where our sweep speaks to it: the RTC subsecond field, one of the 256-way factors inside their broad candidate bound, was zero in every wallet we reconstructed. That makes the wide bound loose, not wrong.
Better placed for: Everything about the device.
Sources
Everything this page compares itself against
Figures published by others are cross-checks, never inputs — the classifier is frozen independently of them. Third-party totals move as their investigations continue; the dates below are when we last read them.
1,405.07 ₿ across 4,925 verified addresses. Methodology — the fee-convergence and fresh-collector fingerprints our classifier reimplements. Read 12 Aug 2026.
A running tally, so every figure needs its date: 1,367 ₿ (1 Aug), 1,596 ₿ (3 Aug), 1,719 ₿ (7 Aug), 1,778.84 ₿ (14 Aug), 1,789.28 ₿ (24 Aug). This page previously dated the 1,719 figure to 4 Aug via The Block and called it the latest; it is the 7 Aug number and has been superseded twice. Primary posts read 25 Aug 2026.
328 reconstructed seeds covering 1,042 of Wave 1's 1,195 source addresses, with the fee rule, transaction construction and RNG-state statistics tested above. Publishes the 153 unresolved addresses as a CSV, which we verified by its stated sha256 and joined against our own set. Read 14 Aug 2026.