spark.exposed

Mainnet incident report · incomplete recovery

The 100,000-sat “escape hatch,” attempted for real

A mobile wallet displayed approximately 100,000 sats after ordinary use. Recovering it without later Operator cooperation expanded into a state-export problem, a 900+-transaction full graph, economic triage, separate fee funding, ordered package relay, weeks of resumable chain monitoring, relative timelocks, and final sweeps that still have not occurred.

Measured result: More than two dozen of roughly three dozen selected packages have confirmed. About ten packages and all five final sweeps remain.

What this page demonstrates: The protocol path can advance without later Operator signatures once complete state exists. The consumer capability still does not exist inside the reviewed mobile wallet.

~100,000
sats displayed
30+
current leaves
900+
full-plan transactions
0
completed sweeps

The substantive conclusion

This was not a withdrawal. It was an incident-response operation.

An exit path that begins by extracting hidden protocol state, requires custom developer tooling and separate on-chain bitcoin, turns routine wallet use into hundreds of transactions, advances through confirmation-dependent packages, survives an interrupted multi-week execution, waits again for relative locks, and ends with a separate sweep is not meaningfully delivered by a seed-backup screen.

For an ordinary user, the practical question is not whether an expert can eventually satisfy the script. It is whether the wallet itself preserves and operates the escape path when every service the user normally relies on is unavailable or refusing service. In the reviewed product, it did not.

The starting point

One displayed balance became more than 30 independently recoverable fragments

Visible or measured itemResultWhy it matters
Funding historyOrdinary mobile-wallet receiptsRoutine use produced the state under test; exact receipt values and timing are withheld.
Displayed balanceapproximately 100,000 satsThe UI presented one number; the exit did not contain one output.
Current leaves30+Each leaf carried its own ancestry, fees, ordering, and sweep.
Saved tree nodes400+This current graph was not recoverable from the mnemonic alone.
Full plan450+ packages / 900+ transactionsThe nominal balance concealed the actual execution workload.

Economic triage

“Recover your balance” first meant choosing which owned sats to leave behind

At the 1.25 sat/vB planning rate, the five largest leaves held 82,000 sats. More than 30 other leaves held 18,000 sats and were excluded. The selected plan projected approximately 13,500 sats of separate CPFP spending and 700 sats deducted by final sweeps. Counting abandoned leaves and both fee classes, projected net economic recovery was approximately 68,000 sats—68% of the displayed balance—before the fee for creating the funding UTXO.

Stress rate450+ package fees30+ sweep feesCombinedLeaves still economical
5 sat/vB≈697,000 sats≈20,000 sats≈717,000 sats3 of 30+, holding ≈66,000 sats
10 sat/vB≈1,394,000 sats≈40,000 sats≈1,434,000 sats1 of 30+, holding ≈33,000 sats
100 sat/vB≈13,938,000 sats≈400,000 sats≈14,338,000 sats0 of 30+, holding ≈0 sats

These stress rows price this measured graph; they are not fee forecasts or universal Spark-wallet costs.

Complete workflow

Twelve stages that the word “non-custodial” leaves off the screen

Each stage below records the actual task, its failure boundary, and the product responsibility a credible mobile implementation would have to assume.

01

Recovery stage

Preserve state that the seed cannot reproduce

What the attempt required

The account exposed more than 30 current leaves and more than 400 total nodes. The consumer wallet exposed no recovery-bundle export, so the bundle had to be obtained by authenticating directly to a Spark Operator with the seed-derived Spark identity while services were reachable.

How it fails

If every Operator is already unavailable and no complete current bundle exists, the seed can derive keys but cannot rediscover the current leaf and ancestor graph.

What a mobile wallet must own

Refresh, validate, encrypt, version, and back up the complete exit graph after every balance-changing operation—not merely store a mnemonic.

02

Recovery stage

Validate every ancestor before declaring the backup usable

What the attempt required

Package construction depends on a closed root-to-leaf chain for every leaf. The tooling must decode protobuf tree nodes, resolve missing parents, reject open ancestry, bind the bundle to the correct network and account, and verify that the seed matches the saved state.

How it fails

A syntactically valid but incomplete backup can fail only when the emergency begins, when the missing state may no longer be retrievable.

What a mobile wallet must own

Continuously run integrity checks and surface a recovery-readiness state that is distinct from ordinary seed-backup completion.

03

Recovery stage

Turn a balance into hundreds of Bitcoin transactions

What the attempt required

The approximately 100,000-sat balance expanded into more than 30 leaf chains containing more than 450 zero-fee Spark parent or refund transactions, the same order of CPFP children, more than 30 final sweeps, and one fee-funding transaction: more than 900 transactions in the full no-fan-out plan.

How it fails

Displayed balance says almost nothing about exit workload. Tree history and leaf depth determine how many ordered confirmations and fees are required.

What a mobile wallet must own

Decode the graph, deduplicate shared ancestors, derive per-leaf chains, and explain that the balance is not a single recoverable output.

04

Recovery stage

Decide which owned sats are rational to abandon

What the attempt required

At 1.25 sat/vB, only the five largest leaves were selected. They held about 82,000 sats; more than 30 other leaves held about 18,000 sats. The selected plan still required roughly three dozen packages, five sweeps, and an external funding transaction.

How it fails

Forcing the full graph at the planning rate required roughly 175,428 sats of external fee funding—more than the Spark balance itself. Higher fee-rate stress tests made most or all leaves uneconomical.

What a mobile wallet must own

Perform per-leaf economic triage, disclose abandoned value and external fees separately, and obtain informed approval for a partial recovery.

05

Recovery stage

Find ordinary on-chain bitcoin before Spark value is spendable

What the attempt required

The zero-fee exit parents rely on ephemeral anchors. A separate 15,000-sat confirmed L1 UTXO was supplied to construct and sign CPFP children. Those sats were outside the 99,999-sat Spark balance.

How it fails

The emergency path cannot bootstrap its network fees from the trapped Spark outputs. A user with only the advertised Spark balance needs another wallet, exchange, sponsor, or service.

What a mobile wallet must own

Derive and monitor a fee-funding address, source an adequate UTXO, protect its signing key, handle underfunding, and wait for confirmation before starting.

06

Recovery stage

Construct and sign paired transaction packages

What the attempt required

Every step consists of a pre-signed zero-fee Spark transaction plus a newly constructed fee-paying child. The child spends both the parent's anchor and a rolling fee UTXO, then returns fee change for a later step.

How it fails

A generic transaction broadcaster is insufficient. Parent and child must be internally consistent, correctly signed, economically funded, and submitted as a package.

What a mobile wallet must own

Embed a Bitcoin signer, PSBT handling, anchor detection, package construction, secure seed access, and a package-relay-capable backend or node connection.

07

Recovery stage

Advance one valid dependency at a time

What the attempt required

The five selected chains required roughly three dozen ordered packages. Each round reconstructed chain state, skipped confirmed parents, signed the current child, submitted the package, and waited for confirmation before a dependent step could advance.

How it fails

Submitting a deep chain as though it were an ordinary transaction batch produces TRUC descendant-policy and missing-input failures. The process is a state machine, not a fire-and-forget upload.

What a mobile wallet must own

Persist per-leaf progress, recognize dependency errors, poll confirmations, recover from eviction or rate limits, and resume idempotently after process death or upgrade.

08

Recovery stage

Keep the recovery alive across blocks, outages, and provider failures

What the attempt required

By the earlier checkpoint, more than two dozen of roughly three dozen selected packages had confirmed. The recovery was later reconstructed from raw saved artifacts and live chain queries; no transaction remained in the mempool.

How it fails

Old signed CPFP children were stale because their fee inputs referred to prior change outputs. One SDK status path was fixed to a single public Esplora host that was unreachable from the recovery environment, requiring an alternate chain endpoint for reconstruction.

What a mobile wallet must own

Use redundant chain services, distinguish absent, mempool, confirmed, replaced, and spent states, rebuild stale fee children safely, and never depend on volatile in-memory progress.

09

Recovery stage

Understand that elapsed calendar time may not advance the next lock

What the attempt required

At the August checkpoint, all five next parent packages were mature. But the five following refunds carry 300–600-block relative locks whose clocks begin only after those next parents confirm.

How it fails

Leaving the operation untouched for weeks did not mature a refund whose immediately preceding output was still unconfirmed. Resuming now still creates another roughly multi-day block wait before final refunds can be mined.

What a mobile wallet must own

Calculate each relative lock from the actual confirmation height of its spent output, schedule durable reminders, tolerate reorgs, and resume after days or weeks without assuming wall-clock time completed the protocol wait.

10

Recovery stage

Reprice the unfinished work without spending the fee reserve twice

What the attempt required

Just under 5,000 sats of fee funding remained on chain. A fresh dry run reconstructed and signed about ten remaining packages at 1.25 sat/vB. Their CPFP fees were about 3,500 sats, leaving about 1,500 sats.

How it fails

Replaying the original saved children would attempt to spend stale change. Raising the fee rate too far can consume the reserve and strand later packages.

What a mobile wallet must own

Recalculate the complete remaining fee chain before every resume, preserve a spendable change floor, and require explicit approval if changed conditions alter net recovery.

11

Recovery stage

Preserve and verify a final destination independently

What the attempt required

The saved bundle and package files contain no final sweep destination. A new mainnet address controlled by the user must be supplied and checksum-validated before any final sweep is signed.

How it fails

Exit packages can progress while the final destination remains unknown. Guessing or reusing an address would turn a recovery procedure into an irreversible loss risk.

What a mobile wallet must own

Persist destination intent safely, verify network and script support, show the exact final outputs and fees, and require irreversible-action confirmation.

12

Recovery stage

Broadcast refunds, construct sweeps, and prove completion

What the attempt required

After each refund matures, it still needs a package submission and confirmation. Only then can the wallet construct, sign, broadcast, and confirm five ordinary Taproot-input sweeps to the chosen address.

How it fails

A confirmed exit-chain ancestor is not recovered bitcoin at the destination. At the August checkpoint, zero final sweeps had completed.

What a mobile wallet must own

Continue through refund maturity, sweep construction, destination confirmation, partial-failure reporting, and a durable final receipt.

Live checkpoint · August 24, 2026

The attempt can resume. It is still nowhere near a completed mobile withdrawal.

24+ / ~36selected parents confirmed
≈5,000 satsconfirmed fee reserve left
≈3,500 satsfresh remaining CPFP cost
300–600blocks still required after next parents confirm

Each selected leaf has one mature parent package and one refund package left. A refund's relative lock begins from confirmation of the parent it spends, so the final 300–600-block wait has not started for those refunds. Old CPFP children cannot simply be replayed because they spend prior fee-funding change; they must be reconstructed from the live UTXO set.

The dry run reconstructed and signed every remaining package without broadcasting. It also found that no sweep destination was preserved in the artifacts. The operator must provide and verify a fresh mainnet destination before the final irreversible step.

Mobile implementation verdict

A recovery daemon, Bitcoin signer, backup system, and fee wallet—not a button

This is technically implementable in software, including software that can run on a phone. But it is not credibly represented by a seed-backup screen or a Withdraw button. It requires a continuously maintained recovery system spanning normal operation, encrypted backup, external L1 funding, Bitcoin transaction signing, package relay, chain monitoring, fee management, multi-day resumability, timelock scheduling, and final sweeping.

For an ordinary user of the reviewed mobile wallets, cryptographic signing authority does not produce independently usable availability. During an all-Operator outage or refusal, control remains operationally dependent on infrastructure and expert tooling outside the app. In that practical availability sense, the experience is custodial even where the narrow key-sharing design is described as non-custodial.

The product test: A wallet should not receive credit for mobile unilateral exit until a normal user can export or restore current state, source fees, complete every package and wait, survive restarts and provider failures, sweep to a verified address, and demonstrate the destination confirmations without developer intervention.

Prediction boundary: The record cannot prove that no team will ever ship this machinery. It does show that the missing work is a long-lived recovery subsystem, not a small SDK call, and that no complete in-app operatorless flow was found in the eight reviewed consumer-wallet surfaces.

Record boundary

What is public, and what is deliberately withheld

One mainnet consumer Spark wallet funded through ordinary mobile-wallet receipts. Counts and costs describe this wallet graph, not every Spark wallet.

Public quantities are rounded or expressed as lower bounds where precision could enable correlation. The underlying calculations used exact private artifacts.

The public record intentionally omits the seed, account identity, wallet-provider provenance, recovery bundle, funding address, destination, leaf identifiers, transaction identifiers, block heights, precise transaction timestamps, and raw transactions.

The case publishes aggregate measurements only. Provider provenance, transaction identifiers, block heights, timestamps, addresses, raw transaction material, and secret data are withheld because they are unnecessary to evaluate the workflow and could enable correlation.

1

Spark unilateral-exit manual

Official documentation

Open source
2

Breez 0.23 unilateral-exit guide

Pinned source documentation

Open source
3

BIP 68 relative lock-time specification

Bitcoin specification

Open source
4

Consumer-wallet exit implementation review

Reproducible audit manifest

Open source