MetaMask / MetaMask/metamask-mobile
Reproducible-build check of v8.6.0: builds cleanly from source, artifacts differ
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 3k
- Forks
- 1.7k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
What is this about?
Who we are
Hi MetaMask team — we're from WalletScrutiny, a project that
independently checks whether cryptocurrency wallets can be rebuilt from their published source
and compared against the binaries users actually install. We do this across Android, iOS,
desktop and hardware wallets, and we publish results either way.
This is informational. We're not reporting a bug and we're not asking for any work — we just
think you should see our results directly rather than only reading them on our site.
What we did
We built MetaMask Android v8.6.0 from tag v8.6.0
(commit 4f0d82c75de7acca97cfd68dd34e2c0ed9c386c7) in a clean container and compared the
resulting split APKs against the three official Google Play splits.
The build succeeded: ./gradlew bundleProdRelease completed in 37m 2s across 3390 Gradle
tasks and produced an AAB, from which we extracted splits with bundletool.
That's worth stating plainly. The last version we attempted, v7.76.0 back in May, would not
build from source for us at all. For 8.6.0 the only obstacles were on our side: matching your
declared toolchain, and supplying a signing keystore, since signingConfigs.mainProd expects
one that isn't in the public repo. Once both were handled the build went through cleanly.
We used the documented setup mechanics: .js.env and .android.env created from their committed
.example files, and android/app/google-services.json generated via the
GOOGLE_SERVICES_B64_ANDROID path described in the README's Firebase Messaging Setup section.
Two prerequisites we did not fulfil, and should be upfront about: the README tells external
contributors to obtain an Infura API key and set MM_INFURA_PROJECT_ID, which we left at the
example file's null; and it says to supply your own Firebase project configuration, whereas we
passed a fixed configuration of our own through the documented base64 mechanism. So this is not a
complete external-contributor setup as written.
Environment used:
| Component | Version | Source |
|---|---|---|
| React Native | 0.83.6 | package.json |
| Node | 24.16.0 | package.json engines / .nvmrc |
| Yarn | 4.14.1 | packageManager |
| compileSdk / targetSdk | 36 | android/build.gradle |
| build-tools | 36.0.0 | android/build.gradle |
| NDK | 27.1.12297006 | android/build.gradle |
| Gradle | 8.14.3 | wrapper |
| JDK | 17 | our container |
What we found
The built splits do not match the official ones. Counts below are differing lines from a
recursive diff of the unzipped APKs, excluding root-level META-INF/:
| Split | Differences |
|---|---|
base.apk |
424 |
split_config.arm64_v8a.apk |
24 |
split_config.xxhdpi.apk |
4 |
Where they sit:
base.apk— 363 compiled resource files underres/,resources.arsc,
AndroidManifest.xml, all four DEX files,assets/index.android.bundle, the baseline
profiles,assets/app.config,assets/app.manifest, the Sentry asset files, and several
META-INF/servicesentries.split_config.arm64_v8a.apk— all 22 bundled native libraries differ, plus
AndroidManifest.xml.split_config.xxhdpi.apk—AndroidManifest.xml,resources.arsc, and one drawable.
Some files appear in the official APKs but not in ours. Ten are Google Wallet / tap-and-pay
resources (add_to_googlewallet_button.xml, tokenization_fragment.xml,
tp_issuer_progress.xml and similar), plus play-services-tapandpay.properties, a Google Pay
drawable in the xxhdpi split, META-INF/services/rg.H and META-INF/services/wg.s, and
stamp-cert-sha256 in all three splits.
We have not determined why any of these differ. We're reporting observations, not a
diagnosis. One thing we can state precisely rather than guess at: the split APKs we compared are
unsigned, because we extract them with bundletool without passing a keystore, so signature
material was excluded from the counts above. Everything else we simply haven't traced, and we'd
rather say so than speculate in your issue tracker.
Why we're posting this
Mainly so it isn't a surprise if you come across our listing.
If any of it is useful to you, we're glad. If some of these differences have obvious
explanations from your side, we'd genuinely like to know — it would improve our reporting, and
we'll correct anything we've got wrong.
We're not asking you to make the build byte-for-byte reproducible. That's real engineering
effort and whether it's worth it is your call. We'd only note that reproducibility is a
meaningful security property for a self-custody wallet: it lets anyone confirm the app in the
Play Store was built from the source you published, without having to take anyone's word for it.
If it's ever something you want to pursue, we're happy to help however we can.
For reference
Our verification is scripted and containerized: the build itself runs entirely inside a
container, so beyond Docker or Podman it needs only a shell, network access, and a copy of the
official APKs to compare against. Happy to share the script or walk through the comparison
method if that's useful.
- WalletScrutiny: https://walletscrutiny.com
- MetaMask listing: https://walletscrutiny.com/mobile/io.metamask/
- Verification: https://walletscrutiny.com/mobile/io.metamask/#verificationId=86df954c014d7e3c0f2808d35b34dfe18b9295bf995fb981c074d26b2798aef6
- Methodology: https://walletscrutiny.com/methodology/
Thanks for reading, and thanks for keeping the source open.
Related issues
https://github.com/MetaMask/metamask-mobile/issues/30428
Scenario
No response
Design
No response
Technical Details
No response
Threat Modeling Framework
No response
Acceptance Criteria
No response
Stakeholder review needed before the work gets merged
- Engineering (needed in most cases)
- Design
- Product
- QA (automation tests are required to pass before merging PRs but not all changes are covered by automation tests - please review if QA is needed beyond automation tests)
- Security
- Legal
- Marketing
- Management (please specify)
- Other (please specify)
References
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
This is an informational report, not a requested change: it names ./gradlew bundleProdRelease, tag v8.6.0, and the APK comparison workflow. Start by reviewing the documented setup and the cited files .js.env, .android.env, package.json, .nvmrc, and android/build.gradle; no acceptance criteria or definition of done is provided.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, react-native, typescript
- Domain
- build-system, mobile, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100