Passed 4 of 7 tests
We check each wallet from its code to the app on your device, asking these questions in order:
We stopped asking questions after we encountered a failed answer.
The answer is "yes".
If the answer were "no", we would mark it as "Fake" and the following would apply:
The answer is "no". We marked it as "Fake".
We did not ask this question because we failed at a previous question.
If the answer were "no", we would mark it as "Fake" and the following would apply:
The bigger wallets often get imitated by scammers that abuse the reputation of the product by imitating its name, logo or both.
Imitating a competitor is a huge red flag and we urge you to not put any money into this product!
The answer is "yes".
If the answer were "no", we would mark it as "Not a wallet" and the following would apply:
The answer is "no". We marked it as "Not a wallet".
We did not ask this question because we failed at a previous question.
If the answer were "no", we would mark it as "Not a wallet" and the following would apply:
If it’s called “wallet” but is actually only a portfolio tracker, we don’t look any deeper, assuming it is not meant to control funds. What has no funds, can’t lose your coins. It might still leak your financial history!
If you can buy Bitcoins with this app but only into another wallet, it’s not a wallet itself.
The same goes for off-ramps that hold no balance at all: bitcoin (or gift cards) go in and fiat goes straight out to your bank. An off-ramp that keeps your fiat until you cash out is a wallet, just not for Bitcoin. One that keeps a bitcoin balance you can’t send out is ‘Can’t send or receive bitcoins’.
The answer is "yes".
If the answer were "no", we would mark it as "A wallet but not for Bitcoin" and the following would apply:
The answer is "no". We marked it as "A wallet but not for Bitcoin".
We did not ask this question because we failed at a previous question.
If the answer were "no", we would mark it as "A wallet but not for Bitcoin" and the following would apply:
At this point we only look into wallets that at least also support BTC.
A product that can send and receive other coins but only tracks Bitcoin addresses read-only is a wallet, just not for Bitcoin, so it gets this verdict. ‘Not a wallet’ is for products that hold no coins at all, such as pure portfolio trackers.
The same goes for off-ramps that credit your bitcoin to a fiat balance you later cash out: the app holds your money, but not as bitcoin.
The answer is "yes".
If the answer were "no", we would mark it as "Can't send or receive bitcoins" and the following would apply:
The answer is "no". We marked it as "Can't send or receive bitcoins".
We did not ask this question because we failed at a previous question.
If the answer were "no", we would mark it as "Can't send or receive bitcoins" and the following would apply:
If it is for holding BTC but you can’t actually send or receive them with this product then it doesn’t function like a wallet for BTC but you might still be using it to hold your bitcoins with the intention to convert back to fiat when you “cash out”.
All products in this category are custodial and thus funds are at the mercy of the provider.
The product cannot be independently verified. If the provider puts your funds at risk on purpose or by accident, you will probably not know about the issue before people start losing money. If the provider is more criminally inclined he might have collected all the backups of all the wallets, ready to be emptied at the press of a button. The product might have a formidable track record but out of distress or change in management turns out to be evil from some point on, with nobody outside ever knowing before it is too late. The answer is "yes".
If the answer were "no", we would mark it as "Custodial: The provider holds the keys" and the following would apply:
The answer is "no". We marked it as "Custodial: The provider holds the keys".
We did not ask this question because we failed at a previous question.
If the answer were "no", we would mark it as "Custodial: The provider holds the keys" and the following would apply:
A custodial service is a service where the funds are held by a third party like the provider. The custodial service can at any point steal all the funds of all the users at their discretion. Our investigations stop there.
Some services might claim their setup is super secure, that they don’t actually have access to the funds, or that the access is shared between multiple parties. For our evaluation of it being a wallet, these details are irrelevant. They might be a trustworthy Bitcoin bank and they might be a better fit for certain users than being your own bank but our investigation still stops there as we are only interested in wallets.
Products that claim to be non-custodial but feature custodial accounts without very clearly marking those as custodial are also considered “custodial” as a whole to avoid misguiding users that follow our assessment.
Balances on operator-dependent layers (Spark, Ark and similar) are judged by one question: “Can a user, with only what the app gives them and expecting help from nobody, actually force their money onto the Bitcoin blockchain?” Such a balance counts as self-custodial only for the part that the app itself shows the user could recover this way, calculated without network access at a fee rate the user chooses, and only once a recovery from that app has been demonstrated, because the app decides how the balance is split and therefore how much of it can come back. The rest of the balance is considered custodial.
We have to acknowledge that a huge majority of Bitcoiners are currently using custodial Bitcoin banks. If you do, please:
- Do your own research if the provider is trust-worthy!
- Check if you know at least enough about them so you can sue them when you have to!
- Check if the provider is under a jurisdiction that will allow them to release your funds when you need them?
- Check if the provider is taking security measures proportional to the amount of funds secured? If they have a million users and don’t use cold storage, that hot wallet is a million times more valuable for hackers to attack. A million times more effort will be taken by hackers to infiltrate their security systems.
The answer is "yes".
If the answer were "no", we would mark it as "No source for current release found" and the following would apply:
The answer is "no". We marked it as "No source for current release found".
We did not ask this question because we failed at a previous question.
If the answer were "no", we would mark it as "No source for current release found" and the following would apply:
A wallet that claims to not give the provider the means to steal the users’ funds might actually be lying. In the spirit of “Don’t trust - verify!” you don’t want to take the provider at his word, but trust that people hunting for fame and bug bounties could actually find flaws and back-doors in the wallet so the provider doesn’t dare to put these in.
Back-doors and flaws are frequently found in closed source products but some remain hidden for years. And even in open source security software there might be catastrophic flaws undiscovered for years.
An evil wallet provider would certainly prefer not to publish the code, as hiding it makes audits orders of magnitude harder.
For your security, you thus want the code to be available for review.
If the wallet provider doesn’t share up to date code, our analysis stops there as the wallet could steal your funds at any time, and there is no protection except the provider’s word.
“Up to date” strictly means that any instance of the product being updated without the source code being updated counts as closed source. This puts the burden on the provider to always first release the source code before releasing the product’s update. This paragraph is a clarification to our rules following a little poll.
We are not concerned about the license as long as it allows us to perform our analysis. For a security audit, it is not necessary that the provider allows others to use their code for a competing wallet. You should still prefer actual open source licenses as a competing wallet won’t use the code without giving it careful scrutiny.
The product cannot be independently verified. If the provider puts your funds at risk on purpose or by accident, you will probably not know about the issue before people start losing money. If the provider is more criminally inclined he might have collected all the backups of all the wallets, ready to be emptied at the press of a button. The product might have a formidable track record but out of distress or change in management turns out to be evil from some point on, with nobody outside ever knowing before it is too late.Distribution
Build Verifications
Check your file
App Description
Blitz Wallet is an open-source Bitcoin and Lightning wallet.
It supports on-chain Bitcoin and Lightning transactions.
Update 2026-09-21
Verdict remains custodial. Blitz Wallet users said on X that “the unilateral exit is already implemented”, so we re-checked. It was implemented in the sense our 2026-08-20 update already describes: the app collects and exports the data an exit needs, and a third party’s tool performs the exit. Nothing has changed since then. One statement of ours was broader than the code supports; it is corrected below.
We compared the code our 2026-08-20 update is pinned to (BlitzWallet@b3891c86f) against the Android release on Google Play today (Android-v0.7.15, tagged 2026-09-04) and against Blitz Wallet’s current main (8182853ef, 2026-09-09, an unreleased 0.7.16). Blink’s tool was compared at its current head (1132b77f, 2026-09-09).
What has not changed
- The exporter is identical.
git diff b3891c86f..8182853ef -- app/components/admin/homeComponents/settingsContent/leaves/is empty, so the file-only output, the single globalhasAncestorscheck and the 16,348-sat floor quoted below are still what ships. - The app still never constructs, signs or broadcasts an exit.
git grep constructUnilateralExitFeeBumpPackagesover the app’s own code (app/,context-store/) onmainreturns nothing. - The Spark SDK dependency is still
^0.8.8. - Blink’s tool has one commit since our pin (it now detects an operator-initiated exit and pivots to its refund). Its documentation still describes recoveries only from bundles its own exporter produced, and mentions Blitz Wallet nowhere. We still found no documented recovery, start to finish, from a file produced by Blitz Wallet.
- Blitz Wallet’s own words on 2026-08-18, in the thread we cited: “There is no unilateral exit directly in Blitz, you would need to use the recovery tool listed above.” That matches our reading.
Correction: the export’s live request does have a time limit on most installs
Point 3 under “What would change our verdict” says the export’s first action is a live request “that nothing ever gives up on”. We traced that on the app’s native Spark runtime, and for that runtime it is true. But the app has two Spark runtimes, and the native one is not the default.
The default is a WebView running a bundled copy of the SDK (context-store/webViewContext.js:203):
let fallbackState = FALLBACK_STATE.WEBVIEW;
The native runtime is only entered when that bridge fails, either persistently after a failed bundle verification or after repeated bridge failures in one session (enterNative, setForceReactNative, lines 677-707).
On the WebView runtime every bridge request carries a deadline. The leaves query is in the longOperations set (webViewContext.js:121), which gets 90 seconds (webViewContext.js:1329-1341):
if (longOperations.has(action)) {
return 90000; // 90 seconds for payment operations
}
When the deadline passes the bridge answers with an error (webViewContext.js:1557), getSparkLeaves catches it and returns undefined (app/functions/spark/index.js:501-520), and the export continues from the cached leaves exactly as its comment promises (exportLeavesProgress.js:75-93).
So the indefinite wait we described exists only on installs that have fallen back to the native runtime. Point 3 is already met on the default runtime, and we now weigh it accordingly. It does not change the verdict: points 1 and 2 stand on their own.
One more thing in Blitz Wallet’s favour that we did not state explicitly: the automatic collection of exit records happens on both runtimes. A comment in the app says otherwise (index.js:532-533: “Native runtime only. Returns {} on the WebView path”), but the shipped bundle (android/app/src/main/assets/sparkContext.html) implements the getSparkLeafExitNodes operation with the SDK’s chain builder, and the SDK’s “Exit chain is incomplete” error text is present in it. The comment is stale; the code does the right thing.
Why the verdict still stays custodial
The test is the one below: can a user, with only what the app gives them and expecting help from nobody, actually force their money onto the Bitcoin blockchain? Today still no. The app writes a file. Recovery needs Blink’s command-line tool, fee funding the user supplies, and weeks of waiting. The file’s completeness is never checked leaf by leaf. Nobody has shown a recovery from a Blitz Wallet file. Balances below the exit floor cannot be recovered by this route at all.
Items 1 and 2 of “What would change our verdict” are unchanged. Item 3 now concerns only the native fallback runtime.
Housekeeping: Blitz Wallet removed Liquid and Rootstock from the app’s user interface on main (c7ffda52e, 2026-09-09). That is not in 0.7.15, so the liquid feature tag stays until the next release ships.
Update to Verdict 2026-08-20
Verdict remains custodial, but for different reasons than we gave before. Part of our previous analysis is now wrong, and we correct it below.
On 2026-08-18 Blitz Wallet said publicly that a recent update makes the app compatible with Blink’s Spark unilateral-exit process. We re-checked the code to see whether that changes the verdict. GitLab issue #947 tracks this question across all Spark wallets.
Everything below is pinned to BlitzWallet@b3891c86f, the Spark SDK at buildonspark/spark@2f0e6062, and Blink’s tool at blinkbitcoin/spark-unilateral-exit@1a33a032. Code blocks are quoted verbatim from those commits; where a block is abridged it is marked.
First, what the question actually is
In Spark, your money sits behind two signatures: yours and the Spark operators’. Every ordinary payment needs both. The operators cannot spend your money on their own, but they can refuse to move it.
So the question that decides self-custody is the escape route. Spark’s answer is called unilateral exit: you publish a stack of already-signed transactions to the Bitcoin blockchain yourself, wait out a delay, and claim your coins without asking anyone. If that works, the operators refusing is an inconvenience. If it doesn’t, it is permanent.
Correcting our previous analysis
Two things we published are no longer true. They were accurate when written against commit 546fcfc8, and they are wrong at b3891c86f:
- We wrote “No path was found in Blitz Wallet’s own client code.” The app now does call the Spark SDK’s exit machinery.
- We wrote that searching the repository for “unilateral” returned zero hits. It now returns many.
What Blitz Wallet built, and why it matters
To claim your money on the blockchain you need more than your own piece of it. Spark stores balances in a tree, and your piece — a “leaf” — can only be published if every transaction above it is published first. That chain of parent records is held by the Spark operators.
This is the SDK function that collects it (spark-sdk/src/utils/unilateral-exit.ts:103-108; the walk up the tree runs from line 112 to 163):
export async function buildUnilateralExitChain(
node: TreeNode,
nodeMap: Map<string, TreeNode>,
sparkClient?: SparkServiceClient,
network: Network = Network.MAINNET,
): Promise<TreeNode[]> {
and this is what happens when the operators reply without the parent record it asked for (same file, lines 144-149; a failed query is wrapped separately at 150-155):
parentNode = nodeMap.get(currentNode.parentNodeId);
if (!parentNode) {
throw new Error(
`Parent node ${currentNode.parentNodeId} not returned by query_nodes. Exit chain is incomplete.`,
);
}
Either way the chain cannot be completed. In plain terms: the records you need in order to escape the operators normally come from the operators. Ask for them too late and there is nothing to collect.
Blitz Wallet’s update addresses exactly this. The app now calls that function to gather the records ahead of time (app/functions/spark/index.js:569-574):
const chain = await buildUnilateralExitChain(
leaf,
nodeMap,
sparkClient,
Network.MAINNET,
);
And it starts doing so automatically, without the user knowing to ask. Collection begins when the wallet connects (context-store/sparkContext.js:2290-2295):
// On the first successful connect for an account: hydrate the leaves summary
// from the local SQLite store (so the Wallet Leaves page has cached totals
// immediately, even offline), then kick off one forced sync to freshen it.
useEffect(() => {
if (!sparkInformation.didConnect) return;
if (!sparkInformation.identityPubKey) return;
ending in a forced sync (sparkContext.js:2316):
reconcileLeaves(true);
which refreshes the stored parent records on every fresh snapshot (sparkContext.js:1206-1208):
// Backfill the exit-node ancestors for this fresh snapshot (background,
// not awaited — it self-throttles and single-flights).
reconcileExitNodes(rawLeaves);
This is a genuine improvement and it counts in Blitz Wallet’s favour. A user who never opens a settings screen may still hold what an exit requires. What the code guarantees is that collection is attempted automatically and retried on later activity; whether it completes for every leaf on a real wallet is not something source alone can tell us.
What the app still cannot do
It collects the records and writes them into the file. What it never does is use them to construct, sign or broadcast an exit.
The Spark SDK contains a second function, constructUnilateralExitFeeBumpPackages(), which builds the actual transactions that move your money. Blitz Wallet never calls it:
$ git grep -n 'constructUnilateralExitFeeBumpPackages' b3891c86f
(no matches)
There is no signing, no broadcasting, no waiting out the delay, no final sweep. The app’s exit feature ends by writing a file (exportLeavesProgress.js:168-170):
const fileData =
`{"schema":"spark.unilateral-exit-bundle.v1",` +
`"createdAt":${JSON.stringify(new Date().toISOString())},` +
To actually recover the money you take that file to Blink’s command-line tool — software from an unrelated company — and run the recovery yourself.
It has been done for real, but never from a Blitz file
This is not theoretical. Blink documented a real recovery on Bitcoin mainnet, begun on 2026-07-08, with transaction IDs anyone can check. It is worth knowing what it cost:
| Wallet | 100,000 sats in 22 leaves |
| Full 22-leaf exit graph | 253 transaction packages |
| Expected to reach the destination | ~82,028 sats at 1 sat/vB |
| Left behind as dust | 18 leaves, 9,888 sats |
| Time | multi-week — each level waits for a block, then a delay of 1,400 blocks for this wallet’s leaves (~2,000 for fresh ones) |
Those 253 packages describe exiting all 22 leaves; after a first attempt submitted them all, the recovery that was actually pursued covered only the four economical leaves. Recovering every leaf would have cost around 77,500 sats to get back 100,000. The exit did not finish quickly: the four refunds confirmed by 2026-08-01, and at Blink’s 2026-08-14 follow-up the final sweeps were still outstanding. That follow-up also revises the expected recovery down from an earlier 89,668-sat projection to ~82,028, because the exits completed by a costlier route than first assumed.
That recovery used Blink’s own file, produced by Blink’s own exporter. We found no documented recovery, start to finish, from a file produced by Blitz Wallet — not in Blitz Wallet’s repository or release notes, not in Blink’s repository or case studies, and not in Blitz Wallet’s own public statement, which points users to Blink’s tool rather than citing a completed Blitz recovery.
The same write-up records something that matters here. Blink’s first file was silently incomplete — the operators’ bulk lookup omits the very top record for older wallets, so all 22 chains had a gap — and their exporter now, in their words, “refuses to write a bundle with open chains.”
Blitz Wallet’s exporter performs no equivalent check. Its only completeness-related indicator is this (exportLeavesProgress.js:123):
const hasAncestors = nodeParts.length > 0;
That asks whether at least one parent record was stored anywhere in the wallet. It does not validate that any individual leaf has a complete chain to the top. Its effect is to select which note the file carries about itself — so a single stored record can produce a file whose own description claims complete chains, with no per-leaf check behind it.
To be clear about where the weakness is not: while collecting, the app fails loudly. If the operators do not return a parent, the SDK throws, the app catches it, and that leaf is left pending and retried later. The gap is at export time, where whatever happens to be cached is written out without checking that any leaf’s chain reaches the top.
The seed phrase is not a second route
Blitz Wallet also said you can “either use the Spark leaves cached by Blitz or go through Blink’s recovery process using your Blitz seed phrase.”
Blink’s own README opens by saying the opposite:
Seed-only recovery is not sufficient once Spark operators are offline, because current leaves cannot be discovered from the seed alone.
Their command for fetching that data is described as “Query live Spark leaves from a seed”, and their own recovery diagram places it in the stage marked “Preparation — Spark operators online”.
Put simply: your seed proves the money is yours. It does not tell anyone where it is. The seed remains necessary — the tool derives the fee-funding key, the signing keys and the refund keys from it — but the fees themselves are paid by a separately funded on-chain UTXO, and the money is swept to an address you supply. What the seed cannot do is discover which leaves you own. Both routes therefore depend on data captured while the operators were still answering, and the seed-only route works only while that data remains available from them. It is unavailable in exactly the outage the recovery route exists to survive. (Operators can also be reachable and still refuse to authorise a transfer, which is its own reason to exit.)
Small balances cannot be recovered this way
Recovering a leaf costs on-chain fees, so tiny leaves cost more to rescue than they hold. Blitz Wallet sets a floor (app/functions/spark/leavesStorage.js:14-16):
// Leaves below this value cannot be unilaterally exited economically (fees
// exceed value). Mirrors the Spark unilateral-exit minimum.
export const EXIT_MIN_SATS = 16348;
Anything smaller is left out of the file (exportLeavesProgress.js:142-146):
if (Number(leaf.value || 0) < EXIT_MIN_SATS) {
excludedCount++;
excludedSats += value;
continue;
}
Blitz Wallet does disclose this. The Wallet Leaves screen shows a separate “Below threshold — Too small to exit alone” bucket with its count and value (settingsContent/leaves/index.js:170-177), and the exported file records the excluded count, sats and reason. The threshold is also sound: it matches the figure in Spark’s own documentation, and Blink’s real dust leaves would have cost 2,100–4,600 sats each to recover.
The consequence is what matters for the verdict, not the disclosure: money below the floor cannot be recovered by this route at all, and if no leaf reaches the threshold the export produces nothing.
Why the verdict stays custodial
Needing a second signature is not by itself custody. A Lightning channel partner co-signs every update too, and we do not call Lightning wallets custodial — because under BOLT 5 both sides always hold a signed transaction they can publish alone, with no help and no lookup. Spark is different: its safety also depends on operators deleting their old key, which Spark itself calls a “moment-in-time” trust model, and its documentation lists attacks by a previous owner, timelock defence and watchtowers as part of the design.
So we judge it on whether the escape route can actually be used:
Can a user, with only what the app gives them and expecting help from nobody, actually force their money onto the Bitcoin blockchain?
Today the answer is still no:
- The app cannot perform a recovery. It only writes a file.
- The recovery needs a third party’s command-line tool, plus fee funding the user supplies, run over several weeks.
- The file’s completeness is never checked leaf by leaf — and the people who built this tooling treat that failure as serious enough to refuse to write such a file at all.
- We found no demonstrated recovery from a Blitz Wallet file. We would not normally require a wallet to prove its recovery path by drill, but this one is a single implementation of a new file format feeding a third party’s tool, and the reference implementation’s own first file was silently incomplete in exactly this way — the risk is demonstrated, not hypothetical.
- Money below the exit threshold cannot be recovered by this route, and if every leaf is below it, the file cannot be produced.
This verdict is not based on the operators co-signing ordinary payments, and it is not based on the automatic collection described above, which works in Blitz Wallet’s favour.
What would change our verdict
- One documented recovery, start to finish, from a file Blitz Wallet produced, with the operators treated as unavailable.
- An exporter that refuses to write — or clearly marks — a file whose chains are incomplete, instead of the single global check shown above.
- A path that still works once the operators are unreachable. The export is written to fall back on what is already stored, and its own comment says so — but its first action is a live request that nothing ever gives up on. The background code wraps the same call in a 15-second limit; the export calls it directly:
// Assemble the file from the cached leaves + cached exit nodes in
// yielding batches. Works fully offline from cache — no dependency on
// operators being reachable at export time.
const rawLeaves = await getSparkLeaves(currentWalletMnemoinc, false);
(the comment is at exportLeavesProgress.js:112-114; the live call it describes is the function’s first action, at :75)
We traced that call through every layer on the Android native runtime and found no time limit at any of them: index.js:515 hands it to the SDK’s getLeaves() (spark-wallet.ts:917), which reaches leaf-manager.ts:292 without setting a deadline; with no deadline the React Native connection layer installs no timer at all (connection.react-native.ts:259-281); and the Android gRPC module the SDK ships (SparkGrpcModule.kt, 575 lines) contains no timeout, deadline or keepalive anywhere, building a plain OkHttpChannelBuilder channel at :476. The SDK’s 60-second cap exists only in its XHR transport (xhr-transport.ts:15), which the native runtime does not use.
A refused connection still fails quickly, so this is not every outage — the exposure is an operator that accepts the connection and then never answers. In that case the export can wait indefinitely instead of falling back to the cache it already holds.
Items 1 to 3 together would, in our assessment, meet the standard above for a user’s exit-eligible balance — even though Spark operators would still co-sign ordinary payments. Money below the exit threshold would remain outside any unilateral route.
Previous Analysis (2025-11-03)
Updated Verdict
Verdict updated to custodial (see rationale below) — pending Luis/team review of this MR.
Prompted by GitLab issue #947, tracking public claims that Spark-based wallets do not implement unilateral exit in practice. Governing precedent: Wallet of Satoshi’s Spark “Self-Custody Mode” was reviewed for the same question and kept at custodial, because normal Spark transfers require the operator’s co-signature — see _mobile/com.livingroomofsatoshi.wallet.md, lines 103-105. Blitz Wallet is reviewed below against that same bar.
All code citations below are pinned to two commits: Blitz Wallet Android at BlitzWallet/BlitzWallet@546fcfc8, and its @buildonspark/spark-sdk dependency at the matching source commit buildonspark/spark@87f3b357. package.json declares ^0.8.5; the yarn lockfile resolves that to the exact published version 0.8.5.
1. Does the app support (sending/receiving) on-chain BTC?
Yes, but on-chain receive is not the default action. The home screen has two separate buttons: the main “Receive” (down-arrow) button opens a Spark-native menu (Quick Pay/username, Create Invoice, Create Pool, Add Contact) — handleReceive. A separate “Deposit” button opens a different menu that does include “Deposit Bitcoin / Receive via on-chain address” — handleDeposit, on-chain option label.
That “on-chain” address is not a locally-derived wallet address — selecting it calls the SDK’s wallet.getStaticDepositAddress() (app call site: app/functions/spark/index.js#L467-L482), which in turn requests the address from the Spark coordinator via generateStaticDepositAddress(), a live RPC (generate_static_deposit_address) that returns a coordinator-signed proof. The address cannot be generated offline.
On-chain sending works through sendSparkBitcoinPayment(), which calls the SDK’s wallet.withdraw(). Reading the SDK implementation, withdraw() is explicitly a Cooperative Exit Flow — it calls the Spark Service Provider’s requestCoopExit/completeCoopExit endpoints to produce the on-chain payout transaction. Generating the app’s on-chain receive address and completing on-chain withdrawals both depend on Spark server-side infrastructure.
2. Does the app allow the export of BIP-39 seed phrases?
Yes. The app exports a standard 12-word BIP-39 mnemonic — confirmed via its @scure/bip39 usage in app/functions/seed.js#L1-L25 and app/functions/isValidMnemonic.js#L1-L2 — but Spark balances depend on Spark-specific BIP-32 derivation paths on top of that mnemonic (see Q3). The Settings screen displays the live wallet’s mnemonic in full, with copy-to-clipboard and a SeedQR export view — app/components/admin/homeComponents/settingsContent/seedPhrasePage.js#L1-L34, matching the project’s own claim: README.md, “Self-custodial recovery”.
3. Are the seed phrases importable to wallets outside of the Spark SDK ecosystem?
The words themselves are valid input to any standard BIP-39 wallet, but generic wallets will not find or use the Spark balance by default, because they do not implement Spark’s derivation scheme or off-chain state model. The SDK derives every functional key (identity, signing, deposit, static deposit, HTLC preimage) under a custom, Spark-specific derivation path — m/8797555'/{accountNumber}'/{0-4}' — not any registered BIP44/49/84/86 purpose code a general-purpose wallet would try by default. See DefaultSparkKeysGenerator.deriveKeysFromSeed().
Practical effect: importing a Blitz Wallet seed phrase into a standard wallet (e.g. Electrum) is expected to derive an unrelated, empty set of keys under that wallet’s own default path, rather than surfacing any Blitz/Spark balance. This is expected because generic wallets do not implement Spark’s derivation scheme or off-chain state model — it does not by itself confirm or rule out unilateral exit, which is a separate question addressed next.
4. Is it possible for users to commence unilateral exit?
No path was found in Blitz Wallet’s own client code. Notably, unlike some other Spark-SDK wallets reviewed under #947, @buildonspark/spark-sdk 0.8.5 does publicly export real, operator-independent unilateral-exit machinery — buildUnilateralExitChain() and constructUnilateralExitFeeBumpPackages() — callable using leaf data (wallet.getLeaves()) the app already fetches, with no SSP cooperation required to construct the exit chain.
An exhaustive, file-by-file audit of every @buildonspark/spark-sdk call in the Blitz Wallet app (34 distinct SDK methods across the 5 files that import the package) found no call to either function, or to any other export of unilateral-exit.ts. The app’s only on-chain withdrawal path is the cooperative wallet.withdraw() described in Q1. No UI element, settings screen, or documentation in the repository references “unilateral exit” or an operator-independent recovery flow (grep -n -iE "unilateral" across the full repo: zero hits).
Blitz Wallet also operates its own standalone recovery tool, BlitzWallet/spark-recover, hosted at recover.blitzwalletapp.com and self-described as “Self-custodial. Stateless. Trustless. No third parties.”. Reviewed at commit eddcd347, pinned to @buildonspark/spark-sdk@^0.6.0: it calls the same cooperative wallet.transfer() and wallet.withdraw() methods as the main app — it is a second client for the same operator-dependent operations, not an independent exit mechanism. It is useful if the main app becomes unavailable while Spark’s operators are still online and cooperating, but it does not answer what happens if they are not.
Rationale for the verdict change
Under the WoS precedent — self-custodial requires exclusive user control as the normal path, not just a theoretical capability elsewhere in the dependency tree — Blitz Wallet’s shipped app does not meet that bar today: the app’s Spark send/withdraw paths, on-chain receive-address generation, and recovery tool all depend on Spark operator/coordinator cooperation. The verdict above has been updated from sourceavailable to custodial accordingly, consistent with the WoS/#947 precedent, pending team review of this MR.
Analysis (Previous)
We installed the app and created a BTC wallet with send/receive functions. We found its repository and confirm that it is source-available.
Lightning Network support
The lightning Network (LN) is a layer two protocol that promises instant, low fee micro payments. Some wallets do not support receiving transactions which disqualifies them for this feature.
Transactions on LN are
- instant: It usually takes less than five seconds to finish a transaction.
- cheap: Transactions usually cost a tiny percentage (0.1%) of the transferred amount and might also be free.
- micro payments: The unit of account on the LN is micro Satoshis (mSat) or 0.00000000001 BTC.
LN is still in early development and several very feasible attacks are known that can cost users money or disrupt the network.
Why we list it for this wallet
Blitz Wallet is a React Native application that allows users to interact with the Bitcoin Lighting Network in a self-custodial way.
Source: GitHub README
Liquid Network Integration
The Liquid Network is a Bitcoin sidechain designed to enable faster and more private transactions, launched by Blockstream in 2018. It allows for the issuance of digital assets, including security tokens and stablecoins. Wallet integration with Liquid means users can send and receive Bitcoin and other assets issued on the Liquid Network. This feature assesses wallets that support both sending to and receiving from Liquid addresses. Learn more about Liquid.
-
Faster Transactions: Liquid provides faster and cheaper transactions compared to Bitcoin’s main chain, improving the user experience for certain use cases like transfer between exchanges that support Liquid.
-
Enhanced Privacy: Through Confidential Transactions, Liquid hides the amounts and types of assets being transferred, offering greater privacy for its users.
-
Asset Issuance: Users can issue and manage digital assets on the Liquid Network.
- Bitcoin Custody: The Liquid Network is operated by a federation of members. “Liquid Bitcoins” are Liquid tokens representing actual Bitcoins in the custody of this federation. If the required quorum of the federation decides to use the bitcoins under their custody, they can. Therefore the user has to trust this federation.
-
Limited Adoption: As a specialized network, Liquid’s benefits are most pronounced in specific scenarios, and the ecosystem is smaller than Bitcoin’s main network, potentially limiting its utility for some users.
- Fallback custody: Bitcoins held by the Liquid federation need to be rotated on a regular schedule. If the federation fails to do so, they can also be spent by Blockstream alone.
Fingerprint Authentication
Fingerprint authentication in Bitcoin wallets enhances security by adding a biometric layer to the access control mechanisms. This feature leverages the unique patterns of a user’s fingerprint to grant access to the wallet, making unauthorized access significantly more difficult. Biometric authentication is generally faster and more convenient than traditional methods like passwords or PINs, as it eliminates the need to remember complex combinations. This form of authentication is particularly useful in mobile wallet applications, where ease of access and security are paramount. Fingerprint authentication minimizes the risk of phishing or brute force attacks that typically target password-based systems. Furthermore, it can be combined with other forms of security measures, like PINs or passwords, to create a multi-factor authentication system for added security. The use of biometric data, however, raises concerns regarding privacy and data security. Users must be assured that their biometric data is stored securely and not vulnerable to unauthorized access or breaches.
- Enhanced security through a unique, non-replicable form of identification
- Convenience and speed of access compared to traditional methods
- Reduced risk of phishing and brute force attacks
Potential privacy concerns and dependency on device hardware capabilities.
Free and Open Source (FOSS)
The wallet’s source code is published under a license approved by the Open Source Initiative (OSI) — such as MIT, GPL, Apache, or AGPL — allowing anyone to inspect, modify, and redistribute it. This is a prerequisite for independent security audits and reproducible builds. Licenses that are merely “source available” but not OSI-approved (e.g., those with Commons Clause restrictions, commercial use prohibitions, or other non-free terms) do NOT qualify. Reproducible builds are not sufficient alone.
-
Transparency: Anyone can verify what the code does — including whether it contains backdoors, bugs, or privacy-violating behavior.
-
Community auditing: More reviewers means vulnerabilities are more likely to be found and fixed.
-
Forkability: If the project is abandoned or goes rogue, the community can fork and continue it.
FOSS alone does not guarantee security. The released binary must also be reproducible from the public source to ensure users are actually running the code that was audited.
Why we list it for this wallet
Blitz is released under the terms of the Apache 2.0 license. See LICENSE for more information.
Source: License
Unlimited Accounts
The user can create or restore secondary BTC accounts
- Accounting: Multiple accounts allow for easier separation of funds.
- Privacy: Using an account with a positive balance at all time makes it relatively easy for blockchain analysis companies to determine the full history of the wallet. By using multiple accounts, emptying them at times, the user can retain some privacy.
Camera for Secure Transactions
Camera functionality in Bitcoin hardware wallets, represents a significant advancement in wallet security and data transmission. These wallets use their built-in cameras to scan QR codes, enabling a secure and efficient method for data exchange and transaction signing. The camera-based approach is integral to maintaining an air-gapped environment, meaning the wallet does not rely on potentially vulnerable USB or Bluetooth connections for communication. The process involves creating an unsigned transaction as a QR code using a compatible wallet app. This QR code is then scanned by the hardware wallet’s camera. Inside the wallet, the transaction is securely signed using the stored private key. The signed transaction is subsequently displayed as a new QR code on the wallet, which can then be scanned back by the app to broadcast it to the blockchain network. This method of using a camera for QR code scanning and transmission ensures that the private keys remain solely within the hardware wallet, significantly bolstering security against remote attacks. It also enhances privacy, as the wallet does not directly connect to the internet or other devices, thereby reducing exposure to potential vulnerabilities.
- Enhanced security by maintaining an air-gapped environment
- Increased privacy as the wallet does not directly connect to the internet
- User-friendly transaction process with QR code scanning
Product page updated by Daniel Andrei R. Garcia
⚠️ Liquid Network Integration
Liquid Bitcoin is NOT Bitcoin. L-BTC is held by a federation of companies. If a quorum of federation members collude or are compelled, they can seize all L-BTC. If the federation stops refreshing its timelocks, Blockstream's emergency keys alone can spend the funds. Only use Liquid for amounts you'd trust to a corporate consortium.
⚠️ Spark (operator-dependent Lightning balance)
A Spark balance depends on its operators. They co-sign every payment and withdrawal and can freeze them. Getting the funds out without them needs exit data saved in advance, a separate on-chain coin for fees, and a wait of about two weeks.
Community Opinions
These comments are contributed by Nostr users.
Loading opinions…
Do your own research
In addition to reading our analysis, it is important to do your own checks. Before transferring any bitcoin to your wallet, look up reviews for the wallet you want to use. They should be easy to find. If they aren't, that itself is a reason to be extra careful.