Skip to content

feat(dashpay): authorize a token purchase from a DashConnect link - #1109

Open
romchornyi wants to merge 3 commits into
feat/devnet-supportfrom
feat/dashconnect-token-purchase
Open

feat(dashpay): authorize a token purchase from a DashConnect link#1109
romchornyi wants to merge 3 commits into
feat/devnet-supportfrom
feat/dashconnect-token-purchase

Conversation

@romchornyi

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

A dash-st: link was parsed strictly as an IdentityUpdateTransition — the DashConnect key-registration step — so a dApp asking the wallet to authorize a token purchase failed outright with Expected IdentityUpdateTransition, got Batch(...TokenDirectPurchase...).

Yappr's fallback without a wallet path was to ask the user to paste their CRITICAL private key into a web page, which is precisely what DashConnect exists to prevent — the approval sheet's own promise is that the app never sees your keys.

Depends on dashpay/platform#4584, which adds the parser this uses. Merge that first; this branch does not build against a v4.2-dev without it.

What was done?

  • The dash-st path parses once through the SDK's new parseStateTransition and switches on the kind, rather than branching on a thrown "expected X, got Y" error. Identity updates keep today's key-registration behaviour unchanged, including its wrong-identity, unexpected-mutation and mismatched-derived-key validation; the protocol method became handleStateTransition and the old body is now a private continuation, so no rule moved.
  • A token purchase gets its own approval sheet (ApproveTokenPurchaseSheet) showing what is being authorized: token count, the total price rendered in DASH, the token id, and which identity of the user's will be charged.
  • The wallet never signs the bytes it was handed. It reads the intent from them, shows it, and rebuilds the purchase through tokenPurchase, passing the parsed price as expectedTotalCost so the amount shown and the amount charged cannot diverge.
  • Ownership is checked before the sheet is built and again immediately before signing, since the sheet can sit open while the active identity changes.
  • Signing uses a CRITICAL key, which the SDK's signer selects itself (a HIGH login key can post but not spend). An identity without one fails with a visible message rather than a silent no-op.
  • approveLogin gained step markers. It logged that it started and then went silent until it finished, so a stall in key derivation, the Platform write or the connection preview looked identical from a log.
  • Two helpers shared with the connection sheet were promoted out of it rather than copied.

Also included, because the branch builds against a bumped platform: the WalletStartupStatus switch fix. It is submitted separately as #1108 against develop, since develop does not currently compile against v4.2-dev head without it; the copy here is what lets this branch build and will fold away once #1108 lands.

How Has This Been Tested?

xcodebuild -workspace DashWallet.xcworkspace -scheme dashpay -sdk iphonesimulator ARCHS=arm64 — succeeds.

End to end on a device against devnet-moutai: a real Yappr dash-st: purchase link opens the wallet, the sheet renders 100 tokens for 0.001 DASH against the correct identity, and the purchase completes on chain. The price shown was cross-checked against Yappr's own screen independently.

Getting there also surfaced, and required, a platform bump to v4.2-dev head: an older pin could not read the rankedCountable at-form introduced by dashpay/platform#4531, which Yappr's token contract uses, and failed with Proof verification error: rankedCountable value must be a boolean.

Not covered: the unit-test target is the known-broken one described in CLAUDE.md, so tests compile but were not run. The identity-update (dash-st key registration) path was verified by reading the final code rather than re-exercised, since its body is unchanged.

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

`WalletStartupStatus` gained `seedBindingUnverified` and
`identityScanIncomplete`, so the DashPay readiness log stopped compiling.

Both are logged as warnings rather than errors: unlike `discoveryFailed`,
which is a local fault nothing will clear on its own, each of these is
recoverable on a later start — a rerun with the matching signer completes the
queued contact-account work, and an incomplete identity scan stays on record
so the next launch re-scans instead of taking the warm shortcut.
A `dash-st:` link was parsed strictly as an `IdentityUpdateTransition` — the
DashConnect key-registration step — so a dApp asking the wallet to authorize a
token purchase failed with "Expected IdentityUpdateTransition, got Batch(...)".
Yappr's own fallback was to ask the user to paste a CRITICAL private key into a
web page, which is exactly what DashConnect exists to avoid.

The `dash-st` path now parses once through the SDK's new `parseStateTransition`
and switches on the kind rather than on a thrown error. Identity updates keep
today's key-registration behaviour unchanged, including its wrong-identity,
unexpected-mutation and mismatched-key validation. A token purchase gets an
approval sheet showing what is being authorized — token count, the total price
rendered in DASH, and which identity of the user's will be charged — and only
then signs.

The wallet never signs the bytes it was handed. It reads the intent from them,
shows it, and rebuilds the purchase through `tokenPurchase`, passing the parsed
price as `expectedTotalCost` so the amount shown and the amount charged cannot
diverge. Ownership is checked before the sheet is built and again immediately
before signing, since the sheet can sit open while the active identity changes.
Signing uses a CRITICAL key, which the signer selects itself; an identity
without one fails with a visible message rather than a silent no-op.

Two helpers shared with the connection sheet were promoted out of it rather
than copied.
`approveLogin` logged that it started and then went silent until it finished,
so a stall anywhere across key derivation, the Platform document write and the
connection preview looked identical from a log — the whole approval was one
opaque span.

Adds four markers around the steps that can block, which is what let a report
of "the QR never disappears" be narrowed to the wallet having completed its
side and the dApp not picking the document up.
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 739d2aff-9b23-4580-9473-92ddb3518456

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw

thepastaclaw commented Sep 2, 2026

Copy link
Copy Markdown

🕓 Ready for review — 12 ahead in queue (commit 43bfabd)
Queue position: 13/48 · 2 reviews active
ETA: start ~12:26 UTC · complete ~13:36 UTC (median 1h 10m across 30 recent reviews; 2 slots)
Queued 4d 5h ago · Last checked: 2026-09-07 04:40 UTC

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