Skip to content

feat(platform-wallet): parse a state transition's kind and token-purchase intent - #4584

Open
romchornyi wants to merge 1 commit into
v4.2-devfrom
feat/parse-state-transition
Open

feat(platform-wallet): parse a state transition's kind and token-purchase intent#4584
romchornyi wants to merge 1 commit into
v4.2-devfrom
feat/parse-state-transition

Conversation

@romchornyi

@romchornyi romchornyi commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

DashConnect's dash-st: link hands a wallet a serialized state transition. The only parser exposed was platform_wallet_parse_identity_update_transition, which accepts an IdentityUpdateTransition and nothing else — the DashConnect key-registration step.

A dApp asking a wallet to authorize a token purchase was therefore rejected outright:

Expected IdentityUpdateTransition, got Batch(V1(BatchTransitionV1 { ...
  transitions: [Token(DirectPurchase(V0(TokenDirectPurchaseTransitionV0 {
    ... token_count: 100, total_agreed_price: 100000000 })))] ... }))

Observed against a real dApp (Yappr on a devnet). Its fallback without this was to ask the user to paste a CRITICAL private key into a web page, which is what DashConnect exists to avoid.

What was done?

  • New packages/rs-platform-wallet-ffi/src/parse_state_transition.rs exposing platform_wallet_parse_state_transition / ..._free. It deserializes once and reports which kind it found alongside the matching payload, so a caller branches on a discriminant instead of on a thrown "expected X, got Y" error.
  • ParsedStateTransitionFFI carries kind (NONE / IDENTITY_UPDATE / TOKEN_DIRECT_PURCHASE, exported as #defines) plus both payloads by value; exactly one is populated. ParsedTokenDirectPurchaseFFI is a POD struct with owner_id, data_contract_id, token_id, token_contract_position, token_count and total_agreed_price — what platform_wallet_token_purchase needs, plus what a user must see before approving.
  • identity_update.rs keeps its existing public contract untouched, so current callers keep working; its tagged/tagless framing fallback is generalized to several candidate variant tags and shared through deserialize_transition_with_flexible_framing.
  • Swift: ParsedStateTransition / ParsedTokenPurchaseTransition and ManagedPlatformWallet.parseStateTransition(_:) beside the existing identity-update pair.

Deliberately a parser, not a signer. There is no entry point that signs caller-supplied bytes. The intended flow is parse → show the intent to the user → rebuild the purchase through the existing platform_wallet_token_purchase, so a wallet only ever signs a transition it constructed itself.

Rejected rather than projected, because a user cannot meaningfully approve them and the rebuild path could not reproduce them faithfully: a batch that is not exactly one token purchase (empty, several, or another transition kind), and a purchase carrying using_group_info, which the rebuild submits without.

How Has This Been Tested?

  • cargo test -p platform-wallet-ffi --lib — 307 passed, including 10 new tests in parse_state_transition::tests: tagged and tagless purchase, identity update through the umbrella parser, and rejection of multi-transition, empty, non-purchase, group-gated and malformed input, plus a free-is-safe case. The 7 existing identity_update tests guard the framing refactor and still pass, including the real captured fixture.
  • cargo check -p platform-wallet-ffi and cargo clippy -p platform-wallet-ffi --lib clean.
  • packages/swift-sdk/build_ios.sh --target ios --target sim — xcframework rebuilt for both slices; the generated header carries the new symbols and kind constants, and the script's SwiftExampleApp build (-warnings-as-errors) compiles the modified ManagedPlatformWallet.swift.
  • End to end on a devnet: an iOS wallet consuming this parses a real Yappr dash-st: purchase link, renders 100 tokens for 0.001 DASH, and completes the purchase.

Breaking Changes

None. Everything added is new surface; platform_wallet_parse_identity_update_transition is unchanged.

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 added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

Summary by CodeRabbit

  • New Features
    • Added support for parsing Dash state-transition payloads through the platform wallet.
    • State transitions can now be identified as identity updates or token direct purchases.
    • Added support for both standard tagged and tagless payload framing.
    • Added Swift SDK types for token purchase details and parsed state transitions.
    • Invalid, unsupported, or malformed transitions are rejected safely.

…hase intent

DashConnect's `dash-st:` link hands a wallet a serialized state transition.
The only parser exposed was `platform_wallet_parse_identity_update_transition`,
which accepts an `IdentityUpdateTransition` and nothing else — the DashConnect
key-registration step. A dApp asking a wallet to authorize a token purchase was
rejected with "Expected IdentityUpdateTransition, got Batch(...)".

Adds `platform_wallet_parse_state_transition`, which deserializes once and
reports which kind it found alongside the matching payload, so a caller
branches on a discriminant rather than on a thrown error. Two kinds are
recognised: identity update (projected exactly as before) and a batch carrying
a single token direct purchase, projected to the fields a purchase needs and a
user must see — owner, data contract, token id and position, count, and total
agreed price.

Deliberately a parser, not a signer. There is no entry point that signs
caller-supplied bytes: a wallet reads the intent, shows it, and rebuilds the
purchase through the existing `platform_wallet_token_purchase`, so it only ever
signs a transition it constructed itself.

Rejected rather than projected, because a user cannot meaningfully approve them
and the rebuild path could not reproduce them faithfully: a batch that is not
exactly one token purchase (empty, several, or another transition kind), and a
purchase carrying `using_group_info`, which the rebuild submits without.

`identity_update.rs` keeps its narrow public contract; its tagged/tagless
framing fallback is generalized to several candidate variant tags and shared.

Swift: `ParsedStateTransition` / `ParsedTokenPurchaseTransition` and
`ManagedPlatformWallet.parseStateTransition(_:)` beside the existing
identity-update pair.
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: e8290271-0c00-40eb-800f-1041281f7efe

📥 Commits

Reviewing files that changed from the base of the PR and between 974b941 and a77c557.

📒 Files selected for processing (4)
  • packages/rs-platform-wallet-ffi/src/identity_update.rs
  • packages/rs-platform-wallet-ffi/src/lib.rs
  • packages/rs-platform-wallet-ffi/src/parse_state_transition.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The Rust FFI adds flexible parsing for identity updates and token direct purchases. The Swift SDK exposes parsed state-transition types and maps FFI results to Swift values.

Changes

State Transition Parsing

Layer / File(s) Summary
FFI framing and parser entrypoint
packages/rs-platform-wallet-ffi/src/identity_update.rs, packages/rs-platform-wallet-ffi/src/parse_state_transition.rs, packages/rs-platform-wallet-ffi/src/lib.rs
The FFI accepts tagged and Yappr-style tagless framing. It dispatches identity updates and direct-purchase batches, initializes output state, and provides safe cleanup.
Direct-purchase projection and validation
packages/rs-platform-wallet-ffi/src/parse_state_transition.rs
The parser extracts fields from exactly one TokenDirectPurchase. Tests cover supported framing, invalid batches, unsupported transitions, malformed bytes, and repeated freeing.
Swift state-transition API
packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift
The Swift SDK adds ParsedTokenPurchaseTransition, ParsedStateTransition, and parseStateTransition(_:). Identity-update marshalling uses a shared helper.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to a77c5

This change lets wallets parse and display token-purchase intents, but ambiguous framing and the lack of a built-in binding between displayed purchase details and later signing inputs could allow an incorrect intent to be approved if a consumer mishandles the values. The change is mergeable with explicit security-owner awareness and follow-up on canonical framing and approval binding.

Sequence Diagram(s)

sequenceDiagram
  participant ManagedPlatformWallet
  participant RustFFI
  participant StateTransitionParser
  ManagedPlatformWallet->>RustFFI: Call platform_wallet_parse_state_transition
  RustFFI->>StateTransitionParser: Parse tagged or tagless bytes
  StateTransitionParser-->>RustFFI: Return parsed kind and fields
  RustFFI-->>ManagedPlatformWallet: Return ParsedStateTransitionFFI
  ManagedPlatformWallet->>RustFFI: Free FFI output
  ManagedPlatformWallet-->>ManagedPlatformWallet: Build ParsedStateTransition
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: parsing state-transition kinds and token-purchase intent for the platform wallet.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/parse-state-transition

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
Collaborator

🕓 Ready for review — 46 ahead in queue (commit a77c557)
Queue position: 47/58 · 2 reviews active
ETA: start ~07:54 UTC · complete ~08:49 UTC (median 54m across 30 recent reviews; 2 slots)
Queued 11h 58m ago · Last checked: 2026-09-03 10:50 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