Skip to content

Release: develop -> main - #13

Merged
joshuakrueger-dfx merged 40 commits into
mainfrom
develop
May 6, 2026
Merged

Release: develop -> main#13
joshuakrueger-dfx merged 40 commits into
mainfrom
develop

Conversation

@github-actions

@github-actions github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

Automatic Release PR

This PR was automatically created after changes were pushed to develop.

Commits: 1 new commit(s)

Checklist

  • Review all changes
  • Verify CI passes
  • Approve and merge when ready for production

joshuakrueger-dfx and others added 30 commits May 4, 2026 19:24
…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>
TaprootFreak and others added 9 commits May 4, 2026 22:16
- 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
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 joshuakrueger-dfx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-release develop → main approved.

@joshuakrueger-dfx
joshuakrueger-dfx merged commit 34a992b into main May 6, 2026
15 checks passed
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.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants