Release: develop -> main - #13
Merged
Merged
Conversation
…baseline Spell out develop-based branch workflow in CLAUDE.md
* chore(eslint): enable no-floating-promises, security, no-secrets, strict no-console - @typescript-eslint/no-floating-promises (typed) with ignoreVoid - eslint-plugin-security recommended set as error (via @eslint/compat) - eslint-plugin-no-secrets at tolerance 4.5 - no-console now allows console.error/warn only - test files: no-console + detect-object-injection off * fix: resolve no-floating-promises violations Mark fire-and-forget promise calls (Haptics, Linking, secure storage, i18n init, BLE scan helpers) with explicit `void` so the rule passes without ignoring legitimate async work. * fix: silence security/detect-object-injection on type-safe lookups All flagged sites are bracket accesses against `Record<Literal, …>` maps with keys constrained to a literal union, or numeric array indices already bounded by length. Each disable carries a one-line reason. Also disables security/detect-non-literal-fs-filename for the withSwiftConcurrency Expo plugin, where the path is built from config.modRequest, not user input. * fix(auth): make setOnboarded async and surface persistence errors - setOnboarded now returns Promise<void> and awaits secureStorage.set so a write failure rejects instead of silently desyncing the in-memory state from disk. - completeSetup wraps setPin/setOnboarded in try/catch, resets the PIN flow back to 'create' and surfaces a 'PIN konnte nicht gespeichert werden' error via the new pin.saveError i18n key. - checkPin treats a verifyPin rejection like a wrong PIN (Haptics + error state + attempt counter) instead of leaking an unhandled rejection. * fix(deeplink): handle getInitialURL rejection with .catch Previously `void Linking.getInitialURL().then(...)` only swallowed the return value of the promise chain; a rejection inside the .then handler would still escape as an unhandled rejection (potential RN crash). .catch logs via console.warn and stops the rejection from bubbling.
* Add passkey-based wallet creation with WebAuthn PRF extension Implement seedless wallet onboarding using passkeys as an alternative to manual seed phrase backup. The passkey's PRF extension derives deterministic entropy (HKDF-SHA256) that generates a standard BIP-39 mnemonic, fully compatible with the existing WDK wallet flow. New files: - src/services/passkey/ — passkey service (create/authenticate) and PRF-to-mnemonic key derivation (HKDF → 128-bit → 12-word BIP-39) - app/(onboarding)/create-passkey.tsx — passkey creation screen - app/(onboarding)/restore-passkey.tsx — passkey restore screen Changes: - Welcome screen: add "Create with Passkey" button (shown only when device supports PRF), expandable restore options - Settings: passkey-aware delete wallet warning - Storage: add WALLET_ORIGIN and PASSKEY_CREDENTIAL_ID keys - Auth store: clean up passkey keys on wallet reset - app.json: iOS 18.0 minimum, Android API 34, associated domains - i18n: full DE + EN translations for passkey namespace Dependencies: react-native-passkey@3.3.3, @noble/hashes@2.2.0 rpId: dfx.swiss (requires .well-known files on landing page) * Add technical design document for passkey wallet architecture Comprehensive documentation covering: - Cryptographic flow (PRF → HKDF → BIP-39 → WDK) - Design rationale (PRF vs Smart Contract Wallets) - WebAuthn configuration (rpId, user identity, authenticator selection) - Platform requirements (iOS 18+, Android 14+) - Associated Domains setup (apple-app-site-association, assetlinks.json) - Security analysis (threat model, PRF properties, known limitations) - Recovery matrix for all loss scenarios - Comparison with Breez SDK approach - Future considerations (multi-passkey, HKDF info versioning)
Add void operator to fire-and-forget Haptics calls and secureStorage.get() in useEffect to satisfy the stricter @typescript-eslint/no-floating-promises rule from the updated ESLint config.
…ng rejections (#15) Both screens awaited `secureStorage.set` + `createWallet` without a catch — a rejection would escape via the onPress handler as an unhandled rejection. - Wrap the calls in try/catch with a finally that clears the loading flag - Add isCreating/isRestoring state to disable the button + show the PrimaryButton spinner while the async work runs - Surface failures via the new onboarding.createError / onboarding.restoreError i18n keys (DE + EN, alphabetically sorted)
Establishes a single engineering source of truth for security and quality under docs/security/: - README.md indexes the directory and explains how to use it. - threat-model.md catalogues assets, attacker profiles, attack surfaces, and the current mitigation status per item. - roadmap.md lays out the work in four phases: P0 foundation (lint/strict TS/CI hardening/SECURITY.md), P1 mobile hardening (screen capture, PIN bruteforce, biometric re-auth, deep links, cert pinning, etc.), P2 crypto + hardware-wallet audit prep, P3 external audit and bounty. Each task has acceptance criteria so it can be lifted into a GitHub issue. P0.1 (lint security ruleset) is already done via #12; this commit records the rest of the path.
Closes the P0.7 item from the security roadmap: a public, GitHub-recognised security policy at the repo root. Covers: - where to report (security@dfx.swiss) and what to include - response timeline (acknowledgement < 72h, triage < 7 days, weekly updates, disclosure after fix or 90 days) - in-scope (this app, WDK integration, BitBox flow, DFX API integration from the wallet side) and out-of-scope (DFX backend, third-party KYC vendor surfaces, upstream deps without a wallet-side amplifier, DoS, social engineering) - safe-harbor language for good-faith research - pointer to the threat model and roadmap Also adds a Security section to README.md so the policy is discoverable from the front page.
…bot alerts (#20) Pinned through npm overrides because both packages are pulled in as build-time-only transitive dependencies of expo's iOS prebuild tooling (xcode, @expo/plist, simple-plist): uuid 7.0.3 -> 14.0.0 (GHSA-w5hq-g745-h8pq, medium) @xmldom/xmldom 0.8.12 -> 0.8.13 (4x high, all fixed in 0.8.13) xcode@3.0.1 only calls uuid.v4(), which has stable signature across v7 and v14 — verified by reading node_modules/xcode/lib/pbxProject.js and running uuid.v4() against v14 in isolation. Existing sodium-native override kept untouched.
* Harden passkey wallet security: no persisted mnemonic, seed export, derivation versioning - Remove ENCRYPTED_SEED persistence for passkey wallets — mnemonic is only passed transiently to createWallet() and never stored on disk - Add seed export screen (Settings → Recovery Phrase) with passkey re-authentication to derive mnemonic on demand - Add PASSKEY_DERIVATION_VERSION to SecureStore and version-aware PARAMS_BY_VERSION map in key-derivation for future migration paths - Seed-based wallets remain unchanged (still persist in ENCRYPTED_SEED) * Fix lint errors for eslint-security-rules compatibility - Add void operator to fire-and-forget Haptics/storage promises - Use Map instead of Record for PARAMS_BY_VERSION (detect-object-injection) * Format seed-export.tsx with prettier * Use DERIVATION_VERSION constant instead of hardcoded string, add seed-read error state
PR #22 shipped SECURITY.md and the README link, satisfying P0.7. Update both docs to reflect the new state so the engineering view stays consistent with reality.
Closes the remaining items of P0.5 in the security roadmap: - Dependabot enabled for npm + github-actions, weekly Mondays, target develop, with a 5-PR cap so we are not flooded. - CodeQL workflow for javascript-typescript with the security-and-quality query suite, on push/PR to main+develop and weekly schedule. - npm audit --audit-level=high as a separate non-blocking job in the existing CI workflow (continue-on-error). The first cleanup pass (#20: uuid + @xmldom/xmldom overrides) is already merged; once the remaining alerts are cleared and we've had a quiet stretch, we flip this to blocking. Action versions stay on floating tags here to match the existing ci.yml. P0.4 will pin every uses: in one pass, so we avoid a half-pinned intermediate state.
Move duplicated storage/wallet initialization logic from create-passkey and restore-passkey into a single helper function. Both screens now call setupPasskeyWallet(prfOutput, credentialId, createWallet).
The 2.x was added for passkey HKDF but the same APIs (hkdf, sha256) exist in 1.x. This eliminates the duplicate: bip39 and @scure/bip39 already depend on 1.8.0, so npm now deduplicates to a single copy.
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v4...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Reorder setupPasskeyWallet so that createWallet() executes first. If it fails, no orphaned storage keys (WALLET_ORIGIN, CREDENTIAL_ID, DERIVATION_VERSION) are left behind.
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [prettier](https://github.com/prettier/prettier) from 3.8.2 to 3.8.3. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](prettier/prettier@3.8.2...3.8.3) --- updated-dependencies: - dependency-name: prettier dependency-version: 3.8.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [react-native-svg](https://github.com/react-native-community/react-native-svg) from 15.12.1 to 15.15.4. - [Release notes](https://github.com/react-native-community/react-native-svg/releases) - [Commits](software-mansion/react-native-svg@v15.12.1...v15.15.4) --- updated-dependencies: - dependency-name: react-native-svg dependency-version: 15.15.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Combine the library's platform authenticator capability check with the existing OS version gate. This catches devices where the OS version is sufficient but the authenticator is unavailable.
…salt (#41) - Use unique user.name (wallet-<userId>) instead of duplicating displayName - Add RS256 (alg: -257) as fallback alongside ES256 for broader authenticator support - Cache getPrfSalt() result since SHA-256 of a static string is deterministic
Show "Seed Phrase" for seed-based wallets and "Recovery Phrase" for passkey wallets in both settings menu and seed export screen title.
…0.4) (#42) - Pin every `uses:` in `.github/workflows/` to a commit SHA with a trailing `# vX.Y.Z` comment so a casual reader still sees the version. Touched: ci.yml, codeql.yml, auto-release-pr.yaml. - Add `permissions: contents: read` at the workflow root of ci.yml. codeql.yml and auto-release-pr.yaml already had a workflow-level block with per-job escalation; left untouched. - Split the monolithic `check` job into parallel `typecheck`, `lint`, `format` and `test` jobs. The `audit` job stays as is. Side effect: `npm test` now runs in CI for the first time. Existing suite (5 files, 26 tests) passes locally. Roadmap and threat model updated to reflect the new state.
* fix(pin): handle biometric auth rejection and log PIN-verify failures `tryBiometric` previously let an `authenticateBiometric` rejection escape as an unhandled rejection. Wrap it in try/catch and log via `console.warn` (allowed by no-console rule). `checkPin` already had a try/catch but swallowed the error silently — add the same warn so a failing verifyPin call can be diagnosed. * chore(i18n): migrate restore-wallet invalid-seed message to i18n Replace the hardcoded English string in `restore-wallet.tsx` with a new `onboarding.invalidSeed` key, alphabetically sorted in both locale files. * chore(onboarding): log catch errors in create/restore/PIN flows Mirrors the `useDeepLink` pattern: each catch block now does a `console.warn(<screen>: <what failed>, err)` so that a wallet creation, restore, passkey or PIN-setup failure is debuggable when a support ticket comes in. The user-facing error UI is unchanged. * test(auth): add unit tests for useAuthStore PIN, hydrate, reset paths 15 tests covering: - initial state shape - setAuthenticated / setDfxAuthenticated as pure setters - setOnboarded happy path + propagating secureStorage rejection - setPin: deterministic hashing + propagating storage errors - verifyPin: no-hash / correct / incorrect cases - hydrate: full happy path, all-absent, isOnboarded='false' string - reset: removes all storage keys and clears state Biometric paths (authenticateBiometric, setBiometricEnabled) are not covered yet — they need a richer expo-local-authentication mock; out of scope for this PR.
…s, noImplicitOverride (P0.2) (#45) Closes P0.2 from the security roadmap. The three new compiler flags surface classes of bugs that strict mode alone misses: - `noUncheckedIndexedAccess` — array/object lookups now return `T | undefined`. - `exactOptionalPropertyTypes` — `prop?: T` no longer accepts `prop: undefined`. - `noImplicitOverride` — class members shadowing a base class member must say so. Eleven errors surfaced, fixed without loosening the flags or introducing `as any`: - ErrorBoundary: `override` keyword on `state`, `componentDidCatch`, `render`. - welcome.tsx: conditional spread for the optional `variant` prop instead of `variant={cond ? 'outlined' : undefined}`. - dfx/api.ts: same pattern for the optional `body` field of fetch init. - dfx/auth-service.ts: same pattern for the optional `blockchain`/`usedRef` fields of the auth request. - send/index.tsx: `String.split('?')[0]!` — split always yields ≥1 element. - bitbox-protocol.ts: `sig.v[0]!` — recovery byte is always exactly one byte. - verify-seed.tsx: `!` on `verifyIndices[currentStep]`, `seedWords[currentIndex]`, and the random-word lookup. Each has a comment explaining why the bound is statically guaranteed.
* test(auth): add expo-local-authentication mock and biometric coverage Replace the empty `expo-local-authentication` mock with a real one that exposes `hasHardwareAsync`, `isEnrolledAsync`, `supportedAuthenticationTypesAsync`, `authenticateAsync` and the `AuthenticationType` enum. Adds two test suites: - `test/services/biometric.test.ts` (12 cases) covering `isBiometricAvailable` (hardware + enrolment matrix), `getBiometricType` (priority face > fingerprint > iris > none), and `authenticateWithBiometric` (success, failure, prompt forwarding). - New cases in `test/store/auth.test.ts` (8 cases) covering `useAuthStore.authenticateBiometric` (disabled / no-hardware / not-enrolled / success / cancelled) and `setBiometricEnabled` (enable+available, enable+unavailable no-op, disable, storage rejection) Also restructures `jest.config.js` into two projects so unit and component suites can run with different module resolution: `unit` keeps the `react-native: empty` mock used by service/store tests, while `components` switches to the `jest-expo` preset for real RN rendering. * test(components): add UI component tests for PrimaryButton and AssetListItem First component-level tests using the new `components` jest project (jest-expo preset + @testing-library/react-native). - `PrimaryButton`: renders title, propagates onPress, blocks press when disabled or loading, swaps title for ActivityIndicator while loading, supports the outlined variant - `AssetListItem`: renders name/balance/fiat, maps known chain ids to their human label, falls back to the raw chain id for unknown chains, uses the first two letters of the symbol as the icon glyph, propagates onPress when handler is provided, is disabled when no handler is given
* docs(security): mark P0.6 branch protection as done Branch protection is configured as repository rulesets (modern UI). The classic branch-protection API still reports both branches as "unprotected" — that is expected, the new ruleset system is the source of truth here. Active rules per branch: - develop: PR required, 1 approval, squash-only, status checks (typecheck/lint/format/test + CodeQL Analyze) required, no force push, no deletion, no admin bypass. - main: same plus 2 approvals and plain merge commits (preserves the develop history on release). Signed commits left as "encouraged" / opt-in for now, matching the roadmap wording. Will flip to required once everyone has GPG or SSH signing configured. * docs(security): clarify develop has 0 approvals on purpose The wallet repo uses an asymmetric branch-protection setup: develop takes PRs without approval so merges land fast and integration testing happens on develop itself; the human review gate sits in front of the develop → main release PR (2 approvals), where it actually matters. Match the recorded settings to the live ruleset and explain the rationale so a future maintainer doesn't "fix" develop to require an approval and create a backlog of stalled PRs.
Update react, react-dom, @types/react, and react-test-renderer together to avoid version mismatch in test suite.
Bumps [react-native-get-random-values](https://github.com/LinusU/react-native-get-random-values) from 1.11.0 to 2.0.0. - [Release notes](https://github.com/LinusU/react-native-get-random-values/releases) - [Commits](LinusU/react-native-get-random-values@v1.11.0...v2.0.0) --- updated-dependencies: - dependency-name: react-native-get-random-values dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- Add `.maestro/01-app-launch.yaml` smoke flow that asserts the welcome screen renders (DE/EN texts). - Document setup, conventions and limitations in `docs/maestro.md`. - Wire `e2e:maestro`, `e2e:maestro:ios`, `e2e:maestro:android` scripts into `package.json`. - Add `.github/workflows/maestro-e2e.yml` running iOS Simulator and Android Emulator jobs on `workflow_dispatch`.
… function or class (#50) Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
- Replace text-regex smoke flow with id-based `01-welcome.yaml`.
- Add `10-onboarding-create.yaml` covering welcome → reveal seed → setup PIN (create + confirm) → legal → dashboard.
- Add `11-onboarding-restore.yaml` covering welcome → restore-toggle → seed input (`${TEST_MNEMONIC}` env) → setup PIN → legal → dashboard.
- Add `testID` props to `ScreenContainer`, `PrimaryButton`, `ActionBar` so flows can target containers and primary actions consistently.
- Stamp screen IDs on welcome, create-wallet, restore-wallet, setup-pin (dynamic per step), legal-disclaimer, dashboard.
- Update `docs/maestro.md`: convention switched to `<screen>-<element>` (id-based, i18n-proof), added IDB requirement and Face-ID-off tip, refreshed flow list.
- New `12-pin-unlock.yaml`: onboards a fresh wallet, cold-restarts the app to land on the verify-PIN screen, exercises the wrong-PIN error path, then unlocks with the correct PIN to reach the dashboard.
- testIDs on `verify.tsx`: container `verify-pin-screen`, numpad `pin-key-{digit|del}` (same convention as setup-pin), `verify-pin-biometric-button` for the biometric fallback.
- Onboarding steps are duplicated inline rather than factored into a sub-flow; once more post-onboarding flows land we'll consolidate into `.maestro/_lib/`.
* WDK migration, bare-pack fix, and CI/audit hardening Migration to the new Tether WDK packages, plus the supporting infra changes needed to make a clean install + CI work. Maestro flows and testIDs from this branch are now superseded by upstream PRs #49 and WDK migration ============= * Switch from @tetherto/wdk-react-native-provider to @tetherto/wdk-react-native-core (pinned by commit) and the related pricing-bitfinex-http / pricing-provider / pear-wrk-wdk packages. Updated dashboard, receive, useDfxAuth, useSendFlow, seed.ts, and store/wallet to the new useWalletManager / useBalancesForWallet hook surface. * Rework src/config/chains.ts and add src/config/tokens.ts to expose the new network/token shape expected by the core hooks. * Replace the legacy src/services/wallet/wallet-service.ts with a thin pricing-service.ts and let the WDK manage wallet state. * Wire passkey create/restore screens to useWalletManager().initializeFromMnemonic via a shared setupPasskeyWallet helper. Build / install =============== * Add scripts/ensure-bare-pack.js (preinstall) so repeat installs pin bare-pack@1 on PATH. The pinned @tetherto/pear-wrk-wdk prepare runs `npx bare-pack --target ...` and bare-pack@2 removed --target. * Add plugins/withAndroidSubprojects.js Expo config plugin so the bare runtime's Android subprojects link. * Override axios to ^1.15.0 to clear GHSA-43fc-jf86-j433 (high) pulled in via @gelatonetwork/relay-sdk and @tetherto/wdk-pricing-bitfinex-http. CI == * Drop --ignore-scripts from typecheck/lint jobs so wdk-react-native-core's `prepare` builds dist/ (needed by tsc and ESLint's import resolver). * Add an explicit `npm install -g bare-pack@^1` step before `npm ci` in the same jobs, since npm prepares git-deps before running the host's preinstall. * Suppress security/detect-object-injection on typed-literal bracket indexing in sell/index.tsx and pricing-service.ts. Tests ===== * Stub expo-crypto.getRandomBytes/getRandomBytesAsync for generateSeedPhrase unit tests now that seed.ts uses the RN-friendly RNG. Docs / tooling ============== * Add docs/wdk-compliance-audit.md (audit of our usage vs. the current WDK docs; identifies the 6-step worklet-bundler migration we still owe). * Add .cursor/mcp.json so contributors using Cursor pick up the WDK docs MCP server, and ignore the rest of .cursor/. * fix(deps): pin react to 19.1.0 to match RN 0.81.5's bundled renderer react-native@0.81.5 ships a renderer compiled against React 19.1.0 and strict-checks the running React version at startup. Develop's PR #48 bumped react/react-dom to ^19.2.5 (resolving to 19.2.5), so the dev client crashes on launch with: Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. - react: 19.2.5 - react-native-renderer: 19.1.0 CI never caught this because no job actually launches the app. Pin react/react-dom/react-test-renderer to 19.1.0 to match the renderer baked into RN 0.81.5. Revisit when react-native is bumped to a version that ships the 19.2.x renderer (RN 0.82+).
* ci(e2e): enable Maestro workflow on pull requests against develop - Add `pull_request` trigger on `develop` for non-draft PRs. - Skip doc-only changes (`paths-ignore` for `**/*.md`, `docs/**`, issue/PR templates). - Marking a draft PR ready-for-review fires the workflow (`types: [..., ready_for_review]`). - `concurrency: cancel-in-progress: true` so rebases drop superseded runs. - Both jobs gated on `pull_request.draft != true` (or `workflow_dispatch`) so WIP pushes don't burn ~30 min of macOS minutes. - `docs/maestro.md`: document triggers, the cancel-in-progress behavior, and the three required repo `vars`/`secrets` (`E2E_DFX_API_URL`, `E2E_WDK_INDEXER_URL`, `E2E_WDK_INDEXER_API_KEY`). * ci(e2e): move public testnet URLs into committed .env.testnet - New `.env.testnet` at the repo root holds the public DFX API + WDK indexer URLs and chain RPC slots. Committed on purpose — these aren't secrets. - Workflow drops the `vars.E2E_DFX_API_URL` / `vars.E2E_WDK_INDEXER_URL` indirections; only the indexer API key stays as a repo secret (`E2E_WDK_INDEXER_API_KEY`) and is exported via `env:` so it overrides anything in `.env.testnet`. - Both jobs now `cp .env.testnet .env` before the build so Expo's `EXPO_PUBLIC_*` baking sees the right values. - `docs/maestro.md` reflects the new layout and shows the same `cp` step for local runs.
…undler (#56) * feat(wdk): migrate to wdk-react-native-core ^1.0.0-beta.9 + worklet-bundler Replaces the github-pinned WDK packages with their published npm releases (`@tetherto/wdk-react-native-core@^1.0.0-beta.9`, `@tetherto/pear-wrk-wdk@^1.0.0-beta.8`) and adopts the canonical bundle pipeline via `@tetherto/wdk-worklet-bundler`. Audit steps 1-5 in `docs/wdk-compliance-audit.md`. Why both steps land together: the published `pear-wrk-wdk@1.0.0-beta.8` ships only HRPC, no pre-built bundle, so `import { bundle } from '@tetherto/pear-wrk-wdk'` resolves to `undefined` at runtime and `react-native-bare-kit`'s Worklet constructor throws "Source must be a string or TypedArray". The worklet-bundler is the only way to provide a bundle on the new core. Provider + hooks API drift cleanup - `<WdkAppProvider bundle={{ bundle }} wdkConfigs={...}>` (was `networkConfigs` / `tokenConfigs`) - `useWdkApp().state.status` machine (`INITIALIZING` / `NO_WALLET` / `LOCKED` / `READY` / `ERROR`) - `useWalletManager().restoreWallet(mnemonic, id)` (was `initializeFromMnemonic`); `unlock(id)` (was `initializeWallet({ walletId })`) - `useWallet` is gone; `useDfxAuth` and `receive/index.tsx` now use `useAccount({ network, accountIndex })` with `address` / `sign` - `useSendFlow(chain)` takes the chain upfront and routes through `useAccount.send({ asset, to, amount })` with the native asset Config shape - `src/config/chains.ts` now exports `getWdkConfigs(): WdkConfigs` with `networks: { [name]: { blockchain, config: {...} } }` - `src/config/tokens.ts` now exports `getAssets(): IAsset[]` (flat list of `BaseAsset` instances), replacing the per-network record shape Bundle pipeline - New `wdk.config.js` lists only the modules we ship (`erc4337`, `spark`); avoids pulling in BTC / Solana / TON / TRON - `npm run bundle:wdk` regenerates `.wdk/`; chained from `postinstall` so a fresh clone produces a working app - `.wdk/` and `.wdk-bundle/` are gitignored (build artifact) Workaround removals - Drops the `bare-pack@1` `preinstall` hook (`scripts/ensure-bare-pack.js`) and the `npm install -g bare-pack@^1` step from CI; the new `pear-wrk-wdk` no longer runs `npx bare-pack` during install - Drops the temporary `src/types/pear-wrk-wdk.d.ts` augmentation; we no longer import `bundle` from that package Direct dependencies added - `@tetherto/wdk` `^1.0.0-beta.9` (was transitive) - `@tetherto/wdk-wallet-evm-erc-4337` `^1.0.0-beta.6` (was transitive) - `@tetherto/wdk-wallet-spark` `^1.0.0-beta.18` (was transitive) - `react-native-bare-kit` bumped `^0.11.5` -> `^0.12.3` to satisfy the new core's peer dep - `@tetherto/wdk-worklet-bundler` (devDep), `utf-8-validate` (peer dep of the bundler runtime) - `postinstall` also runs `npm run build` inside `node_modules/@tetherto/wdk-react-native-core` because the published tarball ships `src/` only (no `dist/`) and typecheck/ESLint need the emitted `.d.ts` Local verification - typecheck / lint / format / 72 jest tests all pass - iOS release build runs end-to-end on iPhone 17 Pro simulator - Maestro: 01-welcome, 10-onboarding-create, 11-onboarding-restore all pass; 12-pin-unlock fails on a timing race in `assertVisible: "Incorrect PIN"` that is unrelated to the WDK migration (verify.tsx's wrong-PIN code path doesn't touch any WDK API we changed; failure screenshot shows the text is in fact rendered) * ci(e2e): parse simulator UDID from parens column xcrun simctl list devices' line format is ` iPhone 16 (UUID) (Shutdown)`. Newer Xcode appends the device state, so `awk '{print $NF}' | tr -d '()'` was returning "shutdown" and devicectl rejected it with "No device UDID or name matching 'shutdown'". Parse the UUID column explicitly (-F'[()]', field 2) instead.
Switch the app to a light theme matching the new mountain-illustration mockup and rebuild the Dashboard from scratch. - DfxColors: light surfaces, dark navy text, blue (#2F7CF7) accent. The DFX brand red is preserved as DfxColors.brandRed for the logo. - New full-screen mountain background asset (assets/dashboard-bg.png) and the DFX logo (assets/dfx-logo.png) from the brand kit. - Dashboard layout: DFX logo header + hamburger, Total Wallet Balance with eye-toggle, big balance display, Portfolio + Pay pill buttons, Transactions text link, and a bottom Receive | Send pill. - Portfolio pill routes to a new minimal portfolio screen showing the asset list (extracted from the previous dashboard). - Pay pill opens the QR scanner; scanned data triggers a coming-soon alert until the payment flow is implemented. - Hamburger opens a slide-in menu sheet with Settings as the only entry. The bottom tab bar is gone — the (tabs) folder now uses a Stack so Settings is reached only through the menu. - Inline SVG icon set (src/components/Icon.tsx) replaces the previous emoji-based action bar. - Drops ActionBar and BalanceCard, which had no remaining callers.
- Add eas.json with development, preview and production profiles - Add auto-tag workflow: auto semver tag on merge to main - Add beta-release workflow: EAS Build iOS + Android + GitHub Release on tag push - Add platform-specific emergency release workflows (ios/v*, android/v*) - Add runtimeVersion policy for EAS Update support - Update iOS bundle ID to wallet.dfx.swiss (DFX AG team) - Add appleTeamId Y4QBY6387T to app.json - Add Associated Domains developer mode for passkey testing Release flow mirrors RealUnit app: develop -> main merge -> auto-tag -> EAS Build -> GitHub Release Required GitHub secrets: EXPO_TOKEN, TAG_DEPLOY_KEY
TaprootFreak
approved these changes
May 6, 2026
Make Buy reachable from Receive and Sell reachable from Send so the flows match the user-flow charts (HOME → Receive → Buy with Euro and HOME → Send → Sell). - Extract the dashboard pill-button pattern into a reusable ShortcutAction component. - Receive: 'Buy BTC with Euro' shortcut (€ icon) below the address warning; pushes to /(auth)/buy. - Send: 'Sell crypto instead' shortcut (swap icon) on the input step; pushes to /(auth)/sell. - Refactor Dashboard to consume ShortcutAction instead of an inline PillAction so the styles stay in sync.
joshuakrueger-dfx
approved these changes
May 6, 2026
joshuakrueger-dfx
left a comment
Collaborator
There was a problem hiding this comment.
Auto-release develop → main approved.
joshuakrueger-dfx
added a commit
that referenced
this pull request
Jun 16, 2026
#1 deposit.ts: in the relayer path, compare the relayer's root to the on-chain laneRoot BEFORE proving (rootP already resolved concurrently, ~0ms). Avoids burning a ~0.4s on-device proof against a stale relayer root; on mismatch we fall through to the completeness-verified fallback. No new import, typecheck clean. #13 Maestro: replace bare assertVisible at the cold-start (welcome-screen) and wallet-init (dashboard-screen) boundaries in 21-send/22-dashboard-balance-toggle with extendedWaitUntil (20s) — those lag on a loaded CI simulator and caused the iOS flakiness. No behaviour change. (A real Cloister-pay E2E #12 is deferred: it needs a dedicated CI lane with ENABLE_PAY + cloister flags + a dfxwallet://cloister-pay deep-link fixture (no camera on the simulator) + KYC handling — not a quick win.)
Danswar
pushed a commit
that referenced
this pull request
Jul 12, 2026
BEFORE proving (rootP already resolved concurrently, ~0ms). Avoids burning a ~0.4s on-device proof against a stale relayer root; on mismatch we fall through to the completeness-verified fallback. No new import, typecheck clean. (dashboard-screen) boundaries in 21-send/22-dashboard-balance-toggle with extendedWaitUntil (20s) — those lag on a loaded CI simulator and caused the iOS flakiness. No behaviour change. (A real Cloister-pay E2E #12 is deferred: it needs a dedicated CI lane with ENABLE_PAY + cloister flags + a dfxwallet://cloister-pay deep-link fixture (no camera on the simulator) + KYC handling — not a quick win.)
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.
Automatic Release PR
This PR was automatically created after changes were pushed to develop.
Commits: 1 new commit(s)
Checklist