Winnow compare

Measured from source · 7 November 2026

Bitcoin wallets on iPhone, compared from their code

How Winnow compares with seven other open-source iPhone wallets that hold your own keys: features, code size, complexity, proofs and what each one depends on. Every number is measured from pinned source and rebuilt by a script anyone can run.

Winnow is measured at winnowwallet/winnow-lean v0.9.10, its Lean 4 app. What the comparison leaves out

At a glance

Winnow is a Bitcoin-only wallet written in Lean 4: the wallet, its peer networking and the app’s model, update and view compile to C, and LeanNative’s generated Objective-C shell draws them with UIKit. It checks the chain on the phone with compact block filters from Bitcoin peers it finds itself, and reaches Tor and I2P through gateways you run, with no client of its own.

Each wallet below holds its own keys on the phone, with the exceptions noted in the fourth column. Four of them (Phoenix, Bitkit, Blixt and Zeus) are Lightning-first wallets with an on-chain side; this page looks only at their Bitcoin wallet. The table shows defaults; most of these wallets let you change the chain source.

WalletBuilt withWhere the on-chain wallet livesChain dataWho else must signOn iPhone
WinnowLean 4, drawn by LeanNative's generated Objective-C/UIKit shell; no SwiftIts own, in Lean 4 (Lean/Winnow, and Lean/Secp, a secp256k1 whose field and curve arithmetic carry machine-checked proofs), in the same repository: keys, PSBTs, scripts, compact-filter sync, peer networking over Lean's libuv TCP lakefile.toml:5 Winnow.lean:1 Transport.lean:3 Secp256k1.lean:4 Gate.lean:1BIP157/158 compact filters from Bitcoin peers found through the census peer list shipped with the build (its signature checked when the build is made) and DNS seeds; Advanced mode adds your own peers Pool.lean:10 Census.lean:217 import-census:10 Settings.lean:161Nobody for the everyday wallet; multisig accounts need their other signers (phones, Ledgers or keys someone sent), with no co-signing server README.md:4 Send.lean:115 Multisig.lean:580App Store, in the listing the Swift app used (com.btcswift.app); tagged builds upload to TestFlight README.md:11 Project.lean:19 release.yml:1 release.yml:210
BlueWalletReact Native (TypeScript) with small native modulesIts own TypeScript (class/wallets) on bitcoinjs-lib 7 and @noble/secp256k1; chain data through a fork of the Electrum client abstract-hd-electrum-wallet.ts:1075 package.json:132 package.json:144Electrum servers: electrum1.bluewallet.io by default, a short hardcoded fallback list; any host and port can be set in Settings > Network BlueElectrum.ts:90 ElectrumSettings.tsx:193Nobody for on-chain funds. The optional LNDhub (custodial) and Ark wallet types depend on their own servers abstract-hd-electrum-wallet.ts:1075 lightning-custodian-wallet.ts:80 lightning-ark-wallet.ts:94App Store; builds go through TestFlight README.md:10 Fastfile:1306
GreenSwift (UIKit) over Blockstream's gdkgdk 0.78.0 (C++ for multisig, Rust for singlesig), linked as a prebuilt static library fetch_gdk_binaries.sh:29 session_impl.cpp:79Singlesig: Blockstream's Electrum server, or your own in Settings. Multisig: Blockstream's backend, not replaceable network_parameters.cpp:472 network_parameters.cpp:333 AppSettingsViewModel.swift:52Nobody for singlesig. Multisig (2-of-2, 2-of-3) needs Blockstream's server to co-sign every spend ga_auth_handlers.cpp:808 ga_auth_handlers.cpp:835App Store
MuunSwift (UIKit) with a Go library, libwalletlibwallet, Muun's own Go library in the same repository: keys, 2-of-2 and MuSig2 signing, addresses, fees. Muun's server holds the second key README.md:26 V5.go:16Muun's own server (Houston API): history, spendable coins, fees and block height. The person cannot change it Environment.swift:42 SettingsPresenter.swift:154 init.go:160Muun's server must co-sign every spend. Without it the person needs the Emergency Kit, the Recovery Code and Muun's separate Recovery Tool V4.go:47 V5.go:26 RecoveryToolViewController.swift:69App Store, per its fastlane release lane
PhoenixSwift (SwiftUI) over Kotlin Multiplatform shared codelightning-kmp 1.13.2 (Kotlin Multiplatform): Electrum client, swap-in wallet and final (BIP84) wallet build.gradle.kts:85 SwapInWallet.kt:14 FinalWallet.kt:15An Electrum server picked at random from a built-in list of 15, trusted as given; the person can enter their own server in Settings AppConfigurationManager.kt:66 ElectrumConfigurationView.swift:59ACINQ co-signs swap-in deposits (MuSig2 2-of-2) until a refund delay of about six months, and is the channel peer for every on-chain send. The final wallet is the person's alone SwapInProtocol.kt:11 NodeParams.kt:94 ValidateView.swift:1970App Store
BitkitSwift (SwiftUI) on Rust librariesLDK Node 0.7.0-rc.78 (Synonym's fork, Rust); its on-chain wallet is BDK Cargo.toml:48 builder.rs:1384Synonym's Electrum server (bitkit.to); the person can change it in Settings > Advanced > Electrum Server Env.swift:191 ElectrumSettingsScreen.swift:56Nobody LightningService.swift:796App Store
BlixtReact Native (TypeScript) with Go (LND)Embedded LND 0.21.1 (Go); the on-chain wallet is btcwallet, run in-process through react-native-turbo-lnd btcwallet.go:83Neutrino (BIP157/158) through six default peers, four run by Blixt; the person can change the peers in Settings constants.ts:30 Settings.tsx:1670Nobody OnChain.ts:217TestFlight
ZeusReact Native (TypeScript) with native modules and prebuilt Go and Rust librariesEmbedded LND 0.21.5 (Go, Zeus's fork); the on-chain wallet is btcwallet. LDK Node (BDK) is an option, and remote nodes can be driven too btcwallet.go:83 WalletCreationUtils.ts:109 WalletCreationUtils.ts:71Neutrino (BIP157/158) through five default peers, two run by Olympus; the person can add peers or allow only their own. The LDK Node option uses an Esplora server SettingsStore.ts:1476 LndMobileUtils.ts:222 LdkNodeUtils.ts:26Nobody for on-chain funds; they sit in the embedded wallet under one key on the phone EmbeddedLND.ts:100 btcwallet.go:83App Store

Features

What a person can do in each iPhone app today with the wallet’s Bitcoin, checked against the code. Where a wallet has several account types, a cell shows the best one it offers, and its note says which. Hover or focus a cell for the note; every cell links to its evidence. The cells were last checked by hand between 2 October 2026 and 3 November 2026. Since that review, 26 of the 441 cited passages have changed (in BlueWallet, Bitkit, Blixt and Zeus) and 100 have moved within their file in the code measured below.

FeatureWinnowBlueWalletGreenMuunPhoenixBitkitBlixtZeus
Keys and signing
Multisig or shared accounts Yes Yes Yes Partial Partial No No No
Hardware or external signer Yes Yes Yes No No Yes No Yes
PSBT import and export Partial Yes Yes No No No No Yes
Watch-only wallets Partial Yes Yes No No Partial No Yes
BIP 39 passphrase No Partial Yes No No Yes Partial No
Spending
Coin control No Yes Yes No No Yes No Yes
Speed up a stuck payment (RBF or CPFP) Partial Partial Yes Partial Partial Yes Partial Yes
Taproot receive addresses Yes Yes Partial Yes Yes Yes Yes Yes
Output descriptors Partial Yes Yes Partial Partial Yes No Partial
Chain data and privacy
Use your own node or server Yes Yes Yes No Yes Yes Yes Yes
Checks chain data on the phone Yes No No No No Partial Yes Yes
Tor or I2P Partial Partial Yes No Partial No Yes No
Backup and recovery
Standard BIP 39 recovery phrase Yes Yes Yes Partial Partial Yes Partial Partial
Encrypted backup off the phone Yes No Partial Yes Yes Partial No Partial
License
Source licenseMITMITGPL-3.0MITApache-2.0MITMITAGPL-3.0
  • Yes
  • Partial
  • Engine only
  • No
  • Unknown
Notes and code evidence for every cell

Links open the exact line that was checked, at the revision reviewed between 2 October 2026 and 3 November 2026.

Cited code that has changed since the review, and so is due a fresh look:

Winnow

BlueWallet

Green

Muun

Phoenix

Bitkit

Blixt

Zeus

Code size

Winnow counts 24,808 lines of code: 9,322 for the app and 15,486 for its own Bitcoin library, which lives in the same repository and includes 872 lines of proofs. The other wallets count 1.5 to 7.8 times as many lines. That is the app, plus a Bitcoin library of its own for Muun.

The count leaves out the engines each wallet is built on. Green, Phoenix, Bitkit, Blixt and Zeus embed gdk, lightning-kmp, LDK Node and LND; four of the five are Lightning nodes. Winnow is built on LeanNative, a 6,051-line framework that runs a Lean app on iOS; it plays the part SwiftUI or React Native play for the others, which are not counted either. Each engine is shown in the table, not in the totals. BlueWallet takes its Bitcoin primitives from npm packages, which the dependency tables count.

Winnow’s lines are denser, at about 44 characters each against 20–29 for the others. Counted in characters, the gap is 0.9 to 3.5 times.

  • App
  • Own Bitcoin library
Winnow25k1.1M
BlueWallet59k1.7M
Green70k2.0M
Muun86k2.3M
Phoenix73k1.9M
Bitkit115k3.3M
Blixt37k958k
Zeus195k3.8M
Code that builds into each iPhone app, without tests, generated files or comments, and without embedded engines. Snapshot of 7 November 2026.
Lines of code by part
WalletAppOwn Bitcoin libraryTotal linesCharactersvs WinnowEmbedded engine, not in the total
Winnow9,32215,48624,8081.09M—6,051 LeanNative/leannative@700b388
BlueWallet59,121—59,1211.69M2.4× · 1.6×None embedded
Green70,474—70,4742.03M2.8× · 1.9×34,374 Blockstream/gdk release_0.78.0
Muun57,14428,46985,6132.33M3.5× · 2.1×None embedded
Phoenix72,881—72,8811.90M2.9× · 1.8×28,779 ACINQ/lightning-kmp v1.13.2
Bitkit114,604—114,6043.35M4.6× · 3.1×128,396 synonymdev/ldk-node 0.7.0-rc.78, lightningdevkit/rust-lightning v0.2.7
Blixt36,740—36,7400.96M1.5× · 0.9×238,947 hsjoberg/lnd@ae185f2 (0.21.1)
Zeus194,646—194,6463.82M7.8× · 3.5×243,921 ZeusLN/lnd v0.21.5-beta-zeus

“vs Winnow” divides each total by Winnow’s, first in lines, then in characters. The engine column shows each engine at the version the app pins, so its size can be read beside the wallet’s.

Languages

Share of each wallet’s lines, app, own Bitcoin library and embedded engine together, since all of them ship in the app. Winnow and LeanNative are Lean, apart from LeanNative’s small C shim; most of the others combine a user-interface language with a library or engine in another.

WalletLeanSwiftKotlinRustGoTypeScript/JSC family
Winnow100%—————<1%
BlueWallet—6%———94%<1%
Green—68%—9%——23%
Muun—67%—<1%33%—<1%
Phoenix—60%40%———1%
Bitkit—47%—53%———
Blixt—<1%——87%13%<1%
Zeus—1%——56%43%<1%

Complexity and CRAP

CRAP scores each function by its cyclomatic complexity (CC, the number of paths through it) and how much of it the tests run: CRAP = CC² × (1 − coverage)³ + CC. Coverage needs each project’s test suite run with instrumentation, so it is not measured here. Testing can only bring CRAP down to CC, so complexity alone sets a floor: a function above 12 scores above 12 however well it is tested, and one above 30 fails crap4j’s default threshold whatever its tests. Complexity is measured on each wallet’s counted code, not on embedded engines.

4.3% of Winnow’s 3,001 functions are above 12, against 0.8–7.9% for the others. Its mean is 3.7; the others’ range from 1.9 to 5.3. Its most complex function, parseDouble, scores 44, and 11 of its functions are above 30. Winnow’s app follows the Elm architecture, and its update dispatchers, split by screen, come next: match expressions with an arm for each message, which this rule counts as paths.

Cyclomatic complexity per function, app and own Bitcoin library together
WalletFunctionsMean CC90th pctMaxCRAP above 12 at any coverageShare above 12, relative to the highestCRAP above 30 at any coverage
Winnow3,0013.7844130 (4.3%)11 (0.37%)
BlueWallet1,8215.310262143 (7.9%)45 (2.47%)
Green6,9052.244585 (1.2%)7 (0.10%)
Muun6,5721.942652 (0.8%)0 (0.00%)
Phoenix4,7032.5515577 (1.6%)9 (0.19%)
Bitkit6,9003.26111168 (2.4%)26 (0.38%)
Blixt1,0673.6713958 (5.4%)19 (1.78%)
Zeus5,0744.710246372 (7.3%)78 (1.54%)
By part: app and own Bitcoin library
PartFunctionsMean CC90th pctMaxCC > 12CC > 30Most complex
Winnow · app9824.21044687parseDouble
Winnow · own bitcoin library2,0193.4834624replenish
BlueWallet · app1,8215.31026214345(arrow_function)
Green · app6,9052.2445857validateTransaction
Muun · app5,0941.6322190getKindOfError
Muun · own bitcoin library1,4782.8626330do
Phoenix · app4,7032.55155779(computed_property)
Bitkit · app6,9003.2611116826(computed_property)
Blixt · app1,0673.671395819Settings
Zeus · app5,0744.71024637278render

Machine-checked proofs

Lean is also a proof assistant, so part of Winnow’s code comes with theorems that Lean’s kernel checks on every build. Tests check examples; a theorem covers every input. The other wallets are written in languages without proofs, so this section has nothing to compare them on.

95

Theorems in Winnow’s code

872 lines, all in its Bitcoin library’s secp256k1: limb and field arithmetic, the curve and its Jacobian formulas. Secp/Proofs/Gate.lean lists them, and the build fails if one rests on sorry or an extra axiom.

45

Theorems in LeanNative

303 lines, most of them about how the runtime steps an app: each step runs update on one message and asks for exactly that message’s commands, whatever the order of events. The Objective-C renderer and platform effects are not proved; they are trusted and tested.

Not proved

What the theorems leave out

The wallet logic, networking and app have tests, not proofs. Signing runs on the proved arithmetic but is not constant-time: its timing depends on the key (Keys/Secp256k1.lean).

Proofs are erased when Lean compiles, so they add nothing to the app. They are counted as lines in the code size above, and are not functions in the complexity table.

Dependencies and vendored code

Winnow resolves no third-party package: its Lake manifest pins only LeanNative, the framework counted above as its engine. Like every Lean program it links Lean’s runtime and standard library from the toolchain, which bring libuv and GMP; they are under prebuilt binaries below. The others depend on 58 to 1,447 packages, counting what their embedded engines pull in, because that code ships in the app too. Package counts from different ecosystems are not equal units: an npm package is often a few hundred lines, a Rust crate or Go module often more. Read them per row.

Third-party packages each app resolves and ships, beyond the code counted above
KindWinnowBlueWalletGreenMuunPhoenixBitkitBlixtZeus
Swift packages——31—195——
Lean packages (Lake)————————
Kotlin libraries————39———
Rust crates, inside prebuilt libraries——126——715—411
Go modules, inside the Go library———111——120136
npm packages—901————552897
CocoaPods from the trunk—2—28——83
Third-party packages0903157139587206801,447
Main libraries, the Bitcoin code outside the counted code, and code that arrives other than as source packages
WalletMain librariesBitcoin code not counted abovePrebuilt binariesThird-party code copied into the repositoryPatched dependencies
WinnowLeanNative 700b388 (its Lean framework, C shim and printed Objective-C shell) and Lean 4.35.0's runtime and standard library (libLean, libStd, libInit, libleanrt) with the toolchain's libuv and GMP; no package from a registry lakefile.toml:10 lean-toolchain:1 lake-manifest.json:3 ios_lean_sources.sh:58 Manifest.lean:193—None in the repository; the build links the Lean toolchain's static libraries (libLean, libStd, libInit, libleanrt, libuv, libgmp), macOS arm64 objects retargeted to iOS by LeanNative's retarget_ar.py ios_lean_sources.sh:15 ios_lean_sources.sh:58 ios_lean_sources.sh:67NoneNo patch files; the build rewrites the platform (LC_BUILD_VERSION) of Lean's macOS static libraries to iOS, which Lean does not support
BlueWalletbitcoinjs-lib, bip32, bip39, ecpair, @noble/secp256k1, BlueWallet forks of coinselect, rn-electrum-client, bip47, SilentPayments and slip39-js, aezeed, bolt11, payjoin-client, bc-ur (Keystone and NGRAVE), realm, react-native-keychain, @arkade-os/sdk and boltz-swap, Bugsnag. package.json:93bitcoinjs-lib, bip32 and @noble/secp256k1, from npmNo binaries are checked in. The build links React Native's prebuilt Hermes and React-Core-prebuilt pods, and the patched react-native-background-fetch pod vendors the npm package's TSBackgroundFetch.xcframework. Podfile.lock:28 Podfile.lock:97 react-native-background-fetch+4.4.2.patch:85,136 lines in blue_modules: pako, bbqr, uint8array-extras, bc-ur, bc-bech32Four patch-package patches on npm packages: react-native-background-fetch (iOS xcframework with Catalyst), react-native-notifications (Android token), react-native-context-menu-view (Android menus), react-native-screens (iOS accessibility id; patch is for 4.27.0 while package.json pins 4.28.0). Several dependencies also come from BlueWallet forks pinned to git commits.
Greengdk (libwally-core, rust-bitcoin, Tor), Liquid Wallet Kit, Greenlight SDK, Firebase, Countly, Zendesk, Rive, Charts, SwiftCBOR, AsyncBluetooth Package.resolved:77 Package.resolved:149 Package.resolved:68libwally-core, rust-bitcoin, rust-elements (inside the prebuilt gdk)gdk static library (libgreen_gdk_full.a) from Blockstream's release tarballs, checked against pinned SHA-256 sums; Google's grpc-binary and abseil-cpp-binary packages fetch_gdk_binaries.sh:29NoneCountly's iOS SDK comes from a fork (angelix/countly-sdk-ios 23.6.1-ave). gdk's own build patches libevent and bc-ur, outside the app.
MuunSwift: Firebase (Analytics, Crashlytics, Messaging), GoogleSignIn and Google Drive API, GRDB.swift, RxSwift, Dip, grpc-swift, Lottie. Go: btcd, lnd (lnwire, zpay32), lightning-onion, go-lnurl, gofpdf, pdfcpu, gorm with SQLite, gRPC. Podfile:15 Package.swift:15 go.mod:6btcd and lnd, as Go modules. Copies inside libwallet (btcsuitew, musig2v040) are countedRust zero-knowledge proof verifier (plonky2) linked as the static library multi-apple-ios-librs.a, built by libwallet/librs/makelibs.sh and not in the tree; FirebaseAnalytics and GoogleAppMeasurement xcframeworks from dl.google.com librs.go:16 pods_dependency_registry.txt:4 pods_dependency_registry.txt:14101,694 lines in falcon/app/Pods: GRDB.swift, FirebaseCrashlytics, RxSwift, GTMSessionFetcher, FirebaseMessaging, GoogleAPIClientForREST and 19 moreGo 1.24.7 toolchain is patched for cgo struct alignment (falcon/0001-align-to-ptrsize.patch); golang.org/x/mobile is replaced by the muun/mobile fork (libwallet/go.mod:200)
Phoenixbitcoin-kmp with secp256k1-kmp, Ktor, kotlinx-serialization, SQLDelight (Kotlin side); Firebase Messaging and a few small SwiftUI packages (Swift side) build.gradle.kts:88 build.gradle.kts:85 project.pbxproj:387bitcoin-kmpKotlin libraries (lightning-kmp, bitcoin-kmp, secp256k1-kmp native code, Ktor, SQLDelight) as compiled klibs from Maven Central; Firebase through Swift Package Manager build.gradle.kts:85 build.gradle.kts:18NoneNone
BitkitLDK Node (with BDK), bitkit-core, Paykit, VSS client, Lottie, swift-secp256k1 project.pbxproj:1176BDK, rust-bitcoinLDK Node, bitkit-core, Paykit and the VSS client, as XCFrameworks Package.swift:28 Package.swift:29 Package.swift:27 Package.swift:28NoneNone
BlixtReact Native, react-native-turbo-lnd, @bufbuild/protobuf, secp256k1, bech32, react-native-nitro-tor, react-native-keychain, easy-peasy package.json:70 package.json:104 package.json:122 package.json:134 package.json:145btcwallet (with btcd, neutrino) under LNDLndmobile.xcframework (LND), downloaded from the react-native-turbo-lnd release for the pinned LND commit react-native-turbo-lnd.podspec:19NoneNone in the app (no patch files); LND is Blixt's own fork (hsjoberg/lnd) with an added gossip speedloader
ZeusReact Native, bitcoinjs-lib, noble and scure cryptography, bc-ur registries (Keystone, NGRAVE), Nostr (NDK), react-native-nitro-tor, Cashu CDK package.json:53 package.json:55 package.json:56 package.json:59 package.json:77 package.json:86 package.json:117 package.json:148btcwallet (with btcd, neutrino) under LND; bitcoinjs-lib; BDK in the optional LDK NodeLndmobile.xcframework (LND), LDKNodeFFI.xcframework (LDK Node), and the Cashu CDK and restore xcframeworks, downloaded from release archives pinned by SHA-256 fetch-libraries-versions.json:2 fetch-libraries.sh:374,043 lines in zeus_modules, zeus_modules/@lightninglabs: lnc-core, lnc-rn, noble_eccSix postinstall scripts rewrite installed npm packages (two are Android-only: jcenter and notifications; four reach iOS: NativeEventEmitter, keychain cloudSync, noble-hashes exports, reanimated C++); rn-nodeify shims crypto, stream and dgram
  • Lean packages are the git packages in the app’s lake-manifest.json, less the framework counted as its engine.
  • Swift packages are every package Swift Package Manager resolves. Phoenix links Firebase Messaging, which brings in Google’s package graph.
  • Kotlin libraries are the 39 libraries Phoenix’s iOS framework links besides lightning-kmp, resolved for the iPhone target.
  • Rust crates are compiled into prebuilt libraries. Bitkit’s four libraries each carry their own copies: 715 distinct crates, 1,122 counting each library’s copy. Green’s gdk adds 126. gdk’s C++ dependencies are not counted.
  • Go modules are linked into Muun’s libwallet (111), and into LND in Blixt and Zeus.
  • npm packages are everything the app’s runtime dependencies pull in (98 declared by BlueWallet, 86 by Blixt, 109 by Zeus), not build tools. The bundler includes only what the app imports.
  • CocoaPods counts pods from the CocoaPods trunk. Another 106 pods in BlueWallet, 98 in Blixt and 114 in Zeus are the native halves of npm packages and React Native itself.
  • Muun commits the sources of its CocoaPods into its repository: 101,694 lines of third-party code that build into the app, with a registry of each pod’s upstream and checksum.

Over time

Wallet repositories are read at the head of their main branch, except Phoenix and Winnow, which are read at their latest release (ios-v2.8.4, v0.9.10); Phoenix’s default branch was configured for testnet when we checked. Every engine and library is read at the version its wallet pins. A snapshot is recorded whenever any of them moves: 14 since 2 October 2026, when this page began counting Bitcoin wallets only. Changes are shown against that first snapshot. On 30 October 2026 Winnow’s measured source moved from its Swift app to its Lean 4 rewrite, so its line jumps there. Hover or focus a line for each snapshot. The 4 earlier snapshots (27 September 2026 to 1 October 2026) compared Lightning wallets and counted their Lightning engines; they are in data.json.

WalletLines of codeThird-party packagesFunctions above CC 12
Winnow24,808+2,811 (+12.8%)0−1 (−100.0%)130+106 (+441.7%)
BlueWallet59,121+1,496 (+2.6%)903−27 (−2.9%)143+4 (+2.9%)
Green70,474no change157no change85no change
Muun85,613no change139no change52no change
Phoenix72,881no change58no change77no change
Bitkit114,604+20,915 (+22.3%)720+29 (+4.2%)168+76 (+82.6%)
Blixt36,740+380 (+1.0%)680−1 (−0.1%)58+2 (+3.6%)
Zeus194,646+4,662 (+2.5%)1,447−20 (−1.4%)372+27 (+7.8%)
Every snapshot
Lines of code
SnapshotWinnowBlueWalletGreenMuunPhoenixBitkitBlixtZeusSources that moved
7 November 202624,80859,12170,47485,61372,881114,60436,740194,646zeus, bluewallet
6 November 202624,80859,06970,47485,61372,881114,60436,740194,447bitkit, zeus, bluewallet
4 November 202624,80859,06770,47485,61372,881113,06636,740194,004bitkit, zeus, bluewallet
3 November 202624,80859,07570,47485,61372,881112,90636,740193,516winnow, leannative, bitkit, synonym-ldk-node, rust-lightning, bitkit-core, paykit-rs, vss-rust-client-ffi, blixt, zeus, lnd-zeus, zeus-ldk-node, cdk, bluewallet
30 October 202624,68357,85870,47485,61372,881100,64036,360191,851winnow, leannative
10 October 202621,99757,85870,47485,61372,881100,64036,360191,851bitkit, paykit-rs, zeus
9 October 202621,99757,85870,47485,61372,881100,23936,360190,159bitkit, zeus, lnd-zeus
8 October 202621,99757,85870,47485,61372,88199,67136,360190,500bitkit, paykit-rs, zeus, bluewallet
7 October 202621,99757,85870,47485,61372,88198,06836,360190,456bitkit, zeus, bluewallet
6 October 202621,99757,81670,47485,61372,88197,92136,360190,382bitkit, zeus
5 October 202621,99757,81670,47485,61372,88194,77436,360190,363bitkit, zeus, zeus-ldk-node, bluewallet
4 October 202621,99757,81670,47485,61372,88193,68936,360190,001zeus, bluewallet
3 October 202621,99757,62570,47485,61372,88193,68936,360190,005winnow, zeus, lnd-zeus
2 October 202621,99757,62570,47485,61372,88193,68936,360189,984First snapshot
Third-party packages
SnapshotWinnowBlueWalletGreenMuunPhoenixBitkitBlixtZeus
7 November 20260903157139587206801,447
6 November 20260903157139587206801,447
4 November 20260903157139587206801,452
3 November 20260904157139587206801,452
30 October 20260924157139586986811,457
10 October 20261924157139586986811,457
9 October 20261924157139586986811,457
8 October 20261924157139586986811,457
7 October 20261930157139586916811,457
6 October 20261930157139586916811,466
5 October 20261930157139586916811,466
4 October 20261930157139586916811,466
3 October 20261930157139586916811,466
2 October 20261930157139586916811,467
Functions above CC 12
SnapshotWinnowBlueWalletGreenMuunPhoenixBitkitBlixtZeus
7 November 202613014385527716858372
6 November 202613014385527716858372
4 November 202613014385527716658370
3 November 202613014385527716458368
30 October 202611714185527710956350
10 October 20262414185527710956350
9 October 20262414185527710556345
8 October 20262414185527710156347
7 October 2026241418552779656347
6 October 2026241418552779656347
5 October 2026241418552779356347
4 October 2026241418552779256347
3 October 2026241398552779256346
2 October 2026241398552779256345

What the numbers leave out

Less code is not proof of safety

A smaller codebase is quicker to read and review. It does not show the code is correct, and it says nothing about whether anyone independent has reviewed it. Check each project’s own security page.

The wallets do different amounts of work

Each count is the whole app, not only its on-chain wallet. Zeus also manages remote LND and Core Lightning nodes and includes Cashu, Nostr Wallet Connect and a point of sale. Bitkit includes Pubky contacts, Paykit and Trezor support. Winnow includes k-of-n multisig accounts and a Ledger signer. BlueWallet includes Lightning wallet types (an LNDHub custodian and an Ark one). Green’s count includes its lightning folder. Muun includes Lightning payments through swaps.

Engines are not counted

Phoenix, Bitkit, Blixt, Zeus and Green build their Bitcoin wallet on an engine that is a separate project: lightning-kmp, LDK Node with BDK, LND and gdk, so they look smaller than everything they ship. Winnow’s engine is a UI framework, LeanNative; its Bitcoin code is its own and counted, as Muun’s is. BlueWallet has no engine.

Who can move on-chain funds

Where a service has to cosign, the wallet cannot spend without it, or only after a delay. The fourth column of the table at the top says which wallets that applies to, and the notes under each cell give the account type.

How we measured

  • Lines: cloc 2.06 counted non-blank, non-comment lines in each app’s programming languages, for code that builds into the iPhone app. Characters were counted after removing comments and all whitespace. Excluded: tests; Android, desktop and web code; build tools; files marked as generated (protobuf, sqlc and UniFFI bindings); and vendored third-party code. Rust test modules inside source files were removed. Winnow’s test oracles, test vectors and UI tests are left out by name; Lean proofs are counted as lines.
  • Complexity: each file was parsed with tree-sitter, and one rule applied to every language. CC is 1, plus 1 for each if or guard, loop, catch, ternary, &&, ||, ?? or elvis, and non-default case, when or match arm. Closures, lambdas and nested functions count toward the function that contains them, so a React screen written as one function component counts as one function. Tree-sitter could not fully parse at most 5% of any wallet’s files; it recovers locally, so their functions are still counted. No tree-sitter grammar parses current Lean, so Lean is read lexically under the same rule: each def, instance, abbrev or opaque is a function, and theorems are not.
  • Dependencies: read from each app’s own lockfiles (Swift Package Manager, Lake, CocoaPods, npm, yarn and bun). Kotlin libraries were resolved from Maven Central’s metadata for the iPhone target. Rust crates come from cargo tree for aarch64-apple-ios at each prebuilt library’s pinned source. Go modules come from go list -deps for iOS, with the build tags LND’s own iOS build uses or, for Muun, none.
  • Vendored code: each repository was searched for third-party code committed into it: vendor directories, committed CocoaPods, copied npm packages, license files below the root, committed binaries and dependency patches.
  • Features: each cell was checked by hand in the app’s and the engine’s source between 2 October 2026 and 3 November 2026, at the revisions in reviewed.json. The build compares every cited passage with today’s code and lists any that have changed. The rows and what each value means are in features/rows.json.
  • Embedded engines: counted at the version each app pins, as the named library only, and kept out of every total. Winnow’s LeanNative (LeanNative/leannative@700b388) is the commit its lake-manifest.json pins: its Lean framework, the Lean that prints the shell’s Objective-C, and its C shim. Phoenix’s lightning-kmp comes from its Gradle catalog. Bitkit’s LDK Node and libraries come from its Package.resolved, and rust-lightning from LDK Node’s lockfile. Blixt’s LND is the fork its react-native-turbo-lnd release builds. Zeus’s LND, LDK Node and Cashu versions come from its fetch-libraries-versions.json, and its count uses its default embedded LND. Green’s gdk is the release its fetch_gdk_binaries.sh downloads.
Exact sources

Each project changes often. These numbers are measured again whenever a pinned source moves, and each snapshot is dated. They are a measurement, not a ranking.

Reproduce it

Everything on this page is generated from the source repository. It pins every project to an exact commit, and data.json holds every measurement, including the full package lists. To rebuild it:

git clone https://github.com/winnowwallet/compare
cd compare
npm ci && pip install -r requirements.txt
./build          # fetch the pinned sources, measure, render
git diff         # nothing changes if the measurement reproduces

It needs Python 3.11, Node, Go and Cargo, plus network access to GitHub, crates.io, the Go module proxy and Maven Central; no Lean toolchain is needed. Feature cells and profile facts are reviewed by hand. The build checks that every citation resolves at the revision it was reviewed against, and reports which cited code has changed since.

Not compared

This comparison needs the iPhone app’s source, so closed-source and custodial wallets cannot be measured and are left out, however widely they are used. Other open-source wallets could be added.

  • Exodus, Cash App and Strike are widely used iPhone wallets whose apps are closed source, so there is no code to measure or cite.
  • Envoy, Bull Bitcoin, Bitcoin Keeper, Cake Wallet and Edge publish source code and may be added. Each needs a measured iPhone build and a hand review of its features.