Self-custodial mobile wallet for the DFX ecosystem. Multi-chain, hardware-wallet ready, built with React Native and the Tether Wallet Development Kit (WDK).
Status: Early development (
v0.1.0). APIs, flows and UI are still moving.
This matrix lists every user-facing function the app exposes, the technical components that implement it, whether it ships on by default, and the current test coverage. It is the source of truth for "what does this wallet actually do" — keep it in sync when adding or removing a flow.
MVP scope. The MVP is intentionally narrow: create a wallet, receive
tokens, send tokens. Everything else is deferred behind a build-time
EXPO_PUBLIC_ENABLE_* flag (off by default) and only ships when it has
dedicated test coverage. A function being marked deferred is a
triage decision, not a statement about code quality — most of these
flows already exist and work; they are simply held back until their
tests are written.
Code isolation. Every deferred feature lives in
src/features/<feature>/ and is loaded through a conditional
require() against a FEATURES.X constant from src/config/features.ts.
Expo's babel-preset-expo inlines process.env.EXPO_PUBLIC_* at build
time, so each FEATURES.X resolves to a boolean literal inside
features.ts itself.
At the call site (const Screen = FEATURES.X ? require('A') : require('B'))
Metro keeps both require() calls — the boolean is read at runtime
from a separate module, so the bundler cannot fold the ternary
across module boundaries. The effect:
- A build with the flag off never evaluates the deferred module's
top-level code (no
new BitboxProvider(), no service-singleton bootstrap, no fetch interceptors), because therequire()for the real implementation is unreachable. This is the crash- and exfiltration-isolation property the matrix relies on. - The deferred module's source does still ship in the JavaScript
bundle as an unevaluated factory. Surface-isolation (symbol absent
from the binary) is a stronger property and is a known limitation;
closing it requires a babel-plugin that folds
FEATURES.Xacross modules. Tracked separately.
Status legend
always— MVP function, wired up on every build, no flag to turn offdeferred (EXPO_PUBLIC_ENABLE_X)— off by default, opt-in via the named build-time flag; required to be 100% test-covered before the flag flips on by defaultenv— configuration variable (URL / API key / RPC override), not a feature switch; defaults are baked intosrc/config/
Test legend
full— unit + integration tests cover the happy path and at least one failure modepartial— only a helper, sub-service, or screen baseline is covered; the end-to-end flow has no dedicated testnone— no automated test references the code path
deferred rows are not required to be full today — the flag stays off
until they are. always rows must reach full for the MVP to ship.
| Function | Technical components | Status | Tests |
|---|---|---|---|
| Welcome / entry screen | app/(onboarding)/welcome.tsx, isPasskeySupported() gate |
always | partial (Detox visual baseline, Maestro 01-welcome.yaml) |
| Create wallet from generated seed | (onboarding)/create-wallet.tsx, services/wallet/seed.ts, WDK useWalletManager.initializeFromMnemonic |
always | full (test/services/seed.test.ts, Detox onboarding.test.ts, Maestro 10-onboarding-create.yaml) |
| Restore wallet from 12 / 24-word seed | (onboarding)/restore-wallet.tsx, validateSeedPhrase, WDK |
deferred (EXPO_PUBLIC_ENABLE_RESTORE) |
partial (seed unit tests + Detox + Maestro 11-onboarding-restore.yaml, no screen unit test) |
| Verify seed phrase (post-create quiz) | (onboarding)/verify-seed.tsx |
deferred (EXPO_PUBLIC_ENABLE_RESTORE) |
none |
| Create wallet via Passkey (WebAuthn PRF) | (onboarding)/create-passkey.tsx, features/passkey/services/passkey-service.ts, key-derivation.ts (HKDF → BIP-39) |
deferred (EXPO_PUBLIC_ENABLE_PASSKEY) — iOS 18+ / Android 14+ |
partial (passkey-service.test.ts, passkey-key-derivation.test.ts cover the PRF → HKDF → BIP-39 services; no screen test) |
| Restore wallet via Passkey | (onboarding)/restore-passkey.tsx, features/passkey/services/setup-wallet.ts |
deferred (EXPO_PUBLIC_ENABLE_PASSKEY) |
partial (passkey-setup-wallet.test.ts covers setup-wallet.ts; no screen test) |
| Legal disclaimer acceptance | (onboarding)/legal-disclaimer.tsx |
deferred (EXPO_PUBLIC_ENABLE_LEGAL) |
partial (Detox baseline only) |
| PIN setup | (onboarding)/setup-pin.tsx, services/pin.ts, useAuthStore.setPin |
deferred (EXPO_PUBLIC_ENABLE_PIN) |
full (test/store/auth.test.ts, Detox setup-pin/setup-pin-confirm baselines) |
| Function | Technical components | Status | Tests |
|---|---|---|---|
| PIN unlock (cold start, deep link, JS reload) | app/(pin)/verify.tsx, useAuthStore.verifyPin, WDK useWalletManager.unlock, MAX_ATTEMPTS = 5 |
deferred (EXPO_PUBLIC_ENABLE_PIN) |
full (test/store/auth.test.ts + Detox verify-pin* + Maestro 12-pin-unlock.yaml) |
| Biometric unlock (Face ID / Touch ID / fingerprint) | features/biometric/biometric.ts, expo-local-authentication, useAuthStore.authenticateBiometric, biometricEnabled flag |
deferred (EXPO_PUBLIC_ENABLE_BIOMETRIC) |
full (test/services/biometric.test.ts, test/store/auth.test.ts) |
Auth gate around every (auth)/* route |
app/(auth)/_layout.tsx, hard redirect to /(pin)/verify when isAuthenticated is false |
deferred (EXPO_PUBLIC_ENABLE_PIN) |
partial (covered indirectly via PIN-unlock E2E) |
| Function | Technical components | Status | Tests |
|---|---|---|---|
| Dashboard total balance | app/(auth)/(tabs)/dashboard.tsx, useTotalPortfolioFiat, services/balances/, pricing-service.ts |
always | partial (pricing-service.test.ts, test/store/wallet.test.ts; no hook/screen test) |
| Hide balance (eye toggle) | dashboard.tsx local state |
deferred (EXPO_PUBLIC_ENABLE_PORTFOLIO) |
none |
| Receive (BTC native / Lightning / EVM stables) | (auth)/receive/index.tsx, components/QrCode, WDK useAccount, useLdsWallet (LN address) |
always | none |
| Send native + ERC-20 | (auth)/send/index.tsx, useSendFlow, WDK sendFromAccount / estimateFee, components/QrScanner |
always | full (use-send-flow.test.tsx happy+failure paths, evm-address/evm-signature + property suites, Maestro 21-send.yaml) |
| Buy (DFX fiat on-ramp, SEPA) | (auth)/buy/index.tsx, useBuyFlow, features/dfx-backend/services/payment-service.ts, features/dfx-backend/services/asset-service.ts, features/dfx-backend/services/fiat-service.ts, DfxAuthGate |
deferred (EXPO_PUBLIC_ENABLE_BUY_SELL) |
partial (dfx-api.test.ts, dfx-auth-gate.test.ts, dfx-auth-service.test.ts; no flow test) |
| Sell (DFX off-ramp to IBAN) | (auth)/sell/index.tsx, useSellFlow, features/dfx-backend/services/payment-service.ts, DfxAuthGate |
deferred (EXPO_PUBLIC_ENABLE_BUY_SELL) |
partial (same DFX-API/auth tests; no flow test) |
| Portfolio (local + DFX-linked wallets) | (auth)/portfolio/index.tsx, services/balances/useBalances.ts, useEvmBalances, useWdkBalances, useLinkedWalletDiscovery, useLinkedWalletFiat |
deferred (EXPO_PUBLIC_ENABLE_PORTFOLIO) |
partial (btc-fetcher.test.ts, blockscout.test.ts, etherscan.test.ts, coingecko-*.test.ts, discoverable-tokens.test.ts, pricing-service.test.ts; no screen/hook test) |
| Asset detail screen | (auth)/portfolio/[symbol].tsx |
deferred (EXPO_PUBLIC_ENABLE_PORTFOLIO) |
none |
| Chain enable / disable | (auth)/portfolio/manage.tsx, useEnabledChains (MMKV-backed) |
deferred (EXPO_PUBLIC_ENABLE_PORTFOLIO) |
none |
| Linked wallet detail (per-address holdings + tx history) | (auth)/linked-wallet/[address].tsx, useLinkedWalletDiscovery, useWalletTransactions, useLinkedWalletNames, Blockscout + Etherscan + mempool.space |
deferred (EXPO_PUBLIC_ENABLE_LINKED_WALLETS) |
partial (explorer + discovery libs tested; no hook test) |
| Transaction history (local wallet, DFX-side) | (auth)/transaction-history/index.tsx, (auth)/transaction-history/[id].tsx, features/dfx-backend/services/transaction-service.ts |
deferred (EXPO_PUBLIC_ENABLE_TX_HISTORY) |
none |
| Pay (QR scan → payment URL) | (auth)/pay/index.tsx, (auth)/pay/opencryptopay.tsx, services/opencryptopay/, expo-camera CameraView |
deferred (EXPO_PUBLIC_ENABLE_PAY) — OpenCryptoPay QRs route to a quote screen (signing is a follow-up); other QRs still show the coming-soon alert |
partial (opencryptopay-lnurl.test.ts, opencryptopay-service.test.ts, OpenCryptoPayScreen.test.tsx) |
| Function | Technical components | Status | Tests |
|---|---|---|---|
| DFX silent sign-in (wallet → JWT) | useDfxAuth, features/dfx-backend/services/auth-service.ts, features/dfx-backend/services/jwt.ts, features/dfx-backend/services/api.ts 401-retry hook |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
full (dfx-auth-service.test.ts, dfx-jwt.test.ts, dfx-api.test.ts, dfx-auth-gate.test.ts) |
| DFX e-mail login / account merge | (auth)/dfx-login/index.tsx, dfxAuthService, /v1/auth/mail, JWT account claim diff |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
partial (auth-service tested; merge flow not) |
| Auto-link BTC / EVM / Lightning to DFX account | useDfxAutoLink, DFX_LINKED_CHAINS cache in secure store, useLdsWallet |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
none |
| DFX-linked wallets management (list / rename / hide / link extra address) | (auth)/wallets/index.tsx, features/dfx-backend/services/user-service.ts, useLinkedWalletNames, useLinkedWalletSelection, useLinkedWalletReauth |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
none |
| KYC multi-step wizard (Email → Personal → Nationality → Financial → 2FA → Sumsub/IDnow) | (auth)/kyc/index.tsx, useKycFlow, features/dfx-backend/services/kyc-service.ts, (auth)/webview.tsx, services/security/safe-url.ts |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
partial (only dfx-kyc-service.test.ts → registerEmail; wizard untested) |
| User data summary (email, phone) | (auth)/email/index.tsx, features/dfx-backend/services/user-service.ts /v2/user |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
none |
| Support tickets (list / create / chat) | (auth)/support/index.tsx, features/dfx-backend/services/support-service.ts |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
none |
| Contact channels (mailto, tel, web, in-app support) | (auth)/contact/index.tsx, Linking, services/security/safe-url.ts |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
none |
| Tax report CSV export (CoinTracking et al.) | (auth)/tax-report/index.tsx, features/dfx-backend/services/transaction-service.ts (requestCsv), expo-file-system, expo-sharing (soft import) |
deferred (EXPO_PUBLIC_ENABLE_TAX_REPORT) |
none |
| Legal documents launcher (T&C, Privacy, Disclaimer) | (auth)/legal/index.tsx, OS browser hand-off via Linking, safe-url allow-list |
deferred (EXPO_PUBLIC_ENABLE_LEGAL) |
none |
| Function | Technical components | Status | Tests |
|---|---|---|---|
| Language switcher (DE ↔ EN) | (auth)/(tabs)/settings.tsx, i18next, dfxUserService.updateUser sync |
deferred (EXPO_PUBLIC_ENABLE_SETTINGS) |
none |
| Display currency (CHF / EUR / USD) | settings.tsx, useWalletStore.setSelectedCurrency, persisted in secure store, dfxUserService.updateUser sync |
deferred (EXPO_PUBLIC_ENABLE_SETTINGS) |
full (test/store/wallet.test.ts) |
| Biometric unlock toggle | settings.tsx, useAuthStore.setBiometricEnabled |
deferred (EXPO_PUBLIC_ENABLE_BIOMETRIC) |
full (covered under "Biometric unlock" above) |
| Seed phrase export | (auth)/seed-export.tsx, WDK useWalletManager, passkey re-auth for passkey-origin wallets, expo-screen-capture (soft import) |
deferred (EXPO_PUBLIC_ENABLE_SETTINGS) |
partial (seed.test.ts covers the lib; screen untested) |
| Hardware wallet pairing (BitBox02) | (auth)/hardware-connect/index.tsx, features/hardware-wallet/services/bitbox.ts, transport-usb.ts (Android only), transport-ble.ts, BitboxWasmWebView.tsx, wasm-bridge.ts, bitbox-protocol.ts |
deferred (EXPO_PUBLIC_ENABLE_HARDWARE_WALLET) |
full (wasm-bridge, bitbox-provider cross-layer vs. behavioral fake, transport-usb/transport-ble, bitbox-wasm-html suites; firmware side via bitbox-simulator.yml) |
| Multi-sig vault setup (local cosigner list) | (auth)/multi-sig/index.tsx, (auth)/multi-sig/setup.tsx, useMultiSigStore |
deferred (EXPO_PUBLIC_ENABLE_MULTISIG) — Zustand store only, no on-chain signing yet |
none |
| Delete wallet | settings.tsx, WDK deleteWallet, useAuthStore.reset, secure-store wipe |
deferred (EXPO_PUBLIC_ENABLE_SETTINGS) |
partial (auth.test.ts covers reset; flow itself untested) |
| Function | Technical components | Status | Tests |
|---|---|---|---|
| PIN bruteforce limit (5 attempts → reset to onboarding) | app/(pin)/verify.tsx MAX_ATTEMPTS = 5 |
deferred (EXPO_PUBLIC_ENABLE_PIN) |
partial (Detox verify-pin-error baseline; counter logic untested) |
| Screen-capture protection on seed screen | (auth)/seed-export.tsx soft import of expo-screen-capture |
deferred (EXPO_PUBLIC_ENABLE_SETTINGS) |
none |
Deep-link payment URL handling (dfxwallet://buy, sell, send?to=...) |
useDeepLink, expo-linking |
deferred (EXPO_PUBLIC_ENABLE_DEEPLINKS) |
none |
| WebView host allow-list (KYC, legal, payment hand-off) | (auth)/webview.tsx, services/security/safe-url.ts (isAllowedDfxHost, isDfxOwnedHost, isSafeHttpsUrl) |
deferred (EXPO_PUBLIC_ENABLE_WEBVIEW) |
partial (security-safe-url.test.ts: 126 adversarial allowlist tests; WebView screen untested) |
| Offline banner | components/OfflineBanner.tsx, @react-native-community/netinfo |
always | none |
| Global error boundary | components/ErrorBoundary.tsx, mounted in app/_layout.tsx |
always | none |
| DFX session 401 silent retry | features/dfx-backend/services/api.ts setOnUnauthorized, useDfxAuth.authenticateSilent |
deferred (EXPO_PUBLIC_ENABLE_DFX_BACKEND) |
full (dfx-api.test.ts, dfx-auth-service.test.ts) |
Every deferred row in the matrix above is gated by one of these
build-time flags. They are off by default. A flag flips to "on by
default" only once every function it gates is full in the test
matrix. Each flag isolates the code path so that a build with the flag
off can never load, parse, or execute the deferred module — the goal
is both crash-safety (broken WIP code can't take the MVP down) and
defense-in-depth (a compromised dependency in a non-MVP path can't
reach the MVP runtime).
| Flag | Gated functions | Default |
|---|---|---|
EXPO_PUBLIC_ENABLE_RESTORE |
Restore wallet from seed, Verify seed phrase | off |
EXPO_PUBLIC_ENABLE_PASSKEY |
Create + Restore wallet via Passkey | off |
EXPO_PUBLIC_ENABLE_LEGAL |
Legal disclaimer acceptance, Legal documents launcher | off |
EXPO_PUBLIC_ENABLE_PIN |
PIN setup, PIN unlock, Auth gate around (auth)/*, PIN bruteforce limit |
off |
EXPO_PUBLIC_ENABLE_BIOMETRIC |
Biometric unlock, Biometric toggle in Settings | off |
EXPO_PUBLIC_ENABLE_PORTFOLIO |
Hide-balance toggle, Portfolio screen, Asset detail, Chain enable/disable | off |
EXPO_PUBLIC_ENABLE_BUY_SELL |
Buy (fiat on-ramp), Sell (off-ramp) | off |
EXPO_PUBLIC_ENABLE_LINKED_WALLETS |
Linked wallet detail screen | off |
EXPO_PUBLIC_ENABLE_TX_HISTORY |
Transaction history | off |
EXPO_PUBLIC_ENABLE_PAY |
Pay (QR scan placeholder) | off |
EXPO_PUBLIC_ENABLE_DFX_BACKEND |
DFX silent sign-in, e-mail login, auto-link, DFX-linked wallets management, KYC, user data summary, support tickets, contact channels, DFX session 401 silent retry | off |
EXPO_PUBLIC_ENABLE_TAX_REPORT |
Tax report CSV export | off |
EXPO_PUBLIC_ENABLE_SETTINGS |
Settings screen and the entries it owns: language switcher, display currency, seed phrase export, delete wallet, screen-capture protection | off |
EXPO_PUBLIC_ENABLE_HARDWARE_WALLET |
BitBox02 pairing (USB + BLE + WASM bridge) | off |
EXPO_PUBLIC_ENABLE_MULTISIG |
Multi-sig vault setup (local cosigner list) | off |
EXPO_PUBLIC_ENABLE_DEEPLINKS |
Deep-link payment URL handler | off |
EXPO_PUBLIC_ENABLE_WEBVIEW |
In-app WebView (KYC iframe, payment hand-off) and the safe-url allow-list |
off |
These variables are configuration, not feature switches: they override
URLs, RPC providers, or API keys. Defaults are baked into
src/config/chains.ts / src/config/env.ts so the app runs with none of
them set.
| Variable | Effect | Default | Tests |
|---|---|---|---|
EXPO_PUBLIC_DFX_API_URL |
DFX backend base URL (auth, buy/sell, KYC, support, tx history) | https://api.dfx.swiss |
full (covered by every dfx-* service test) |
EXPO_PUBLIC_LDS_URL |
Lightning Data Service base URL (LN address resolution) | https://lightning.space/v1 |
none |
EXPO_PUBLIC_ETHERSCAN_API_KEY |
Enables Etherscan V2 transaction feed for linked-wallet history; without it falls back to Blockscout / curated tokens | empty | full (test/services/etherscan.test.ts exercises both paths) |
EXPO_PUBLIC_ETH_RPC_URL |
Custom Ethereum RPC (Alchemy / Infura / keyed provider) | https://ethereum-rpc.publicnode.com |
none |
EXPO_PUBLIC_ARBITRUM_RPC_URL |
Custom Arbitrum RPC | https://arbitrum-one-rpc.publicnode.com |
none |
EXPO_PUBLIC_POLYGON_RPC_URL |
Custom Polygon RPC | https://polygon-bor-rpc.publicnode.com |
none |
EXPO_PUBLIC_BASE_RPC_URL |
Custom Base RPC | https://base-rpc.publicnode.com |
none |
EXPO_PUBLIC_PLASMA_RPC_URL |
Custom Plasma RPC | https://rpc.plasma.to |
none |
EXPO_PUBLIC_SEPOLIA_RPC_URL |
Custom Sepolia (testnet) RPC | https://sepolia.gateway.tenderly.co |
none |
EXPO_PUBLIC_BTC_ELECTRUM_HOST / EXPO_PUBLIC_BTC_ELECTRUM_PORT |
Custom Electrum server for the BTC WDK provider | electrum.blockstream.info:50001 |
none |
EXPO_PUBLIC_WDK_INDEXER_URL / EXPO_PUBLIC_WDK_INDEXER_API_KEY |
WDK indexer for richer balance + transaction queries | unset (RPC-only mode) | none |
Production: Bitcoin on-chain, Spark / Lightning, Ethereum, Arbitrum,
Polygon, Base, Plasma. Testnet: Sepolia. Each EVM chain ships with an
ERC-4337 paymaster (Candide) so users can pay gas in USDT / USDC where
available. The active set lives in wdk.config.js; the worklet bundle
is regenerated by npm run bundle:wdk (also runs in postinstall).
| Area | Choice |
|---|---|
| Framework | React Native (Expo ~54, New Architecture) |
| Routing | Expo Router (file-based) |
| Language | TypeScript (strict) |
| Wallet SDK | @tetherto/wdk-react-native-provider |
| Hardware wallet | bitbox-api (WASM) + react-native-ble-plx |
| State | Zustand |
| Storage | react-native-mmkv (fast KV), expo-secure-store (secrets) |
| i18n | i18next + react-i18next |
- Node.js 20+
- npm 10+
- Xcode 16+ with iOS 15.1 SDK (for iOS builds)
- Android Studio with SDK 35 / min SDK 29 (for Android builds)
- CocoaPods (installed automatically by Expo prebuild)
# install dependencies (patches and WDK worklet bundling run automatically)
npm install
# run on iOS simulator
npm run ios
# run on Android emulator / device
npm run android
# Metro dev server only
npm startThe first iOS / Android run regenerates the native projects via expo prebuild if needed:
npm run prebuild| Script | Description |
|---|---|
npm run ios / npm run android |
Build and launch on a simulator / emulator. |
npm start |
Start the Expo dev server. |
npm run prebuild |
Regenerate ios/ and android/ from the Expo config. |
npm run typecheck |
TypeScript (tsc --noEmit). |
npm run lint / npm run lint:fix |
ESLint. |
npm run format / npm run format:fix |
Prettier. |
npm run test |
Jest. |
npm run check |
typecheck + lint + format (run before pushing). |
npm run bundle:bitbox |
Rebuild the bundled BitBox WASM module. |
Coverage is scoped to the logic surface (collectCoverageFrom in
jest.config.js: services, stores, feature services, hooks) — forcing line
coverage on JSX produces render tests, not safety. Screens and UI flows are
covered behaviourally instead (component tests, visual regression, Maestro).
Two gates run in CI on top of that scope (job coverage-floor):
- Aggregate floor —
.coverage-floor-linesis the minimum overall scoped line coverage (ratchet: raise it in the same commit when coverage climbs; lowering needs thecoverage:lower-floorlabel). - Per-file Tier-A floors —
.coverage-floors.jsonpins each security-critical file (key derivation, seed/wallet setup, signing, auth, secure storage, biometric, hardware-wallet bridge) to its own minimum (100). The aggregate floor alone would let one such file rot to zero behind a high mean; this catches that. Enforced byscripts/check-coverage-file-floors.mjs.
app/ # Expo Router screens (file-based routing)
(onboarding)/ # Welcome, create / restore, verify seed, PIN setup
(pin)/ # PIN unlock
(auth)/ # Authenticated area
(tabs)/ # Bottom tabs: Dashboard, Settings
buy/ sell/ send/ receive/ # Wallet action flows
hardware-connect/ # BitBox02 pairing (USB / BLE)
kyc/ # Multi-step KYC verification
support/ # Ticket system
transaction-history/
src/
components/ # Shared UI components
config/ # Chain configs, environment
hooks/ # Custom React hooks
i18n/ # Localization (de, en)
services/
dfx/ # DFX backend client + DTOs
hardware-wallet/ # BitBox02 transport + provider
passkey/ # WebAuthn PRF wallet creation
store/ # Zustand stores
theme/ # Colors, typography
modules/ # Native modules (Android USB-HID, etc.)
plugins/ # Expo config plugins
patches/ # patch-package patches
The wallet uses bitbox-api (WASM) for the protocol stack — Noise XX handshake, Protobuf framing and signing are transport-agnostic. Only the byte-level read/write transport is platform-specific:
- USB HID — Android only (Apple does not allow third-party apps to talk to USB-HID devices).
- BLE — Android + iOS, for the BitBox02 Nova.
The wallet operates in a view-only model when paired: no seed is stored on device, signing is delegated to the hardware.
Signing is supported for:
- BTC: SegWit, Taproot, PSBT
- ETH: EIP-1559, ERC-20, EIP-712
-
Every change goes on a feature branch with a pull request against
develop. Do not push directly todevelopormain. -
Always start from the latest
develop:git checkout develop && git pull origin develop git checkout -b feat/your-feature # or fix/..., docs/..., chore/...
-
develop→mainis handled by an automated release PR.
Before pushing, run:
npm run checkIssues and pull requests are welcome. Please:
- Open an issue first for non-trivial changes.
- Keep PRs focused — one feature / fix per PR.
- Run
npm run checkand make sure tests pass. - Update
de.jsonanden.jsontogether when touching i18n.
Found a vulnerability? Please follow SECURITY.md — do
not open a public issue. The threat model and security roadmap live under
docs/security/.