Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
9622fb2
feat(walletconnect): allowlist Aktionariat and Frankencoin sessions
TaprootFreakAI Sep 11, 2026
e6f11c5
fix(walletconnect): require Verify VALID before allowing a session
TaprootFreakAI Sep 11, 2026
4a22add
fix(walletconnect): isolate storage, attest origin, cover intercepts
TaprootFreakAI Sep 11, 2026
f8935b7
fix(walletconnect): pin reown_walletkit 1.3.8 for pointycastle 3
TaprootFreakAI Sep 11, 2026
1cdb9bc
fix(walletconnect): match reown_walletkit 1.3.8 error and event types
TaprootFreakAI Sep 11, 2026
44ff5eb
test(walletconnect): avoid pumpAndSettle on infinite loading spinner
TaprootFreakAI Sep 11, 2026
b3a069b
test(walletconnect): seed session sheet prompt and fix unknown-verify…
TaprootFreakAI Sep 11, 2026
6b4e4ea
fix(walletconnect): harden https allowlist and scanner-nav tests
TaprootFreakAI Sep 11, 2026
d786a7c
test(goldens): regenerate baselines on the self-hosted runner
github-actions[bot] Sep 11, 2026
d88ac22
fix(walletconnect): fail-closed verify map, stash wipe, attested orig…
TaprootFreakAI Sep 11, 2026
5ed3670
fix(walletconnect): disconnect sessions when the wallet is deleted
TaprootFreakAI Sep 11, 2026
f1780ec
fix(walletconnect): drop a sign prompt when the peer session ends
TaprootFreakAI Sep 11, 2026
6c0ddd2
fix(walletconnect): re-check the session after signing and keep the s…
TaprootFreakAI Sep 11, 2026
81c184b
fix(walletconnect): do not wipe a newer prompt after approve returns
TaprootFreakAI Sep 11, 2026
be9907e
fix(walletconnect): disable reject while in flight and drop a failed …
TaprootFreakAI Sep 11, 2026
af4b79c
test(goldens): add WalletConnect scan and session visual tests
TaprootFreakAI Sep 11, 2026
3b787d7
test(goldens): regenerate baselines on the self-hosted runner
github-actions[bot] Sep 11, 2026
0f51071
docs(goldens): count the four WalletConnect baselines
TaprootFreakAI Sep 11, 2026
3482d35
fix(walletconnect): fail-closed scheme-relative origins and sort
TaprootFreakAI Sep 11, 2026
4778792
fix(walletconnect): wipe persisted sessions on cold reset
TaprootFreakAI Sep 11, 2026
807c996
test(walletconnect): cover scoped packages for the 100% floor
TaprootFreakAI Sep 11, 2026
9e7d7be
fix(walletconnect): extract attested origin and fix migrate mismatch …
TaprootFreakAI Sep 11, 2026
2d0aeeb
fix(walletconnect): reject mailto and other non-https schemes
TaprootFreakAI Sep 11, 2026
c740773
fix(walletconnect): roll back a bad migrate copy and fail closed
TaprootFreakAI Sep 11, 2026
1626ee1
fix(walletconnect): reject userinfo on allowlisted hosts
TaprootFreakAI Sep 11, 2026
e5c39c2
fix(walletconnect): do not collapse www onto exact hosts
TaprootFreakAI Sep 11, 2026
aedbb06
fix(walletconnect): do not assert on unmatched intent pairing URLs
TaprootFreakAI Sep 11, 2026
20b8a9c
fix(walletconnect): accept intent://investorpage as a scan deeplink
TaprootFreakAI Sep 11, 2026
6cab833
test(walletconnect): cover the sign state on the responsive matrix
TaprootFreakAI Sep 11, 2026
4ebe475
ci: give Analyze & Test 45 minutes on macOS
TaprootFreakAI Sep 12, 2026
d7ab879
test(walletconnect): close remaining scoped coverage holes
TaprootFreakAI Sep 12, 2026
20da19b
fix(walletconnect): treat malformed percent-encoding as no pairing
TaprootFreakAI Sep 12, 2026
d8deb78
fix(walletconnect): fail closed on every URI decode path
TaprootFreakAI Sep 12, 2026
c11f06b
test(walletconnect): drop the raw wc: truncated-symKey assertion
TaprootFreakAI Sep 12, 2026
56f7940
fix(walletconnect): surface pair failures and keep session dead
TaprootFreakAI Sep 12, 2026
b9fc724
fix(walletconnect): make coverage ignores and visibility hold
TaprootFreakAI Sep 12, 2026
ed4f876
fix(walletconnect): keep request mapping and always surface policy er…
TaprootFreakAI Sep 12, 2026
25833e1
ci: give Analyze & Test 60 minutes on macOS
TaprootFreakAI Sep 12, 2026
739cecb
fix(walletconnect): reject empty BitBox signatures
TaprootFreakAI Sep 12, 2026
544a5ec
fix(walletconnect): leave the session page after a cancelled sign
TaprootFreakAI Sep 12, 2026
c56c701
fix(walletconnect): latch the session dead before wallet wipe
TaprootFreakAI Sep 12, 2026
7c8b0e4
fix(walletconnect): keep the wipe latch sticky
TaprootFreakAI Sep 12, 2026
e52279a
fix(storage): do not mint a mnemonic key over a partial migrate
TaprootFreakAI Sep 14, 2026
0722abb
ci: give Analyze & Test 90 minutes on macOS
TaprootFreakAI Sep 14, 2026
e245645
ci: give Analyze & Test 120 minutes on macOS
TaprootFreakAI Sep 14, 2026
cb2a868
ci: give Analyze & Test 180 minutes on macOS
TaprootFreakAI Sep 14, 2026
490864b
ci: collect coverage only on the floor surface
TaprootFreakAI Sep 14, 2026
30f436d
ci: include cubit state/event specs in coverage collection
TaprootFreakAI Sep 14, 2026
f7e2a06
docs: align Tier 0/1 CI cells with two-step Analyze
TaprootFreakAI Sep 14, 2026
3b97f6d
ci: give the split Analyze job 120 minutes
TaprootFreakAI Sep 14, 2026
9a8c74c
ci: run Analyze & Test on the self-hosted macOS runner
TaprootFreakAI Sep 14, 2026
bc96325
ci: document the shared self-hosted Analyze slot
TaprootFreakAI Sep 14, 2026
711f3b4
ci: give self-hosted Analyze & Test 60 minutes
TaprootFreakAI Sep 14, 2026
25bab00
ci: skip brew lcov when present; give Analyze 90 minutes
TaprootFreakAI Sep 15, 2026
2040d81
ci: collect coverage in the same self-hosted test run
TaprootFreakAI Sep 15, 2026
c5b7b3d
ci: collect coverage from directories, not exploded files
TaprootFreakAI Sep 15, 2026
32e02fb
docs: match Tests paragraph to directory coverage collection
TaprootFreakAI Sep 15, 2026
80c325a
docs: align testing-tier table with directory coverage
TaprootFreakAI Sep 15, 2026
69fdaaf
ci: give directory coverage 60 minutes
TaprootFreakAI Sep 15, 2026
c2cdc18
ci: cover only packages and cubit/bloc folders
TaprootFreakAI Sep 15, 2026
68775c4
ci: include sibling cubit specs in coverage collection
TaprootFreakAI Sep 15, 2026
6190307
docs: mention sibling cubit specs in coverage copy
TaprootFreakAI Sep 15, 2026
71a1c27
docs: raise hosted Analyze timeout in the outage fallback
TaprootFreakAI Sep 15, 2026
ea4132c
docs: restore Analyze timeout 30 when leaving the outage path
TaprootFreakAI Sep 15, 2026
2d6e6fc
test(goldens): refresh settings referral-eligible baseline
TaprootFreakAI Sep 15, 2026
f786f25
ci: collect floor coverage after Visual on the one runner slot
TaprootFreakAI Sep 15, 2026
b9af336
ci: skip coverage jobs unless Analyze and Visual succeeded
TaprootFreakAI Sep 15, 2026
76c00ba
ci: keep Coverage Floor Gate red when collect does not succeed
TaprootFreakAI Sep 15, 2026
3f5e9b7
fix: keep coverage:ignore-start tokens bare so collect does not hang
TaprootFreakAI Sep 15, 2026
174f272
fix: honour coverage ignore-file and close the floor-surface holes
TaprootFreakAI Sep 15, 2026
371cbc6
test: hit ClearAccountCurrencyEvent at runtime and sort KYC cubit imp…
TaprootFreakAI Sep 15, 2026
e47b242
test: pump KYC registration titles through the app localization helper
TaprootFreakAI Sep 15, 2026
69ee8e3
fix: ignore the unreachable hex-decode FormatException catch
TaprootFreakAI Sep 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .coverage-visibility-allowlist
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,12 @@ lib/screens/restore_wallet/cubit/validate_seed/validate_seed_state.dart
# Const-only data (covered as values by default_assets_test.dart, no lines):
lib/packages/utils/default_assets.dart

# Const-only WalletConnect metadata (project id, chains, methods); no bodies:
lib/packages/walletconnect/walletconnect_config.dart

# Reown WalletKit plugin/relay adapter (`coverage:ignore-file`); FakeEngine
# unit tests cover the port, so this file emits no SF: record:
lib/packages/walletconnect/reown_walletconnect_engine.dart

# Drift table schema (column getters carry coverage:ignore-line):
lib/packages/storage/node_storage.dart
217 changes: 138 additions & 79 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,130 @@ jobs:
# always run — the condition is "anything that isn't a PR, or a PR
# that isn't a draft".
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: macos-latest
timeout-minutes: 30
# Same self-hosted runner as Visual Regression and Coverage collect.
# macos-latest cancelled uninstrumented `flutter test --exclude-tags
# golden` at 91 and 120 minutes; this runner finishes the suite in
# ~2 minutes. One slot: do not collect coverage here — that step
# cancelled at 30 minutes and starved Visual Regression in the queue.
runs-on: [self-hosted, macOS, ARM64, m3-ultra, realunit-app]
# Uninstrumented suite finishes in ~2 minutes here. 15 covers
# checkout, codegen, analyze, and that pass/fail run.
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: subosito/flutter-action@v2
with:
flutter-version: "3.41.6"
channel: "stable"
cache: true
cache: false
- run: flutter pub get
- run: dart run tool/generate_localization.dart
- run: dart run tool/generate_release_info.dart
- run: flutter pub run build_runner build
- run: bash scripts/run-handbook-flows.sh --matcher-self-test
- run: flutter analyze
# Excludes the `golden` tag: visual-regression tests live under
# `test/goldens/` and are validated on the self-hosted runner
# in the parallel `golden-tests` job (Hardware-Determinismus, see
# `docs/visual-regression-tests.md`). Running them here too would
# both duplicate work and erroneously red this job on macos-latest
# where the Skia/font-rendering does not match the committed
# baselines.
- run: flutter test --coverage --exclude-tags golden
# `test/goldens/` and are validated in the `golden-tests` job
# (see `docs/visual-regression-tests.md`). Running them here
# too would duplicate that work. Floor coverage lives in
# `coverage-collect` so this job can free the one slot.
- run: flutter test --exclude-tags golden

# Hard-fails the build when scoped coverage drops below the committed floor.
# Two flat repo-root files hold the integers: `.coverage-floor-lines` and
# `.coverage-floor-functions`. They are diffable, grep-able, and require
# no `yq`/JSON tooling in the runner — same rationale as the rest of this
# workflow: keep the gate readable in a `git blame`, not buried in YAML.
#
# Lives in its own job (not inline in `Analyze & Test`) so it can be set
# as a separately required status check in branch protection — a single
# job name on `Analyze & Test` would let the floor regress without
# blocking merge if it stayed inline. The job graph is:
# build ║ golden-tests (one self-hosted slot; either may start first)
# └► coverage-collect (after both; needs the runner next)
# └► coverage-floor
# bitbox-audit (parallel; informational only)
#
# Ratchet protocol (also documented under README "Coverage infrastructure
# roadmap"):
# * Raising the floor is encouraged on every PR that raises measured
# coverage — bump the file in the same commit and the gate moves up.
# * Lowering the floor needs a reviewer's explicit OK. PR convention is
# the `coverage:lower-floor` label so the regression is visible at
# a glance in the PR list rather than being smuggled in.
#
# Why pure bash + awk instead of `lcov --fail-under-*`:
# the `--fail-under-lines` flag arrived in lcov 2.0 and earlier runner
# images may still pull 1.x out of the package manager. Comparing
# `52.9` against `51` with awk sidesteps that and stays portable.
#
# On the "no data found" path for the functions metric:
# `flutter test --coverage` emits LF/LH (line execution) and BRF/BRH
# (branch) records, but not FN/FNF/FNH (function execution) — that's a
# Flutter limitation, not a repo bug. When the summary reports
# "no data found" for functions, the gate emits a workflow warning
# instead of comparing against an empty value. This is intentionally
# NOT a silent skip: the warning surfaces in the run summary so a
# future Flutter release adding FN records doesn't go unnoticed (and
# the floor file stays committed so the gate activates the moment
# real data appears).
#
# On the missing `coverage/lcov.summary` path:
# the gate fails CLOSED with `exit 1`. Previously this was a silent
# `exit 0` warning, which made the gate effectively advisory — a broken
# upstream filter (e.g. lcov.info missing, brew install failure) would
# skip the gate without anyone noticing. The new behaviour is: if
# `Coverage collect` did not produce a summary, the gate is red, the PR
# is blocked, and the reviewer sees exactly where the pipeline broke.
coverage-collect:
name: Coverage collect
needs: [build, golden-tests]
# Draft guard PLUS `success()`. A custom `if:` replaces the implicit
# skip-on-failed-needs, so without `success()` this job would still
# start after a red Analyze or Visual and occupy the one slot for
# up to 90 minutes.
if: (github.event_name != 'pull_request' || github.event.pull_request.draft == false) && success()
# Same self-hosted runner as Analyze & Test / Visual Regression.
# Waits until both finish so goldens are not queued behind
# instrumented tests. One slot: queue wait is not job timeout.
runs-on: [self-hosted, macOS, ARM64, m3-ultra, realunit-app]
# Instrumented floor-surface run cancelled at 30 minutes inside
# Analyze & Test (uninstrumented suite had already passed in ~2
# minutes). 90 is the collect+filter budget on this runner.
timeout-minutes: 90
steps:
- uses: actions/checkout@v6
- uses: subosito/flutter-action@v2
with:
flutter-version: "3.41.6"
channel: "stable"
cache: false
- run: flutter pub get
- run: dart run tool/generate_localization.dart
- run: dart run tool/generate_release_info.dart
- run: flutter pub run build_runner build
- name: Collect coverage for the floor surface
run: |
set -euo pipefail
cubit_dirs=$(find test -type d \( -name cubit -o -name cubits -o -name bloc \) | tr '\n' ' ')
sibling_tests=$(find test/screens \( \
-name '*cubit*_test.dart' -o \
-name '*bloc*_test.dart' -o \
-name '*_state_test.dart' -o \
-name '*_event_test.dart' \
\) ! -path '*/cubit/*' ! -path '*/cubits/*' ! -path '*/bloc/*' | tr '\n' ' ')
states_tests=$(find test -name '*_states_test.dart' | tr '\n' ' ')
echo "cubit_dirs=${cubit_dirs}"
echo "sibling_tests=${sibling_tests}"
echo "states_tests=${states_tests}"
# Packages + cubit/bloc folders + sibling cubit specs that
# live next to pages. Do not pass feature parent dirs (those
# pull widget tests and hung `--coverage` past 58 minutes)
# and do not explode every cubit file (that hung 87 minutes
# with no log). Expanded reporter so a hang names the spec.
# shellcheck disable=SC2086
flutter test --coverage --reporter expanded --exclude-tags golden \
test/packages/ ${cubit_dirs} ${sibling_tests} ${states_tests}

# Narrow the coverage report to the README-defined activated surface:
# lib/packages/** — services, repositories, signers, utils
Expand Down Expand Up @@ -101,10 +202,12 @@ jobs:
run: |
set -euo pipefail
if [ ! -f coverage/lcov.info ]; then
echo "::warning::coverage/lcov.info not found — skipping coverage filter"
exit 0
echo "::error::coverage/lcov.info not found after the floor-surface run"
exit 1
fi
if ! command -v lcov >/dev/null; then
brew install lcov >/dev/null
fi
brew install lcov >/dev/null
lcov --extract coverage/lcov.info \
'lib/packages/*' \
'lib/screens/*/cubit/*' \
Expand Down Expand Up @@ -168,60 +271,23 @@ jobs:
path: coverage/lcov.summary
if-no-files-found: error

# Hard-fails the build when scoped coverage drops below the committed floor.
# Two flat repo-root files hold the integers: `.coverage-floor-lines` and
# `.coverage-floor-functions`. They are diffable, grep-able, and require
# no `yq`/JSON tooling in the runner — same rationale as the rest of this
# workflow: keep the gate readable in a `git blame`, not buried in YAML.
#
# Lives in its own job (not inline in `Analyze & Test`) so it can be set
# as a separately required status check in branch protection — a single
# job name on `Analyze & Test` would let the floor regress without
# blocking merge if it stayed inline. The job graph is:
# build ─► coverage-floor (sequential; needs the summary artifact)
# build ║ bitbox-audit (parallel; informational only)
#
# Ratchet protocol (also documented under README "Coverage infrastructure
# roadmap"):
# * Raising the floor is encouraged on every PR that raises measured
# coverage — bump the file in the same commit and the gate moves up.
# * Lowering the floor needs a reviewer's explicit OK. PR convention is
# the `coverage:lower-floor` label so the regression is visible at
# a glance in the PR list rather than being smuggled in.
#
# Why pure bash + awk instead of `lcov --fail-under-*`:
# the `--fail-under-lines` flag arrived in lcov 2.0 and earlier runner
# images may still pull 1.x out of the package manager. Comparing
# `52.9` against `51` with awk sidesteps that and stays portable.
#
# On the "no data found" path for the functions metric:
# `flutter test --coverage` emits LF/LH (line execution) and BRF/BRH
# (branch) records, but not FN/FNF/FNH (function execution) — that's a
# Flutter limitation, not a repo bug. When the summary reports
# "no data found" for functions, the gate emits a workflow warning
# instead of comparing against an empty value. This is intentionally
# NOT a silent skip: the warning surfaces in the run summary so a
# future Flutter release adding FN records doesn't go unnoticed (and
# the floor file stays committed so the gate activates the moment
# real data appears).
#
# On the missing `coverage/lcov.summary` path:
# the gate fails CLOSED with `exit 1`. Previously this was a silent
# `exit 0` warning, which made the gate effectively advisory — a broken
# upstream filter (e.g. lcov.info missing, brew install failure) would
# skip the gate without anyone noticing. The new behaviour is: if
# `Analyze & Test` did not produce a summary, the gate is red, the PR
# is blocked, and the reviewer sees exactly where the pipeline broke.
coverage-floor:
name: Coverage Floor Gate
needs: build
# Same draft guard as `build`: skip drafts, always run on push/dispatch.
# `build` already enforces this, but mirroring it here keeps the gate's
# behaviour locally readable instead of inferred from `needs:`.
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
needs: coverage-collect
# `always()` so a failed/cancelled collect does not skip this
# required check (skipped counts as passing). `success()` stays on
# `coverage-collect` so a red Analyze/Visual does not occupy the
# one self-hosted slot. Drafts still skip.
if: always() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Require a successful collect
if: needs.coverage-collect.result != 'success'
run: |
echo "::error::Coverage collect did not succeed (result=${{ needs.coverage-collect.result }}) — floor stays red instead of skipped"
exit 1

- uses: actions/checkout@v6

- name: Download coverage summary
Expand All @@ -240,7 +306,7 @@ jobs:
run: |
set -euo pipefail
if [ ! -f coverage/lcov.summary ]; then
echo "::error::coverage/lcov.summary not found after artifact download — the build job did not produce a scoped coverage summary"
echo "::error::coverage/lcov.summary not found after artifact download — Coverage collect did not produce a scoped coverage summary"
exit 1
fi
if [ ! -f .coverage-floor-lines ] || [ ! -f .coverage-floor-functions ]; then
Expand Down Expand Up @@ -294,28 +360,21 @@ jobs:
# and PRs that drift from them fail this job. Bootstrap and re-generation
# is documented in `docs/visual-regression-tests.md`.
#
# Runs parallel to `build` (which uses GitHub-hosted macos-latest). The
# separation is intentional:
# * `build` covers analyze + unit/widget tests + coverage — needs no
# hardware determinism.
# * `golden-tests` covers pixel-exact rendering — must run on the self-hosted runner so
# baselines and validation use identical Skia/font-rendering state.
# Shares the one-slot self-hosted runner with `build` and
# `coverage-collect`. Job purpose stays split: `build` is analyze +
# uninstrumented unit/widget tests; this job is pixel-exact goldens;
# `coverage-collect` waits for both so goldens are not starved.
# Queue wait does not count toward `timeout-minutes`.
#
# On self-hosted runner outage: temporarily flip `runs-on:` to `macos-15` and regenerate
# On self-hosted runner outage: temporarily flip `runs-on:` on this
# job, `build`, and `coverage-collect` to `macos-15` and regenerate
# baselines in the same PR (see `docs/visual-regression-tests.md`).
golden-tests:
name: Visual Regression
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: [self-hosted, macOS, ARM64, m3-ultra, realunit-app]
# 30 was sized for the era when `subosito/flutter-action@v2` still pulled
# the SDK through the Actions cache and burned ~18 minutes on a stalled
# restore plus the trailing cache-save. Without that overhead the job
# finishes in ~1 minute, so 15 is generous headroom — it still covers the
# worst normal case, where the SDK is absent from the runner's persistent
# tool cache and has to be downloaded from scratch. The lower ceiling is
# what matters here: the self-hosted runner has exactly ONE slot for this
# repo, so a wedged job blocks every other PR until it is killed. 15 hands
# the slot back twice as fast as 30.
# Keep in sync with `golden-regenerate.yaml`. A wedged golden job must
# not hold the one slot past 15 minutes.
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ The auto-opened promotion PRs are idempotent — only one is open per branch pai
- If a feature needs on-chain data (e.g. native ETH balance, transaction status, token balance), add a new endpoint to [`DFXswiss/api`](https://github.com/DFXswiss/api) and let the app call that endpoint. The API is the single gateway.
- All network calls must go through `AppStore.httpClient` with `buildUri(_host, …)` — `_host` resolves to the DFX API host via `ApiConfig`. Do not instantiate `http.Client`/`Dio`/`Web3Client` against other hosts.
- **One scoped exception — crash reporting.** Builds that inject `--dart-define=SENTRY_DSN=...` deliver crash reports to the company-operated crash-reporting service ([`lib/setup/error_handling/crash_reporting.dart`](lib/setup/error_handling/crash_reporting.dart)). This is first-party infrastructure telemetry, not a third-party service: without an injected DSN (all local and test builds) the SDK never starts and produces no network traffic, and the delivered data is limited to error events — no PII, no screenshots, no performance tracing, no session telemetry (the exact pinned option surface lives in `crash_reporting.dart`). Widening what is sent (breadcrumbs with request URLs, user context, attachments) is a review-blocking change, not a config tweak.
- **Second scoped exception — WalletConnect.** Reown WalletKit may talk to the WalletConnect relay (`wss://relay.walletconnect.org`) and verify (`https://verify.walletconnect.org`) **only** after the user starts a pairing, and **only** for sessions whose **Verify-attested** origin is `aktionariat.com` or `frankencoin.com` (including subdomains such as `tokeninfo.aktionariat.com`) or the Aktionariat tenant host `shares.realunit.ch`. `metadata.url` is attacker-controlled and is never the allowlist input. Verify `VALID` is required; `UNKNOWN`, `INVALID`, and `isScam` are rejected and the pairing is disconnected. Any other origin is rejected in-app with a user-visible “not supported for this provider” message. The SDK is not initialized at boot (no relay traffic with zero sessions). This is not a general-purpose dApp connector: do not add Ethereum JSON-RPC, block-explorer APIs, Pulse/analytics hosts, or other WalletConnect-adjacent endpoints. `eth_sendTransaction` is rejected (no broadcast path). Production `SecureStorage` uses an isolated Android/iOS namespace because WalletKit uses the default FlutterSecureStorage namespace and has been observed to wipe sibling keys; a one-shot migrate copies PIN/mnemonic/DB keys into the isolated store. The Cloud project id is a public client id; override via `--dart-define=WALLETCONNECT_PROJECT_ID=...` if needed. Implementation: [`lib/packages/walletconnect/`](lib/packages/walletconnect/). Widening the allowlist, skipping Verify, or initializing WalletKit at process start is a review-blocking change.

## API as Decision Authority — CRITICAL

Expand Down
Loading
Loading