Skip to content

Network Config + react-native-core breaking changes - #55

Open
NirmalPatidar wants to merge 63 commits into
tetherto:mainfrom
NirmalPatidar:develop
Open

Network Config + react-native-core breaking changes#55
NirmalPatidar wants to merge 63 commits into
tetherto:mainfrom
NirmalPatidar:develop

Conversation

@NirmalPatidar

Copy link
Copy Markdown
Contributor

Two independent pieces of work, merged together into develop and raised
here as one PR since they landed in the same window — not one change,
structured below so each can be reviewed on its own terms.

  1. Adapts this app to the breaking wallet-lifecycle changes in
    [wdk-react-native-core PR #77](Harden wallet lifecycle: remove implicit auto-init, enforce explicit identity boundary wdk-react-native-core#77).
  2. Unifies every per-network fact (runtime config, assets, display
    labels, colors, icons, explorer links, indexer mapping) into one file,
    in response to direct product feedback that adding or removing a
    network required editing too many places.

Part 1 — Adapting to wdk-react-native-core PR #77

Reviewed the full PR (every breaking-changes note, both direction and
rationale) before making any change here, rather than adapting reactively
to whichever error surfaced first.

What actually breaks, and the fix for each

useWalletActions.ts

  • wm.setActiveWalletId(DEFAULT_WALLET_ID) removed — that function no
    longer exists in the new API. Turns out it was already redundant before
    this: unlock(), called on the very next line, already sets the active
    identity correctly as part of its own real work, per PR #77's own
    guidance that unlock/createWallet/restoreWallet/switchWallet
    "manage identity correctly ... and should be used instead."
  • lock: (): void => wm.lock()lock: (): Promise<void> => wm.lock().
    wm.lock() now returns Promise<void> (previously void) and shares
    an operation mutex with unlock/createWallet/restoreWallet/
    switchWallet. The old signature silently dropped that promise —
    every caller needs to actually await this now.

AutoLockOnBackground.tsx — the one fix here I'd flag as genuinely
risky if missed, not just a type mismatch. The background-detection
handler called lock() and then clearPasswordSession() immediately
after, without awaiting lock() at all. With the new shared mutex, a
fast background→foreground→unlock cycle could hit unlock() while
lock() was still mid-flight and holding it — a real, if narrow, race
condition, not a hypothetical one. Now sequenced as
lock().then(clearPasswordSession).catch(...).

useWdkSession.ts

  • retry removed from this hook's return value, since useWdkApp() no
    longer exposes it at all.
  • The hasStoredWallet disambiguation logic (working around WDK
    previously reporting NO_WALLET instead of LOCKED right after
    lock(), even when a wallet genuinely existed on disk) is kept,
    not removed — PR #77's own notes claim this exact bug is now fixed
    upstream, but that hasn't been independently verified end-to-end on a
    real device over multiple sessions yet. Removing a working safety net
    on a changelog description alone seemed like a worse trade than a
    little temporary redundancy. Left a comment noting exactly what to
    simplify once this is confirmed.

index.tsx

  • retry removed from the destructured useWdkSession() result, and
    the error screen's onRetry prop dropped entirely (it's optional on
    ErrorState, so this cleanly removes the button rather than leave it
    wired to nothing). Deliberately not replaced with a fabricated retry
    action — this screen renders when WDK's own app-level init fails,
    before any specific lifecycle method like unlock/createWallet was
    ever called, so there genuinely isn't one to re-call, unlike what PR
    #77's own guidance assumes for other error cases.

Verified safe, no change needed

  • enableAutoInitialization / currentUserId /
    clearSensitiveDataOnBackground on WdkAppProvider — never used
    anywhere in this app.
  • The restoreWallet-retry-on-"already exists" path in importWallet
    (the iOS-Keychain-survives-uninstall self-heal) — confirmed safe by
    checking WdkSessionGate.tsx: onboarding is only ever reachable with
    no wallet active, so PR #77's new "throws if a wallet is already
    active" guard can't fire there.
  • activeWalletId no longer persisting across app restarts — nothing in
    this app relied on that; identity is always re-established explicitly
    via unlock(DEFAULT_WALLET_ID).
  • cloud-provider.tsx's direct useWalletManager() usage
    (getMnemonic/getSeedAndEntropyFromMnemonic/activeWalletId) —
    doesn't touch any changed lifecycle API.

Testing scaffolding included here — not a permanent fix, flagging clearly

Two changes exist purely to verify the above against a real, running
build of PR #77 before it merges upstream, and should be revisited once
it does:

  • package.json@tetherto/wdk-react-native-core points at
    github:nulllpc/wdk-react-native-core-fork#npc/remove-auto-init-flag
    (the PR's own branch), not a published version. Should become a real
    version bump once PR #77 merges and publishes.
  • tsconfig.json — a paths override for
    @tetherto/wdk-react-native-core, working around a confirmed bug in
    that fork's own build config (outDir set with no rootDir, landing
    compiled types at dist/src/index.d.ts instead of the dist/index.d.ts
    its own package.json points to). Doesn't affect the app at runtime —
    Metro's resolution is more lenient than tsc's — only
    npm run typecheck needs it. Worth reporting upstream; remove once
    fixed there.

Part 2 — One file to add, remove, or change a network

Direct product feedback: "Adding a network today looks like it involves
editing: worklet config, runtime config, assets, indexer mapping, address
maps, icons, etc. A small registry in the starter ... would make 'clone
and add or remove support for different chains' a bit easier."

The design

src/wdk/networks.ts is now the one file to edit. Per network, one entry
holds: its runtime provider/bundler/paymaster config, its asset(s),
display label (env-aware where relevant, e.g. Bitcoin/Ethereum's
testnet-label handling), color, icon, block-explorer link, and the
indexer's own blockchain-name mapping. Everything else — wdkConfigs,
ASSETS/ASSET_MAP, networkDisplayName(), ALL_NETWORKS, per-screen
filter lists — is derived from this one array, not maintained separately.

Two things deliberately not folded in, both for real, verified
technical reasons, not just scope-narrowing:

  • wdk.config.js (the worklet-bundler's own config — which networks
    compile into the native bundle) stays fully separate. It's read by an
    external tool via Node's native import(), at a point before any of
    our own build tooling runs, and per explicit product-team direction:
    "the worklet-side config would remain the same."
  • Each network's env-var reads and each icon's require() call are
    still written out literally, one per network, inside its own entry —
    never generated in a loop. This isn't residual complexity; it's the
    same real constraint already hit once in this project's history:
    babel-preset-expo only inlines process.env.EXPO_PUBLIC_* when the
    key is a literal at compile time, and Metro only resolves require()
    when the path is a literal at its call site. A previous version of
    this exact config did build these dynamically in a loop, and it
    shipped every EVM network with provider: '' in a release build,
    completely silently, since dev mode papers over it. The header comment
    on networks.ts documents both rules with a ✅/❌ example, so this
    isn't rediscovered the hard way a second time.

Migration

config.ts, assets.ts, and chains.ts are now thin (~9-line)
re-export shims pointing at networks.ts, explicitly labeled
"Shim only — nothing to edit here." This was deliberate: it means the
ten existing files importing from them across the app needed zero
changes, keeping this diff scoped to the registry itself rather than
touching every consumer.

Verified, not assumed

  • Checked every consumer of the previously-scattered pieces
    (networkColor, CHAIN_ICONS, the duplicated explorer-URL map in both
    send/success.tsx and tx/[id].tsx) before consolidating, rather than
    guessing at call sites.
  • The old networkColor map carried two entries (sepolia, tron) that
    didn't correspond to any real internal network key this app actually
    uses — confirmed unreachable, not carried forward.
  • AppAsset.fromConfigs() takes a plain array, so flattening assets out
    of each network's own entry required no change to that file at all.

Related, deferred follow-up (not in this PR)

Genuinely reduced this from four edit locations to two
(wdk.config.js + networks.ts) rather than one — the rootDir/literal
constraints above are real, not a missed opportunity. If
wdk-worklet-bundler ever supports loading a shared, typed config module
directly, wdk.config.js could potentially collapse into this too; not
attempted here since it depends on that external tool's own capabilities,
which haven't been verified.


Verification

  • npm run typecheck — clean.
  • Manual testing on both iOS and Android: fresh onboarding, rapid
    background/foreground cycling (targeting the AutoLockOnBackground.tsx
    race specifically), lock → background → foreground → unlock, cloud
    backup, and a full pass through Home/Send/Receive/Activity confirming
    every network's label/color/icon/explorer link still renders correctly
    post-refactor.
  • Two real, unrelated build/environment issues surfaced and were
    resolved separately during this work (not part of this diff, noted
    here since they blocked testing): an expo-font dual-version conflict
    causing an Android-only native crash, and an org-wide
    legacy-peer-deps=true npm setting preventing react-native-bare-kit
    (a peer dependency) from installing at all.

Related

Nirmal Patidar added 30 commits July 7, 2026 19:13
- expo-router file-based routing under src/app
- all 18 screens with working navigation
- v2 light theme + dark-ready theme system
- reusable component library
- domain/data/session seams for WDK (mock repository)
- auto-lock, edge-to-edge config
- RepositoriesProvider DI boundary (mock now, WDK-ready)
- QueryClient + typed query hooks + centralized keys
- loading/error/retry states on all data screens
- screens no longer import concrete repositories
- add WdkAppProvider at root with minimal Bitcoin wdkConfigs
- import generated worklet bundle
- log useWdkApp lifecycle (INITIALIZING -> NO_WALLET confirmed)
- no screen/behaviour change; still on mock repository
- Replace mock data with live WDK hooks (balances, account, send)
- Wire wallet create/import/unlock via useWalletManager
- Add session/lock handling: WdkSessionGate + AutoLockOnBackground
- Fix locked-vs-no-wallet detection (lock() reports NO_WALLET, not LOCKED;
  disambiguate via persisted wallets list)

Verified on device: create → real balance/address → lock → unlock.
…which is encrypted first with the help of wdk-utils and then we are storing it to secure storage
…ve acccounts get listed in accounts screen
abdulhaseeb4239 and others added 23 commits August 3, 2026 17:32
Ports the pipeline already proven in tetherto/city-wallets-wl-app-mobile,
collapsed from its two-app matrix to this repo's single app: local EAS
builds on GitHub Actions runners, then `eas submit` to TestFlight and the
Play `internal` track. workflow_dispatch only, with buildTarget
(stores|android|ios) and pushTag inputs.

Three composite actions:
  setup-eas     — Node 22 plus a pinned eas-cli. Forces npm >= 11 because
                  npm 10 silently drops packages from git-dependency trees
                  and this project has three git deps (docs/ENVIRONMENT.md).
  select-xcode  — pins Xcode 26.3 on the macOS runner.
  write-dotenv  — materialises the EXPO_PUBLIC_* config to .env before the
                  build, since babel-preset-expo inlines those at bundle
                  time and eas build --local bundles in a copy of the tree.

Notable differences from the city-wallets original, all deliberate:

- No link-bare-addons step. react-native-bare-kit self-links via its
  podspec prepare_command and a gradle `link` task; the upstream script
  only exists to work around that repo's yarn workspaces.
- npm rather than yarn/corepack.
- Artifact location comes from `eas build --output` instead of
  `find $GITHUB_WORKSPACE -name '*.aab'`, which would also match the
  gradle intermediate at android/app/build/outputs/bundle/release/.
- .env values are written single-quoted with $ escaped, and the step fails
  on a value it cannot encode. Expo parses .env with node:util.parseEnv and
  then interpolates, so an unquoted # or an unescaped $word silently
  truncates the value — verified against Expo's own parser. Empty secrets
  are warned about and omitted rather than inlined as "".
- The 20 EXPO_PUBLIC_* secrets are mapped at job level, not on the `uses:`
  step: a composite action's inner steps do not reliably see step-level env.
- concurrency queues instead of cancelling; a cancelled run can leave a
  half-finished store submission behind.

Also adds .github/actionlint.yaml declaring the self-hosted runner label so
`actionlint` runs clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs/RELEASE.md covers how the pipeline is wired, the full secret
inventory (including which values are base64 and the one that is raw
JSON), the credentials.json shape — which exists only as a secret and so
is not otherwise recorded anywhere — and the one-time setup still
outstanding before the workflow can go green.

Called out explicitly because each has a misleading failure mode:

- The distribution provisioning profile must carry the iCloud entitlement
  with container iCloud.io.tether.wdkshowcase, which deliberately differs
  from the bundle id.
- The release keystore's SHA-1 must be registered against the new package
  name, or Google Sign-In fails with DEVELOPER_ERROR (10) in release
  builds only.
- The Play Developer API cannot create an app's first release, so one AAB
  must be uploaded by hand. Doing that with a locally built artifact
  consumes versionCode 1 and then collides with the EAS remote counter —
  the documented sequence avoids that by using the artifact from a CI run
  whose submit step failed.
- The two *_NETWORK_LABEL variables are intentionally blank on mainnet, so
  a warning about them is not necessarily a missing secret.

Includes the credential-free bundle test for confirming EXPO_PUBLIC_*
values actually reach the bundle, and a troubleshooting table for the
failure modes seen while building this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…workflow

ci: add Build and Publish workflow (TestFlight + Play internal), and fix EVM env inlining
- Switch both build jobs from the  to the  GitHub
  Environment so Apple distribution creds can be gated behind protection
  rules; clarify in the header that distribution is still TestFlight /
  Play internal, not a production listing
- Filter eas-cli-local-build-plugin noise out of the Android and iOS
  build logs (pipefail keeps build failures propagating)
@socket-security

socket-security Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​tetherto/​wdk-worklet-bundler@​1.0.0-beta.107710010094100
Updated@​tetherto/​wdk-react-native-core@​1.0.0-beta.14 ⏵ 1.0.0-beta.13N/AN/AN/AN/AN/A

View full report

@socket-security

socket-security Bot commented Aug 17, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Medium
System shell access: npm @tetherto/wdk-worklet-bundler in module child_process

Module: child_process

Location: Package overview

From: package-lock.jsonnpm/@tetherto/wdk-worklet-bundler@1.0.0-beta.10

ℹ Read more on: This package | This alert | What is shell access?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should avoid accessing the shell which can reduce portability, and make it easier for malicious shell access to be introduced.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@tetherto/wdk-worklet-bundler@1.0.0-beta.10. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Dynamic code execution: npm @tetherto/wdk-worklet-bundler

Eval Type: Function

Location: Package overview

From: package-lock.jsonnpm/@tetherto/wdk-worklet-bundler@1.0.0-beta.10

ℹ Read more on: This package | This alert | What is dynamic code execution?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Avoid packages that use dynamic code execution like eval(), since this could potentially execute any code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@tetherto/wdk-worklet-bundler@1.0.0-beta.10. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Comment thread src/wdk/hooks/AutoLockOnBackground.tsx Outdated
// .catch() here, not left to surface as an unhandled rejection —
// this fires from an AppState listener, nothing downstream is
// awaiting this call or positioned to catch a rejection itself.
lock().then(clearPasswordSession).catch((e) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would suggest .finally(clearPasswordSession) here instead of catching on .then. If lock() fails the password now stays in memory while the app is in the background.

Comment thread src/app/(app)/(tabs)/activity.tsx Outdated
{ key: 'arbitrum', label: networkDisplayName('arbitrum') },
{ key: 'polygon', label: networkDisplayName('polygon') },
{ key: 'bitcoin', label: networkDisplayName('bitcoin') },
...ALL_NETWORKS.map((network) => ({ key: network as ChainFilter, label: networkDisplayName(network) })),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we type this filter from ALL_NETWORKS instead of casting into a hardcoded ChainFilter ?

@jonathunne jonathunne left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A few things to fix before merge — see inline comments.

// literally 'sepolia'; Tron isn't compiled into the worklet at all) —
// they weren't reachable, so they weren't carried forward.

function networkToChain(network: string): ChainId {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Return the network as-is (or type ChainId from the registry) instead of defaulting unknown ones to 'bitcoin'.

Nirmal Patidar added 2 commits August 18, 2026 12:40
- AutoLockOnBackground: .then() -> .finally() so clearPasswordSession()
  still runs if lock() rejects (password was staying in memory on failure)
- activity.tsx: derive ChainFilter from ALL_NETWORKS instead of casting
- useWalletData: networkToChain() returns network as-is instead of
  silently defaulting unrecognized ones to 'bitcoin'
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