Conversation
Generalizes the trustless-decode vault pattern to all signing: identicon header, plain-language decoded action, anti-poisoning recipient, risk banner, raw hex behind Advanced, human-readable ERC-20 calldata. Slide-to-approve replaces the approve button across all 10 request types; decode failures now fail closed.
Batch approval derives per-chain keys with progress and returns them via the existing sync path. Adds the out-of-band 6-word verification code with word + scan-to-verify comparison so a malicious relay cannot swap extended keys.
A co-signer that remembers what it signed: a local, encrypted, biometric-gated log of every action this Key co-signed. Uses a new dedicated keychain service and the app's existing encryption; stores only public metadata (never signatures), never synced to the relay. Recorded via one additive fire-and-forget call after the relay post commits — no signing logic changed.
… audit fixes Recheck fixes: pin @noble/hashes 2.2.0 explicitly; batch chain-sync rejects the same xpub reused across chains; verification-chip accents on the SSP palette with black badge text (byte-identical with ssp-wallet); removed console logging of sync payloads. Phase 5: real word-verification on Create — a 3-word challenge with BIP39 decoys replaces the 'I backed it up' switch (read-only over the in-memory phrase; storeMnemonic unchanged, runs only after verification). PillarMark brand component on Startup and backup-confirmed moments. Platform-correct monospace (Menlo on iOS), tabular numerals on amounts/fees, fixed heights relaxed for all 41 languages. Phase 6: privacy mode — masks Signing History identities/txids/identicons (fixed-length dots; new append-only MMKV key), toggled via History eye, identity-chip tap, or Settings switch; never masks anything inside signing screens. VaultRiskStrip now delegates all banner presentation to RiskBanner so severity styling lives in one place (external API unchanged).
Home.tsx shrinks 4,132 -> 1,465 lines with zero behavioral change, proven by token-level parity checks against the previous revision at every stage: usePendingRequests hook owns the 13 pending-request states and the socket mapping effects (identical API, subscription untouched); the render splits into HomeProgress/HomeIdle/HomeRequests/HomeModals with Home remaining the sole state owner; 15 handler bodies relocate verbatim into action modules behind a context whose property names equal the original closure variables; pure input parsing extracted with tests. One verified consolidation: the vault-signing ingestion block that existed as two verbatim copies is now ingestVaultSigningRequest. Icons: the Feather + MaterialCommunityIcons mix migrates to lucide-react-native (pure JS on the existing react-native-svg; exact-pinned). All sizes, colors and accessibility props preserved. react-native-vector-icons stays as a dependency until a device-verified native rebuild removes its fonts. Device pass before release should exercise: socket delivery of every request type, pull-to-refresh vault-sign ingestion, pairing + batch sync end-to-end, approve and reject paths, QR + manual input parsing, nonce replenish.
Official Inter v4.1 static TTFs (Regular/Medium/SemiBold/Bold) wired through the RN asset pipeline. iOS registers all four faces via UIAppFonts so fontWeight resolves to real weights. Android gets an XML font family (res/font/inter.xml, weights 400-700) registered through ReactFontManager.addCustomFont — RN's assets/fonts path cannot resolve 500/600 weights and would faux-bold, so the XML family is the correct route. Fonts.ts sets the Inter family with an unchanged API; no component changes. Verified: iOS simulator build launched and letterform-matched to Inter Bold; Android assembleDebug + emulator install ran clean (visual bold check pending a human eye — FLAG_SECURE correctly blocks screenshots).
calldataDecode gains increaseAllowance(address,uint256) and setApprovalForAll(address,bool) alongside the existing approve. Allowances at or above 2^255 flag unlimited (dapps use many astronomical sentinels; nothing legitimate reaches 2^255) while transfers keep the exact-max sentinel; setApprovalForAll accepts only canonical bool encodings and fails closed. The approval screen warns at high severity for unlimited approvals and collection-wide operator grants, naming the spender; bounded approvals get an info line. Human token amounts only when the on-device registry knows the decimals — never guessed. Decode-layer only; signing untouched; existing decode tests pass byte-unmodified.
CRITICAL: the setApprovalForAll selector was a22cb442; the real keccak-derived selector is a22cb465 — the collection-approval warning never fired on real calldata. Fixed, and the test vectors now DERIVE selectors via keccak at test time so a wrong constant fails tests instead of being self-confirmed by vectors built from the same constant. Also: approve(spender, 0) reads as a revocation instead of 'spend 0 units'; EVM-signing request handler no longer logs the request body (carried the wallet's partial signature); signed-tx/txid logs removed; sign-history appends serialized against concurrent approvals; Create resets its challenge flags on success; verification words deliberately monospace (cross-device parity with the wallet's mono rendering); mismatch banner black-on-red passes AA; PillarMark honors reduce-motion; generic spinners replaced with the pillar brand loader; creation stepper themed to warm stone; dead font styles removed.
…~6.4 MB) Retire the react-native-asset pipeline: Android resolved Inter exclusively via res/font/inter.xml, so the assets/fonts copies were dead weight; fonts are now manually wired on both platforms (documented in assets/fonts/README.md) and a stray react-native-asset run can no longer reintroduce duplicates. Remove react-native-vector-icons entirely — zero imports remained after the Lucide migration; the gradle fonts hook, Info.plist icon-font entries and pbxproj references go with it. APK shrinks 169.9 -> 163.2 MB. Verified with fresh builds: Android assembleDebug + emulator install/launch with clean logcat; iOS simulator build + screenshot (Inter renders, bundle carries exactly the four Inter TTFs and no icon fonts). 386 tests green.
7 Dependabot alerts -> 2 accepted-risk (elliptic — no patched version exists; signing-adjacent, untouched). Fixed: ws family (ethers 6.17.0 upstream security release, deduping with the Schnorr SDK's copy, + scoped resolution for viem), uuid via jayson (scoped resolution, smoke-tested), concurrent-ruby build tooling. Safe JS minors (navigation, axios, i18next, toast); zero native-module bumps — pod install verified a byte-for-byte no-op; jest bump reverted (preset incompatibility). @noble/hashes stays 2.2.0 (wallet parity). 386 tests, Android assembleDebug, and a Hermes release bundle all green.
versionCode 60, iOS build 9, marketing version 2.0.0 on both platforms.
The seed-backup modal (the last v1 surface) is rebuilt around a shared SeedPhraseBackup component: high-severity RiskBanner instead of a red text wall, a numbered monospace word-chip grid masked until revealed, proper button hierarchy — shared by Create, Restore, and the key-details reveal. Success moments (sync, tx sent, EVM sign, nonces) share a SuccessHeader with the pillar mark and a green check; menu rows get icons, left-aligned labels, 44pt targets and menuitem roles; Delete SSP Key is styled as the destructive action it is; Scanner fixes a theme dispatch running in the render body, respects reduce-motion, and makes permission messages legible; Authentication and eye toggles become labeled touchables; sensitive values render monospace everywhere. Radii and dividers snapped to tokens; a11y labels across icon-only controls. Presentation only — reveal, decrypt and signing logic untouched.
The iOS LaunchScreen is rebranded to the v2 identity: the centered pillar mark on the brand-dark background (#131314, matching the app splash) replaces the white background and system-font 'SSPKey' text. Fixes a latent filename-case mismatch in the SplashIcon asset (iTunesArtwork vs ItunesArtwork) that would blank the splash on a case-sensitive build. Settings is redesigned from a flat modal into a polished grouped-card screen mirroring the wallet's v2 Menu: Preferences (language shown concisely as 'System' / native name, and a compact amber pill privacy toggle replacing the oversized native switch) and Security (change password; Delete SSP Key Data as the destructive bottom action) up front, the relay and per-chain network endpoints tucked into a collapsed Advanced expander with uppercase section labels and recessed fields identical in both themes, and an About card with the version and Powered by Flux. Consistent type scale, 44pt rows, aligned icons and chevrons throughout. Modal-preserving; every save/reset/delete/ change-password/language/privacy handler is byte-identical. 386 tests, tsc, lint, prettier green; every state verified on the iOS simulator through a design-critique loop (light + dark).
Mirrors the wallet's periodic seed-backup reminder, adapted for the Key: a routine 30-day checkup card on the Home idle screen — pure security framing, no balance (the Key shows none). It renders ONLY when a Key is set up (a seed exists) and is due, so it never appears on Welcome or during onboarding; verify resets the 30-day cycle, dismiss snoozes it. Create and Restore stamp the first verification so fresh users are not asked immediately. Verify runs the existing word-challenge behind the biometric/password gate over a read-only seed decrypt (reusing Authentication + ConfirmSeedWords + the key-details reveal path — no new crypto). Also adds an on-demand 'Verify seed backup' row in Settings -> Security. Pure cycle logic + storage are unit tested. 398 tests, tsc, lint, prettier green; verified on the iOS simulator (absent on Welcome, present on Home idle for a set-up Key).
…ling
- Drop the cross-chain duplicate-xpub rejection: all testnets share slip 1,
so Sepolia and Amoy (both evm/p2sh with default bip32 bytes) derive
byte-identical wallet xpubs — as do Testnet and Signet Bitcoin. Any batch
containing such a pair was rejected wholesale as 'duplicate_xpub'.
Duplicate CHAIN entries remain invalid.
- Post chainsyncrejected('invalid') for malformed requests instead of
silently dropping them — the wallet otherwise waits the full 30s fallback
timeout before offering per-chain QR sync. This wires up the previously
dead 'invalid' rejection-reason arm.
- Declining a batch left batchStartedRef stuck true for the session
(suppressing every later verification code) and, on a fresh pairing,
dropped the identity verification code entirely while the wallet shows
one. Decline now shows the identity code and resets the flag.
- CHAIN_SYNC_POST_SPACING_MS 1500 → 3000: the relay sync doc is
last-write-wins per walletIdentity and the wallet polls at 1s — 1.5s left
almost no margin for network jitter on already-derived chains, and a
missed doc is never re-posted. 3s gives the poll 2-3 chances per doc and
stays far inside the wallet's 60s stall window.
Round-3 security sweep — the SSP Key is the second factor whose approval screen is the last line of defense, so display must equal what is signed. - EVM vault: the Schnorr sign consumed rawUnsignedTx (an opaque hash the SDK's fromJson trusts and never recomputes) while the approval screen displayed a decode of a SEPARATE evmUserOp (tx) or signMessage (personal_sign). Nothing bound them, so a compromised wallet could show a benign operation and have the key co-sign a vault-draining one. New lib/userOpVerify.ts recomputes the UserOp hash (aa-core getUserOperationHash) or the EIP-191 message digest (ethers hashMessage) on-device from what was DISPLAYED and refuses to sign on mismatch — parity with the Solana vault's existing decode-what-you-sign hard-block. - UTXO vault: the key adds its SIGHASH signatures over walletSignedHex's outputs but the approval screen decoded rawUnsignedTx. Now asserts the two transactions have an identical output set (value + script) before signing, and fails closed when rawUnsignedTx is absent. - Consumer UTXO approval (decodeTransactionForApproval) overwrote the shown receiver/amount on every output, so a multi-output tx displayed only the LAST recipient and understated the total sent. It now sums all non-change recipients into the amount and reports recipientCount; the approval screen shows a warning when the tx pays more than one destination.
- Scan-to-verify forced persisted dark mode: Scanner dispatches darkMode:true on mount and only Home's scanner paths restored it — the theme slice is redux-persisted, so a light-theme user who tapped 'Scan wallet to verify' was stuck in dark mode across restarts. VerificationCode now restores the theme on scan and on close, and mounts the scanner full-screen. - 'Delete SSP Key data' also clears the encrypted sign-history blob (keychain service sspkey_sign_history) — the wipe contract says all data.
Password recovery - The released key comes from its own BIP-48 account, published with this device's signature on sync so the wallet can verify it independently. - Each request is answered once, is time-bounded, and names the key index it needs; the approval screen shows a six-word code matching SSP Wallet. Approval screens - Totals and recipient counts cover every output in a proposal. - Any decode failure fails closed, with Reject still reachable. - Slide-to-approve stretches correctly in every request wrapper. Sync - One nonce pool per batch chain sync; relay actions route identically over the socket and poll transports. Copy pass across the request, home and create/restore screens. Ignore regenerable screenshot artifacts.
Zcash NU6.3 consensus branch id support; ships together with the matching ssp-wallet bump.
Google Play + App Store copy refreshed to v2 messaging (2-of-2 story, Halborn audits, accurate chain list); field limits verified. Screenshot panels regenerate via the documented pipeline (fastlane/screenshots is gitignored per RN convention).
Member
Author
|
Since opening, this branch additionally picked up:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The complete v2 redesign of SSP Key on the shared SSP design system (amber tokens, bundled Inter with real weights on both platforms, Lucide icons, the pillar mark) plus major approval-UX and security additions. 9 commits, reviewed through multiple adversarial audit rounds.
Highlights
The approval moment
Pairing & security
Structure & platform
Verification
Before merge