perf: use native WebCrypto implementation - #36645
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
Smart E2E Test SelectionSelected E2E tags: ALL Selected Performance tags: ALL AI Confidence: 90 E2E reasoningExpand to readThe change in
Since this change affects the foundational cryptographic API used across the entire application, any flow that relies on SubtleCrypto could be impacted. The blast radius is the entire app. Running ALL E2E smoke tags is the appropriate conservative approach to ensure no cryptographic operations are broken across any user flow. Performance reasoningExpand to readThe change to |
|
|
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last green on main) run 35962851073 @ e01b8ad
Summary:
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 8.39% | 10.72% | +2.33 (+27.8%) |
| CPU max | 21.56% | 24.72% | +3.16 (+14.7%) |
| Memory avg | 586.18 MB | 658.28 MB | +72.1 (+12.3%) |
| Memory max | 754.29 MB | 787.35 MB | +33.06 (+4.4%) |
| Slow frames | 5.19% | 29.34% | +24.15 (+465.3%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 2 | 3 | +1 (+50%) |
| Critical issues | 1 | 1 | 0 (0%) |
| App size | 397.48 MB | 397.58 MB | +0.1 (+0%) |
Measure Warm Start: Login To Wallet Screen
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last green on main) run 35962851073 @ e01b8ad
Summary:
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 8.03% | 8.54% | +0.51 (+6.4%) |
| CPU max | 22.55% | 23.41% | +0.86 (+3.8%) |
| Memory avg | 643.23 MB | 572.3 MB | -70.93 (-11%) |
| Memory max | 777.44 MB | 707.9 MB | -69.54 (-8.9%) |
| Slow frames | 9.26% | 44.11% | +34.85 (+376.4%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 2 | 2 | 0 (0%) |
| Critical issues | 1 | 2 | +1 (+100%) |
| App size | 397.48 MB | 397.58 MB | +0.1 (+0%) |
Measure Warm Start: Warm Start to Login Screen
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last green on main) run 35635949154 @ 7bdb9cb
Summary:
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 6.88% | 9.58% | +2.7 (+39.2%) |
| CPU max | 21.63% | 23.25% | +1.62 (+7.5%) |
| Memory avg | 560.41 MB | 657.47 MB | +97.06 (+17.3%) |
| Memory max | 712.95 MB | 782.86 MB | +69.91 (+9.8%) |
| Slow frames | 12.5% | 19.38% | +6.88 (+55%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 2 | 2 | 0 (0%) |
| Critical issues | 1 | 1 | 0 (0%) |
| App size | 396.81 MB | 397.58 MB | +0.77 (+0.2%) |
@mm-perps-engineering-team
Perps add funds
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last green on main) run 34935384411 @ 4e14632
Summary: ✅ No metrics over the +10% baseline margin.
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 9.95% | 9.1% | -0.85 (-8.5%) |
| CPU max | 22.93% | 24.56% | +1.63 (+7.1%) |
| Memory avg | 702.75 MB | 689.09 MB | -13.66 (-1.9%) |
| Memory max | 877.25 MB | 836.73 MB | -40.52 (-4.6%) |
| Slow frames | 15.97% | 10.62% | -5.35 (-33.5%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 2 | 1 | -1 (-50%) |
| Critical issues | 1 | 1 | 0 (0%) |
| App size | 394.88 MB | 397.58 MB | +2.7 (+0.7%) |
Perps open position and close it
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last green on main) run 34966774120 @ acda9d8
Summary:
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 8.17% | 7.83% | -0.34 (-4.2%) |
| CPU max | 25.69% | 21.51% | -4.18 (-16.3%) |
| Memory avg | 826.35 MB | 817.72 MB | -8.63 (-1%) |
| Memory max | 1069.37 MB | 1010.74 MB | -58.63 (-5.5%) |
| Slow frames | 9.2% | 11.23% | +2.03 (+22.1%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 2 | 2 | 0 (0%) |
| Critical issues | 2 | 1 | -1 (-50%) |
| App size | 394.9 MB | 397.58 MB | +2.68 (+0.7%) |
@team-predict
Predict Deposit - Complete Flow Performance
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last run on main (scenario also failing)) run 35962851073 @ e01b8ad
⚠️ No green baseline onmain— comparing against the latest usable profiling.
Summary:
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 7.63% | 10.07% | +2.44 (+32%) |
| CPU max | 21.97% | 23.63% | +1.66 (+7.6%) |
| Memory avg | 668.2 MB | 730.22 MB | +62.02 (+9.3%) |
| Memory max | 748.46 MB | 840.76 MB | +92.3 (+12.3%) |
| Slow frames | 13.1% | 15.12% | +2.02 (+15.4%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 2 | 2 | 0 (0%) |
| Critical issues | 1 | 1 | 0 (0%) |
| App size | 397.48 MB | 397.58 MB | +0.1 (+0%) |
Predict Market Details - Complete Flow Performance
| Platform | Device | Reason | Recording |
|---|---|---|---|
| Android | Google Pixel 8 Pro (v14.0) | no_performance_metrics | 📹 Watch |
🔬 App profiling check · Current run 35991814370 · Baseline (last green on main) run 35962851073 @ e01b8ad
Summary: ✅ No metrics over the +10% baseline margin.
ℹ️ API calls unavailable:
Network logs API error: Bad Request
Full metric table (+10% variance rules)
Disclaimer — allowed variance: a +10% margin over the baseline is permitted.
- If
Current <= Baseline + 10%, treated as acceptable noise.- If
Current > Baseline + 10%, Current and variance % are highlighted with⚠️ .
| Metric | Baseline | Current | Δ |
|---|---|---|---|
| CPU avg | 9.88% | 7.89% | -1.99 (-20.1%) |
| CPU max | 21.66% | 21.33% | -0.33 (-1.5%) |
| Memory avg | 676.97 MB | 692.5 MB | +15.53 (+2.3%) |
| Memory max | 889.79 MB | 782.12 MB | -107.67 (-12.1%) |
| Slow frames | 46.38% | 10.34% | -36.04 (-77.7%) |
| Frozen frames | 0% | 0% | 0 (0%) |
| ANRs | 0 | 0 | 0 (0%) |
| Issues | 3 | 1 | -2 (-66.7%) |
| Critical issues | 2 | 1 | -1 (-50%) |
| App size | 397.48 MB | 397.58 MB | +0.1 (+0%) |
✅ Passed Tests (16)
| Test | Platform | Device | Duration | Team | Recording |
|---|---|---|---|---|---|
| Aggregated Balance Loading Time, SRP 1 + SRP 2 + SRP 3 | Android | Google Pixel 8 Pro (v14.0) | 8.48s | @assets-dev-team | 📹 Watch |
| Asset View, SRP 1 + SRP 2 + SRP 3 | Android | Google Pixel 8 Pro (v14.0) | 1.74s | @assets-dev-team | 📹 Watch |
| Cross-chain swap flow - ETH to SOL - 50+ accounts, SRP 1 + SRP 2 + SRP 3 | Android | Google Pixel 8 Pro (v14.0) | 5.31s | @swap-bridge-dev-team | 📹 Watch |
| Swap flow - ETH to LINK, SRP 1 + SRP 2 + SRP 3 | Android | Google Pixel 8 Pro (v14.0) | 1.80s | @swap-bridge-dev-team | 📹 Watch |
| Import SRP with +50 accounts, SRP 1, SRP 2, SRP 3 | Android | Google Pixel 8 Pro (v14.0) | 4.19s | @Accounts-team | 📹 Watch |
| Predict Available Balance - Complete Flow Performance | Android | Google Pixel 8 Pro (v14.0) | 0.00s | @team-predict | 📹 Watch |
| Rewards tab time-to-content (onboarding or dashboard) | Android | Google Pixel 8 Pro (v14.0) | 0.90s | @performance-team | 📹 Watch |
| Measure Cold Start To Onboarding Screen | Android | Google Pixel 8 Pro (v14.0) | 0.38s | @metamask-mobile-platform | 📹 Watch |
| Fresh SRP wallet creation performance | Android | Google Pixel 8 Pro (v14.0) | 12.65s | @metamask-onboarding-team | 📹 Watch |
| Onboarding Import SRP with +50 accounts, SRP 3 | Android | Google Pixel 8 Pro (v14.0) | 4.50s | @metamask-onboarding-team | 📹 Watch |
| Money Home after fresh wallet creation with empty balance | Android | Google Pixel 8 Pro (v14.0) | 2.58s | @mm-earn-team | 📹 Watch |
| Money Home after importing SRP with funded balance | Android | Google Pixel 8 Pro (v14.0) | 3.08s | @mm-earn-team | 📹 Watch |
| Account creation after fresh install | Android | Google Pixel 8 Pro (v14.0) | 1.83s | @metamask-onboarding-team | 📹 Watch |
| Seedless Onboarding: Google Login New User | Android | Google Pixel 8 Pro (v14.0) | 3.57s | @metamask-onboarding-team | 📹 Watch |
| Seedless Onboarding: Apple Login New User | Android | Google Pixel 8 Pro (v14.0) | 3.80s | @metamask-onboarding-team | 📹 Watch |
| Seedless Onboarding: Telegram Login New User | Android | Google Pixel 8 Pro (v14.0) | 2.85s | @metamask-onboarding-team | 📹 Watch |
Branch: perf/native-webcrypto-subtle · Build: E2E · Commit: bb9c8d7 · View full run



Description
Background
This PR expands the WebCrypto API exposed at
global.crypto.subtlefrom a digest-only shim to the complete nativeSubtleinstance provided byreact-native-quick-crypto@1.1.5.The digest-only behavior was intentional when it was introduced in #23769: MetaMask Mobile was using
react-native-quick-crypto@0.7.15, whose broader SubtleCrypto implementation was still partial, whiledigesthad the parity needed to accelerate SHA-256 verification of the Snaps registry.The dependency was subsequently upgraded from patched
0.7.15to1.1.5in #30716. Version 1.1.5 has native implementations for the standard SubtleCrypto operation families documented in the implementation coverage matrix, including encryption/decryption, key generation/import/export, derivation, signing/verification, and key wrapping.What is being replaced
Before this change, the effective runtime shape was:
Although the old code spread
global.crypto.subtleandcrypto.subtle, neither supplied a broader implementation in the React Native runtime:react-native-get-random-valuesinitializesglobal.cryptowithgetRandomValues, but does not providesubtle.require('crypto')toreact-native-cryptoinmetro.config.js; that JavaScript polyfill does not exportsubtle.digestoverride was therefore the only effective SubtleCrypto method.After this change:
This exposes the actual native-backed
Subtleinstance. Assigning the instance directly matters because its methods are class prototype methods and are not copied by object spread.The newly exposed operation families are:
encrypt/decryptderiveBits/deriveKeygenerateKey,importKey, andexportKeysign/verifywrapKey/unwrapKeydigest(unchanged native implementation)getPublicKeyKnown global WebCrypto consumers
The repository has two distinct consumer groups:
react-native-quick-cryptodirectly. Those paths already used the package singleton and are not changed by this shim.@metamask/snaps-registryusesglobalThis.crypto.subtle.digestfor its SHA-256 registry-verification fast path. The same helper is bundled intoapp/core/InpageBridgeWeb3.js.multiformatsandjsontokensuse globalsubtle.digestwhere available.node-forgefeature-detects global SubtleCrypto and may now select WebCrypto-backed RSA paths that were unavailable with the digest-only object.@solana-mobile/mobile-wallet-adapter-protocoluses global SubtleCrypto for key generation/import, derivation, signing, encryption, and decryption. These branches can now execute natively instead of failing or using a fallback.The runtime matrix below validates the global surface and representative native primitives. It does not claim end-to-end coverage of every third-party feature flow; those dependency branches are the main residual integration risk.
Scope and compatibility
global.crypto.subtle. The existing top-levelglobal.cryptomerge,randomUUID, andgetRandomValueswiring are unchanged.react-native-quick-cryptodirectly is unaffected; it already had access to the complete package API.Validation
subtle-coverage-evidence.pdf
Package-level algorithm and error-path coverage remains owned by react-native-quick-crypto's native iOS and Android SubtleCrypto suites. App-level validation for this PR was executed through
global.crypto.subtleinside Hermes, rather than through a direct package import.The same runtime matrix passed on both branch-matched native builds:
Subtleinstance.decrypt,deriveBits,deriveKey,digest,encrypt,exportKey,generateKey,importKey,sign,unwrapKey,verify, andwrapKey.SHA-256("abc")returnedba7816bf...15adon both platforms.MetaMask native WebCryptoon both platforms.password, saltsalt, one iteration returned120fb6cf...be17bon both platforms.Supporting validation:
shim.js(four unrelated existing deep-import warnings remain).shim.jsintroduces none.The remaining failed component-view CI shard exhausted its 12 GB Node heap after its tests passed. It does not exercise
shim.jsor native crypto and is unrelated to this change.Changelog
CHANGELOG entry: null
Related issues
Refs: #23769, #30716
Manual testing steps
Screenshots/Recordings
Before
N/A — no UI change.
After
N/A — no UI change.
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an examplePre-merge reviewer checklist
Note
Medium Risk
Widens global SubtleCrypto for third-party code paths that may now run native crypto operations; digest-only callers should behave the same, but native binary/JS version skew remains the main integration risk.
Overview
global.crypto.subtlenow exposes the full nativeSubtleinstance fromreact-native-quick-cryptoinstead of a digest-only object built from spreads and a singledigestoverride.In
shim.js, the polyfill assignssubtle: quickCryptoSubtledirectly so prototype methods (encrypt/decrypt, key import/export, sign/verify, PBKDF2, etc.) are available to code that uses global WebCrypto—e.g. Snaps registry SHA-256, Solana mobile wallet adapter, and libraries that feature-detectsubtle.digeststays on the same native path; only the global surface widens from{ digest }to the complete implementation.Reviewed by Cursor Bugbot for commit f4b4592. Bugbot is set up for automated code reviews on this repo. Configure here.