Skip to content

feat(mobile): Metro monorepo config, mobile CI, EAS profiles, earn screen - #599

Open
Salmatcre8 wants to merge 4 commits into
Miracle656:mainfrom
Salmatcre8:feat/mobile-metro-ci-eas-earn
Open

feat(mobile): Metro monorepo config, mobile CI, EAS profiles, earn screen#599
Salmatcre8 wants to merge 4 commits into
Miracle656:mainfrom
Salmatcre8:feat/mobile-metro-ci-eas-earn

Conversation

@Salmatcre8

@Salmatcre8 Salmatcre8 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Draft — one branch covering four mobile backlog items. Landing them together
because they stack: Metro has to reach the workspace SDK before the earn screen
can import it, and CI/EAS are what prove the result builds.

closes #430
closes #439
closes #500
closes #475

What landed

Issue Scope Commit
#430 metro.config.js — watch repo root + sdk/, nodeModulesPaths, unstable_enableSymlinks c085121
#439 .github/workflows/mobile.yml — install + tsc --noEmit + expo lint + tests, scoped to frontend/mobile/** 41ddfd6
#500 eas.json (development/preview/production) + store listing drafts, Android first e339e9e
#475 app/earn.tsx + lib/blend.ts — Blend positions/APYs, passkey-gated supply/withdraw f36a959

#430 — Metro monorepo config

metro.config.js previously only wired the @/* tsconfig aliases, so anything
outside frontend/mobile was invisible to the bundler. Now it also:

  • watches the repo root (where npm workspaces links node_modules/invisible-wallet-sdk -> ../sdk) and sdk/ itself, since symlinked modules are watched by their real path;
  • searches frontend/mobile/node_modules, then the hoisted root node_modules, then sdk/node_modules, so the SDK's own dependencies resolve once Metro steps outside the app;
  • sets resolver.unstable_enableSymlinks so the workspace link is followed to the TypeScript sources;
  • maps invisible-wallet-sdk -> sdk/ via extraNodeModules as a fallback for installs that skipped the workspace root.

react-native-passkey is added to the app's dependencies: it is the SDK's
declared peer for webauthn.native.ts, and without it the native WebAuthn path
cannot resolve.

Verification. Built the Android Metro dependency graph for an entry that does
nothing but import { useInvisibleWallet } from 'invisible-wallet-sdk':

modules in graph: 684
--- invisible-wallet-sdk modules in graph (14) ---
  sdk/src/backup.ts
  sdk/src/bulkPayout.ts
  sdk/src/claimableBalance.ts
  sdk/src/counterfactual.ts
  sdk/src/crypto/prf.ts
  sdk/src/index.ts
  sdk/src/outbox.ts
  sdk/src/recovery/sep30.ts
  sdk/src/sep7.ts
  sdk/src/signMessage.ts
  sdk/src/useInvisibleWallet.ts
  sdk/src/utils.ts
  sdk/src/webauthn.native.ts
  sdk/src/webauthn/attestation.ts
webauthn.native.ts in graph  : true
webauthn.ts        in graph  : false
react-native-passkey in graph: true

That is the acceptance criterion: the import resolves through the package's
"react-native": "src/index.ts" field and Metro picks webauthn.native.ts over
the browser webauthn.ts.

#439 — Mobile CI

New Mobile workflow rather than a job inside ci.yml: GitHub Actions filters
paths per workflow, not per job, so a job in ci.yml could not be scoped to
frontend/mobile/** without affecting the contracts, sdk, wallet and website
jobs. Follows the same shape as the existing storybook.yml and
wallet-e2e.yml.

  • triggers on pull_request and push to main for frontend/mobile/** and the workflow file itself, plus workflow_dispatch
  • Node 20 with npm cache keyed on frontend/mobile/package-lock.json
  • npm ci || npm install, then npx tsc --noEmit, npx expo lint, npm test -- --ci
  • concurrency cancels superseded runs

This PR touches frontend/mobile/**, so the workflow exercises itself here.

#500 — EAS profiles and store metadata

eas.json:

Profile Distribution Android artifact Use
development internal APK dev client, Metro attached
preview internal APK installable QA build
production store AAB Play upload, autoIncrement

appVersionSource: remote, so EAS owns versionCode/buildNumber and app.json
carries only the user-facing version. expo-dev-client is added because the
development profile is unusable without it. android.package and
ios.bundleIdentifier are set to app.veil.wallet, and there are
build:android:* npm scripts.

All three profiles pin EXPO_PUBLIC_NETWORK=testnet deliberately — a store build
pointed at mainnet contracts that are not deployed yet is worse than no store
build. Everything account-specific (EXPO_PUBLIC_WC_PROJECT_ID,
EXPO_PUBLIC_PASSKEY_RP_ID) belongs in EAS secrets, documented rather than
committed.

Store metadata under frontend/mobile/store/:

  • README.md — build commands, secrets, application id, asset spec table, screenshot shot list, pre-launch checklist
  • android/play-listing.md — name/short/full description within Play's limits, release notes, data safety answers derived from what the code actually does, content rating notes
  • ios/app-store-listing.md — drafted, explicitly blocked on the Apple account and associatedDomains (backlog feat: implement useInvisibleWallet React hook for WebAuthn-powered … #18)

Not verified: eas build -p android --profile preview needs an Expo account,
eas init for a project id, and Play App Signing for assetlinks.json
(backlog #19). The config and metadata are what this PR can deliver; the build
itself has to be run by someone holding those credentials.

#475 — Earn screen

app/earn.tsx replaces the placeholder with the ported flow, and lib/blend.ts
mirrors frontend/wallet/lib/blend.ts (V2 pool contract with a V1 fallback,
EXPO_PUBLIC_BLEND_POOL_IDS, @blend-capital/blend-sdk@^3.3.0 matching the
wallet).

  • pools list with average supply APY and total liquidity; existing positions with deposited and accrued interest
  • deposit form with a one-year estimate; withdraw redeems the full bToken balance
  • requirePasskey() (new in lib/passkey.ts) asserts over a fresh 32-byte random challenge, so the biometric prompt actually proves presence rather than decorating the flow
  • lib/sorobanTx.ts simulates, assembles, signs with the fee-payer key, submits and polls to success
  • an unreachable pool is dropped from the list instead of blanking the screen
  • with nothing configured the screen says so, naming the variable to set

Differences from the web page, all forced by the platform: wallet address and
fee-payer secret come from the keychain (expo-secure-store) instead of
session/local storage, and the beginTx/endTx inactivity-lock hooks have no
mobile equivalent yet.

Checks

npx tsc --noEmit   → clean
npx expo lint      → clean
npx jest           → 7 suites, 121 tests passed

9 of those tests are new, covering the Blend read path: APY averaging, supply
summing, the name fallback, the V1 fallback, dropping a dead pool, empty-reserve
pools reporting 0 rather than NaN, filtering reserves with no position, and
never reporting negative accrued interest.

Known pre-existing blocker (not addressed here)

A full native bundle currently fails, on main as well as on this branch:

Unable to resolve module url from .../node_modules/eventsource/lib/eventsource.js

@stellar/stellar-sdk resolves through its exports default condition to the
Node build, which pulls eventsource, which requires Node's url. Reproduced
with an entry importing only the app's own lib/network.ts, so it is unrelated
to SDK resolution — it blocks any Android bundle today, which is also why
eas build cannot be smoke-tested from here even with credentials. Left out of
this PR because the fix is a product decision (browser condition vs the SDK's
no-eventsource entry) affecting all app code, not just the workspace SDK.
Happy to file it separately.

Metro only watched frontend/mobile, so `invisible-wallet-sdk` was unreachable.
Watch the repo root and sdk/, search the app + hoisted + SDK node_modules, and
follow the workspace symlink so the package's "react-native": "src/index.ts"
entry point is what gets bundled.

react-native-passkey is the SDK's declared peer for the native WebAuthn path;
without it webauthn.native.ts cannot resolve, so add it to the app.
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Salmatcre8 is attempting to deploy a commit to the miracle656's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Salmatcre8 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

PRs touching frontend/mobile had no coverage — regressions only showed up after
merge. A dedicated workflow keeps the Expo install off unrelated builds via a
paths filter.
Android first: its passkey domain binding is self-serve, so nothing blocks a
build. iOS listing is drafted but gated on the Apple account.

Profiles are testnet-only on purpose — a store build pointed at mainnet
contracts that are not deployed yet is worse than no store build.
Replaces the placeholder route with the ported earn flow: configured pools with
their average supply APY, the wallet's existing positions, and supply/withdraw
that the device passkey gates before the fee-payer key signs and submits.

An unreachable pool is dropped from the list rather than blanking the screen, so
one dead RPC cannot take earn down.
@Salmatcre8
Salmatcre8 marked this pull request as ready for review July 30, 2026 12:31
@Salmatcre8
Salmatcre8 requested a review from Miracle656 as a code owner July 30, 2026 12:31
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.

72. EAS build + store metadata 47. Earn screen 11. CI workflow for mobile 2. Metro monorepo config

1 participant