38. SEP-7 pay URI + deep link - #518
Conversation
Adds lib/sep7.ts (ported from frontend/wallet/lib/sep7.ts) to parse both web+stellar:pay?... URIs and this app's own veil://pay?... deep link scheme, and wires a Linking listener into the root layout that routes a matched link to /send with the destination/amount/asset/memo pre-filled. Registers "web+stellar" as an additional URL scheme in app.json so the OS opens such links in this app. A minimal send.tsx placeholder renders whatever got pre-filled; the interactive recipient/amount form and submit flow are separate follow-up issues.
|
@ibochivincent-lang is attempting to deploy a commit to the miracle656's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@ibochivincent-lang 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! 🚀 |
Miracle656
left a comment
There was a problem hiding this comment.
You flagged that you couldn't run the toolchain, so — same as on your #516 — I ran it:
$ git merge origin/main # clean, no conflicts
$ npm run typecheck # tsc --noEmit
TYPECHECK_EXIT=0
Clean. And thank you again for marking the unrun checks unticked rather than assuming.
The parsing work is careful. parseDeepLink returning null for an unrelated link like veil://settings, so it falls through to normal expo-router handling instead of swallowing it, is the correct instinct and easy to get wrong.
There's a large overlap problem though, and it isn't your fault — I handed out two issues covering the same surface.
Overlap with #508
#508 (Closes #498, deep-linking config) is open and implements inbound deep-link resolution too, via lib/deepLinks.ts. It already handles SEP-7:
export const SEP7_SCHEME = 'web+stellar';
const SEP7_PARAM_MAP: Record<string, string> = { ... };
if (scheme === SEP7_SCHEME) { ... }
// resolveDeepLink('web+stellar:pay?destination=GABC') // '/pay?to=GABC&uri=…'Two differences matter for deciding which survives:
- #508 uses
app/+native-intent.ts, expo-router's official interception hook, rather than a manualLinking.getInitialURL()+addEventListener('url')pair in_layout.tsx. The manual approach has to hand-manage the cold-start/warm-resume split and is prone to double-handling the initial URL;+native-intentis given the URL by the router and returns a route. - #508 ships 246 lines of tests (
deepLinks.test.ts,appConfig.test.ts). This PR has none — which you noted, correctly, since mobile has no runner configured yet.
So I'm keeping #508's resolution path. That's a call about which mechanism to standardise on, not a judgement on your code.
What I'd like from this PR
Your lib/sep7.ts has two things #508 does not have, and I want both:
export function parseQrValue(value: string): Sep7Parsed | { destination: string } | null
export function buildSep7PayUri(opts: { ... }): string#508 only resolves inbound links. It has no way to construct a SEP-7 URI — which the receive/request flow needs in order to generate a payment request — and no QR parsing. That's real, non-duplicated value.
Please narrow this PR to:
- Keep
parseQrValue,buildSep7PayUri,looksLikeStellarAddress, and theSep7Parsedtype. - Drop
parseDeepLink/parseSep7Uri/parseVeilLinkUri— #508'sresolveDeepLinkcovers these. - Drop the
Linkinglistener inapp/_layout.tsx—+native-intent.tssupersedes it, and running both means the link gets handled twice. - Drop the
app.jsonchange. #508 removesapp.jsonentirely in favour ofapp.config.ts, and already registers your scheme there:scheme: [DEEP_LINK_SCHEME, SEP7_SCHEME]withSEP7_SCHEME = 'web+stellar'. - Drop
app/send.tsx. #507 (navigation shell) provides the route stubs, and #508 routes SEP-7 links to/payrather than/send— worth syncing with @Olorunfemi20 on which target is right, since your reading (/sendprefilled) is arguably the better UX.
That leaves a focused PR: the SEP-7 construction and QR-parsing helpers that the send and receive flows will both need.
On tests
You're right that there's no runner today. #508/#510 add a Mobile — typecheck & test CI job, and #524/#525 add jest.config.js. Once one of those lands, buildSep7PayUri and parseQrValue are ideal unit-test targets — pure functions, string in, string out. Mirroring frontend/wallet/tests/sep7.fuzz.test.ts as you suggested would be very welcome as a follow-up.
Sorry for the overlapping scope — that's mine to fix, and I'll be more careful splitting the mobile backlog.
The inbound-routing half of Miracle656#466 landed with Miracle656#508: lib/deepLinks.ts already maps web+stellar:pay?... onto /pay with the SEP-7 parameters translated, and app/+native-intent.ts drives it identically on cold start and warm resume. Reduce this branch to the SEP-7 payload work that is genuinely missing: - Drop app.json (main uses app.config.ts), the Linking handler added to app/_layout.tsx (duplicates +native-intent.ts), and the root app/send.tsx (/send is owned by the (tabs) group). - Trim lib/sep7.ts to the parts deepLinks.ts does not cover: parseSep7Uri, parseQrValue, looksLikeStellarAddress and buildSep7PayUri. Remove parseVeilLinkUri/parseDeepLink so there is one owner for inbound routing. - Replace the private destination-only builder in (tabs)/receive.tsx with the shared one, so requesting a specific amount is a parameter away. - Add lib/__tests__/sep7.test.ts (14 cases) covering parsing, the QR bare address path, and a build/parse round-trip including memo encoding. tsc clean; jest 10 suites / 174 tests; expo lint clean.
|
Merging, though scoped down quite a bit — half of #466 landed underneath this branch while it was open. What was already done. #508 shipped Because of that I dropped the parts of this branch that would have been a second implementation of the same thing:
What was genuinely missing, and is what merged. The SEP-7 payload helpers on either side of routing, which
I also added Verified: Worth flagging for whoever picks up the follow-up: |
Summary
frontend/mobile/lib/sep7.ts, ported fromfrontend/wallet/lib/sep7.ts(Sep7Parsed,looksLikeStellarAddress,parseQrValue,buildSep7PayUri), plusparseDeepLinkwhich additionally accepts this app's ownveil://pay?...scheme alongside the standardweb+stellar:pay?...SEP-7 URI (seesdk/src/sep7.tsfor the fuller validated implementation the web wallet's send flow uses).frontend/mobile/app/_layout.tsx(Linking.getInitialURLfor cold start +Linking.addEventListener('url', ...)for warm start) that parses inbound links and, when adestinationis present, routes to/sendwith the destination/amount/asset/memo as route params."web+stellar"as an additional URL scheme inapp.json(alongside the existing"veil"scheme) so the OS opens such links in this app.frontend/mobile/app/send.tsxplaceholder that reads the pre-filled params and renders them, so the deep link's effect is visible — the interactive recipient/amount form + passkey submit are separate follow-up issues (backlog feat(wallet): tap a transaction in Activity tab to view full details #36, feat(wallet): add one-tap Friendbot faucet button for zero-balance testnet wallets #37).closes #466
Test plan
parseDeepLinkagainstweb+stellar:pay?destination=...&amount=...&asset_code=...&asset_issuer=...&memo=...,veil://pay?destination=..., and an unrelatedveil://settingslink (correctly returnsnullso it falls through to normal expo-router handling) — all resolve as expected.npm install && npm run typecheck/ on-device deep link test — not run: this sandbox has no Node.js installed, so I could not execute the toolchain or a real device/simulator test. Please verify before merging, and consider adding a jest suite mirroringfrontend/wallet/tests/sep7.fuzz.test.tsonce tooling is available (mobile currently has no test runner configured).