Skip to content

Add deep linking for pool positions and payment requests - #121

Merged
0takuc0mrade merged 10 commits into
Nodus-protocol:mainfrom
Jaydbrown:feat/deep-linking
Jul 9, 2026
Merged

Add deep linking for pool positions and payment requests#121
0takuc0mrade merged 10 commits into
Nodus-protocol:mainfrom
Jaydbrown:feat/deep-linking

Conversation

@Jaydbrown

Copy link
Copy Markdown
Contributor

Summary

Implements "Implement deep linking for sharing pool positions and payment requests." Custom URI scheme only (nodusprotocol://) — see note on https://nodus.fi App Links below.

  • nodusprotocol://pool — opens the (single) AMM pool's detail screen. This app has exactly one pool (PoolProvider always loads a single PoolStats, no multi-pool routing exists), so there's no pool ID to thread through.
  • nodusprotocol://pay?to=<address>&amount=<decimal>&token=<symbol> — opens a new PaymentRequestScreen showing the request (amount, token, requester address, copy-to-clipboard). This app has no peer-to-peer send flow yet, so this deliberately doesn't pretend to fulfill the request — it's a real, complete "view a payment request" feature, not a stub for a send flow that doesn't exist.
  • AppDeepLink/parseDeepLink: a sealed-class model and pure parsing function, validating the address via the existing Validation.stellarPublicKey and rejecting non-positive/non-numeric amounts. Fully unit tested.
  • DeepLinkService: wraps app_links, constructed early in main() (before runApp) so it can capture a cold-start launch link, buffering every link it receives and replaying them once to the first subscriber.
  • DeepLinkListener: the piece that actually reacts to links and navigates. Security-relevant design choice: it's placed as AppLockGate's child rather than wrapping it, so its subscription — and any navigation it triggers — doesn't exist in the widget tree until the app has actually unlocked. A deep link arriving while the app is locked just sits in DeepLinkService's buffer instead of being able to navigate past the biometric lock.
  • Android: registered a nodusprotocol:// intent-filter. https://nodus.fi App Links would need a hosted .well-known/assetlinks.json for domain verification, which is out of scope for this repo (no server-side access).

Test plan

  • parseDeepLink unit tests: scheme/host rejection, well-formed pool + payment links, payment requests rejected for missing param / invalid address / non-numeric or non-positive amount
  • DeepLinkListener widget tests: pushes PaymentRequestScreen for a payment link, stays put with no events. The pool-position path isn't covered by a widget test — it reads PoolProvider, which always constructs a real network-backed PoolService with no test seam — reviewed by hand instead.
  • Could not run flutter analyze / flutter test locally — same sandbox limitation as the last two PRs. Please watch CI closely.

Note on account access

This PR is from my personal fork rather than a branch on the org repo — my org access was revoked partway through this session (confirmed via gh api repos/.../permissions: push: false where it was true earlier). Everything else about the change is unaffected.

Jaydbrown added 10 commits July 9, 2026 18:44
Custom-scheme only: https://nodus.fi App Links would need a hosted
.well-known/assetlinks.json for domain verification, which is out of
scope for this repo.
Sealed class with two recognized link kinds: PoolPositionLink and
PaymentRequestLink (validated address + positive amount). Parsing is
a pure function so it's unit testable without a real platform
channel.
Buffers every parsed link since construction and replays them once to
the first subscriber, then streams new ones live. Meant to be
constructed early (in main()) so app_links can capture a cold-start
launch link, while actually acting on it is deferred to whenever
something chooses to subscribe.
Read-only preview of a nodusprotocol://pay request -- amount, token,
requester address, copy-to-clipboard. This app has no peer-to-peer
send flow yet, so it deliberately doesn't pretend to fulfill the
request, only display it.
Reacts to deep links by pushing PoolDetailScreen or
PaymentRequestScreen. Deliberately meant to be placed as
AppLockGate's child rather than wrapping it: this widget's
subscription doesn't exist until the gate has actually unlocked, so a
deep link can never navigate past the biometric lock -- any link
that arrives before then just sits in DeepLinkService's buffer.
Threaded through AMMobileApp so it's constructed before runApp(),
letting app_links capture the link that launched the app from a cold
start. Not yet wired into the widget tree.
Placed as AppLockGate's child so its subscription -- and any
navigation it triggers -- only ever exists once the app is unlocked.
Covers scheme/host rejection, a well-formed pool link, a well-formed
payment request, and payment requests rejected for a missing
parameter, invalid address, non-numeric amount, or zero/negative
amount.
Covers pushing PaymentRequestScreen for a payment request link and
staying put with no events. The pool-position path isn't covered
here since it reads PoolProvider, which has no test seam (always
constructs a real, network-backed PoolService) -- reviewed by hand
instead.
@0takuc0mrade
0takuc0mrade merged commit b2a3af2 into Nodus-protocol:main Jul 9, 2026
1 check passed
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.

2 participants