Repository navigation
feat(mobile): Freighter signing for admin contract actions, TradeDetail/WalletConnect tests, frontend docs audit (#276 #281 #283 #275) - #375
Open
lockoabosede8-byte wants to merge 28 commits into
Conversation
|
@lockoabosede8-byte 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! 🚀 |
Cover loading/not-found/loaded states, per-status actions, the confirmation alerts for deposit/confirm/release, the dispute modal, evidence navigation and the error banner actions.
Cover the first-time and returning-user render states, the manual address prompt and validation, the challenge/verify exchange, biometric unlock (success, cancel, failure) and busy-state handling.
…lite-Work#276) Replace the TODO in AdminContractScreen with a working sign-and-submit flow. A new freighterSigner service asks Freighter to sign the backend-built unsignedXdr for the configured network and submits the signed transaction via the Soroban RPC sendTransaction method (the same call the web frontend makes). SignAndSubmitXdr renders the action and its result under each XDR card. Freighter is a browser extension, so the service probes it first and degrades to a WALLET_UNAVAILABLE error with the XDR still copyable for external signing. Declined signatures, signing failures and submission failures are reported inline and can be retried. Adds EXPO_PUBLIC_STELLAR_RPC_URL (optional) and unit tests for the service, the component and the screen integration.
…ite-Work#275) Check every doc against src/app and the code it references and correct the stale parts: routes missing from the loading-state inventory and the accessibility baseline, loading.tsx/lhci claims that no longer match the repo, the removed-window.prompt claim, a missing walkthrough file, the unenforced jsx-no-literals rule, replay/409 and cache behaviour that the code does not implement, the unwired wallet state machine and the admin guard's route list.
…ile E2E) - test:scripts: run jest via node_modules/jest/bin/jest.js; pnpm's node_modules/.bin/jest is a shell wrapper that node cannot parse. - Feature Flag Catalog Drift: pin global typescript@5 (ts-node cannot load the native TypeScript 7) and compile the script as CommonJS with --transpile-only, since Node 22 otherwise loads it as ESM where __dirname is undefined. - Backend Required Gate: give 'prisma migrate deploy' the DATABASE_URL of the job's postgres service. - Mobile E2E iOS: drop the invalid getDefaultConfig spread from app.config.ts (not exported by expo/config), which made 'expo prebuild' fail to read the config. - Mobile E2E Android: install only platform-tools in setup-android; the default 'tools' package no longer exists and made sdkmanager exit 1.
… 0.76, rename detox config to .cjs
lockoabosede8-byte
force-pushed
the
feat/mobile-freighter-tests-docs-audit
branch
from
October 27, 2026 17:35
47845cf to
b11c3d2
Compare
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.
Description
Delivers four independent pieces of work in one reviewable PR (each issue was scoped to "a single reviewable PR", so they are shipped together on one branch as four commits, one per issue, which can be reviewed or reverted separately):
TradeDetailScreenWalletConnectScreenfrontend/docs/againstsrc/app/pnpm type-checkandpnpm lintpass inmobile/(0 errors; the 2 remaining warnings already exist onmain: unusedMOCK_WALLET_ADDRESSine2e/smoke.e2e.tsand unusedstyles.centerinVaultDashboard.tsx). Full mobile suite: 30 suites / 341 tests, all passing.Related Issues
src/screens/TradeDetailScreen.tsx#281src/screens/WalletConnectScreen.tsx#283frontend/docs/for staleness against current app structure #275#276 — Freighter signing for admin contract actions
AdminContractScreenhad// TODO: sign unsignedXdr with @stellar/freighter-api and submit it.and left every mediator/fee transaction unsigned. It now has a working flow.What it does. Under each
unsignedXdrcard (add-mediator and update-fee) there is a Sign & submit with Freighter button:isConnected) with a 3 s timeout.signTransaction(unsignedXdr, { networkPassphrase, address })with the connected admin wallet as the address hint (2 min ceiling for the user to approve).sendTransactionJSON-RPC call — the same callfrontend/src/app/vault/page.tsxalready makes — with a 20 s abort timeout.PENDINGandDUPLICATEboth mean the network has the transaction, so both resolve with its hash).Design decisions worth reviewing
@stellar/freighter-apionly works where the extension is reachable (e.g. the Expo web target in a browser with Freighter installed). On a native device it cannot work, so the service reports a typedWALLET_UNAVAILABLEerror and the screen keeps the existing selectable/copyable XDR so an admin can still sign externally. I verified against the real, un-mocked package in a React Native-like runtime (windowdefined): it throws internally, and the probe resolves to "unavailable" instead of hanging or crashing the app.TransactionSigningError.code):WALLET_UNAVAILABLE,USER_REJECTED,SIGNING_FAILED,SUBMISSION_FAILED. They render inline under the XDR card (not asAdminErrorBanner, because they are wallet/network problems, not backend admin errors) and the button changes to "Try signing again". After success the button is removed so a transaction cannot be double-submitted; building a new XDR resets the control.requireof@stellar/freighter-apiinside the service so merely importing the screen never initialises the extension bridge, and a package that fails to load degrades toWALLET_UNAVAILABLE. (A nativeimport()is not supported by the Jest/babel setup, which is why this is a lazyrequire.)src/constants/stellar.ts. It reuses the existingEXPO_PUBLIC_STELLAR_NETWORK(testnet|public; a full passphrase is also accepted to stay interchangeable with the frontend) and adds an optionalEXPO_PUBLIC_STELLAR_RPC_URL. Defaults are unchanged for current users (testnet)./reject|declin|denied|cancel/i), because the package does not export a stable declined-error code.Files:
src/services/freighterSigner.ts,src/components/SignAndSubmitXdr.tsx,src/constants/stellar.ts,src/screens/AdminContractScreen.tsx(TODO replaced, header/hint copy updated),src/types/env.d.ts,.env.example,.env.mobile.example,README.md,docs/admin-errors.md,src/screens/ADMIN_SCREENS.md.Lint config. Declared
AbortController(runtime global, like the already-listedfetch/URL) andglobal(test files only) ineslint.config.mjs.Tests (45 new):
freighterSigner.test.ts(30): availability (connected / not connected / wallet error / throws / never answers / package fails to load); signing (success + network passphrase + address, unavailable without prompting, four rejection phrasings, other wallet errors, empty XDR, thrown error, user never answers); submission (JSON-RPC request shape,DUPLICATE,TRY_AGAIN_LATER,ERROR,PENDINGwithout hash, RPC error, non-2xx, network failure, abort timeout); orchestration (no submit when signing fails).SignAndSubmitXdr.test.tsx(9): idle, success + hash +onSubmitted, busy state and re-entry guard, all four error codes with retry, wrapping of unexpected errors.AdminContractScreen.test.tsx(+6): no sign action before an XDR exists, signing for both sections with the admin wallet, section independence, inline error that keeps the XDR and raises no banner, result reset when a new XDR is built.Not verified: I could not run this against a real Freighter extension or a live Stellar RPC in my environment. The Freighter API and
fetchare mocked in the unit tests; the only unmocked check is the graceful-degradation path above. Reviewers with the extension installed can try it withpnpm webon testnet.#281 —
TradeDetailScreentests (40 tests)src/screens/TradeDetailScreen.test.tsxrenders the real screen and the realAdminErrorBanner, mocking only the stores and safe-area insets (same pattern asTradeListScreen.test.tsx).TradeStatus, terminal timeline steps forDISPUTED/REFUNDED.Alertis accepted (release is adestructivebutton; cancel does nothing); the action button swaps for a spinner while running; evidence navigation; the dispute modal (hidden until opened, blank reason rejected with an alert and no store call, trimmed reason submitted then modal closes and resets, cancel discards the draft).mailto:carries the screen name, error code and request id.#283 —
WalletConnectScreentests (22 tests)src/screens/WalletConnectScreen.test.tsxmocks the auth store,authApiand the biometrics util, and spies onAlert.prompt/Alert.alert.setToken→setWalletAddress→navigation.replace('TradeList')(token persisted before the wallet is marked connected); invalid input (wrong prefix, <56 chars, empty, cancelled prompt) alerts and never calls the API; challenge failure, verify failure and a message-less failure showConnection failed; busy/disabled button while the prompt is open; recovery ifAlert.promptthrows.user_cancelis silent; other failures alert "Unlock failed"; an API failure after a successful biometric check alerts and re-enables the button; both buttons disabled while unlocking; a returning user can still connect a different wallet.#275 —
frontend/docs/staleness auditI checked every claim in all 8 docs against
frontend/src/app/(25 routes) and the files, symbols, CI steps and configs they reference. Docs-only; no code changed. Corrections:loading-states.mdloading.tsxfor/dashboard,/trades/[id],/assets*; onlyapp/vault/loading.tsxexists. Also wrong:/trades/[id]usesLoadingState(it uses a bare spinner),/vault/manageand the wallet-balance widget have skeletons (they don't / don't exist),/trades/createtotals skeleton (none), admin lists useLoadingState(they useSkeletonList), 3× vs 4×SkeletonCard. Missing routes. CI snippet pinned@lhci/cli@0.13.xalthough CI already runslighthouse.ymlwith per-page configs on@lhci/cli@0.15.1. Token table listed dark values only./trades/[id],/vault/manage,/mediator/disputes/[id]), replaced the CLS/CI section with the real configs and workflow, added light-theme token values.accessibility-report.mdroutes.axe.test.tsx, which renders a minimal<h1>shell, not the real pages; 8 routes have no row; claimswindow.promptwas removed but/trades/[id]still calls it (page.tsx); points toci.yml:98(the step is now elsewhere); referencesfrontend/tests/a11y-keyboard-walkthrough.md, which does not exist.offline-resilience.mdfetchTradesruns after replay (onlyretryOnline()does);PendingBadgeis not its own file; idempotency key/lock names imprecise.offline-cache-conflict-matrix.mdcacheInvalidate; neither is implemented (the queue does not touch the cache). Still referred to the queue as a future "issue #74".offlineQueueStore/offline-resilience.md.wallet-states.mdWalletStateCalloutis not rendered by any route and nothing else imports the state machine.i18n.mdreact/jsx-no-literalsflags raw literals and the checklist said "lint-enforced"; the rule is not enabled ineslint.config.mjs.admin-route-guard.md/admin/streams/[id]; said every admin page rendersForbiddenState(only the two list pages do).adminAccess.ts.optimistic-updates.mdsrc/__tests__/tradeStore.test.ts.Everything else (store/toast/dedup APIs, TTLs and quota in
offlineCache.ts, wallet state list and telemetry, i18n module layout,frcatalog,LocaleSwitcher, backend idempotency semantics) was checked and is accurate, so it is left untouched. I did not fix any of the code gaps the audit surfaced (e.g. thewindow.prompt, the missing skeletons, the unwired wallet callout, the unenforced lint rule) — they are now documented honestly and can be filed as follow-ups.Type of Change
frontend/docs/for staleness against current app structure #275 (docs), mobile: add test coverage forsrc/screens/TradeDetailScreen.tsx#281/mobile: add test coverage forsrc/screens/WalletConnectScreen.tsx#283 (tests)Verification
Run from
mobile/:🔒 Required CI Gates
type-checkandlintinmobile/passfrontend/docs/(no source touched)PR Checklist
mobile/README.md,.env.example,mobile/docs/admin-errors.md,ADMIN_SCREENS.md, and thefrontend/docs/audit)EXPO_PUBLIC_*