Skip to content

feat(MWA): add sign_and_send_transactions#279

Open
mstevens843 wants to merge 4 commits into
magicblock-labs:mainfrom
mstevens843:feat/sign-and-send-txs
Open

feat(MWA): add sign_and_send_transactions#279
mstevens843 wants to merge 4 commits into
magicblock-labs:mainfrom
mstevens843:feat/sign-and-send-txs

Conversation

@mstevens843
Copy link
Copy Markdown
Contributor

@mstevens843 mstevens843 commented Apr 16, 2026

NOTE: This PR adds the MWA 2.0 sign_and_send_transactions method to the Unity SDK. The wallet signs the transaction and submits it to the network in one step, returning the signature. All tested
wallets work except Backpack, which crashes due to a deserialization bug on their side.

Problem

The MWA 2.0 spec defines sign_and_send_transactions as a native wallet method that lets the wallet handle both signing and network submission. The Unity SDK only had sign_transactions, which meant developers had to sign through the wallet, get the signed transaction back, then submit it themselves via RPC. The React Native SDK already supports signAndSendTransactions out of the box.

Solution

Added sign_and_send_transactions support across the full stack:

  • SignAndSendOptions class in JsonRequest.cs with all spec fields (min_context_slot, commitment, skip_preflight, max_retries, wait_for_commitment_to_send_next_transaction)
  • SignAndSendResult response model with base64 signature decoding
  • SignAndSendTransactions() on the client and interface
  • SignAndSendTransaction() override on SolanaMobileWalletAdapter that opens a session, does auth/reauth, then calls sign_and_send_transactions
  • SignAndSendTransaction() delegate on SolanaWalletAdapter

The override automatically fetches minContextSlot from the latest blockhash before sending to the wallet. This is needed because Phantom has a known bug (solana-mobile/mobile-wallet-adapter#1146) where it opens but never shows the approval dialog if minContextSlot is not set in the
options. The spec says it's optional, but Phantom requires it. Fetching it automatically means developers don't have to think about it.

The returned signature is converted from base64 to base58 so it matches what Solana explorers and RPC expect.

Test Results

Tested on Solana Seeker (Android 15):

All wallets work except Backpack. Jupiter, Solflare, Phantom, and Seed Vault all successfully sign, submit, and return valid base58 transaction signatures.

Phantom originally failed with the same symptom described in issue #1146. It would open the wallet but never show an approval prompt. After adding the automatic minContextSlot fetch, Phantom works correctly and shows the approval dialog as expected.

Backpack crashes inside its own Kotlin code with JsonDecodingException: Class discriminator was missing in SolanaMobileWalletAdapterWalletLibModule. This is a Backpack deserialization bug. The transaction payload we send is identical to what Jupiter and Solflare receive and handle fine. There are no open issues or workarounds for this on Backpack's side.

Deploy Notes

No new dependencies. No new scripts. Non-breaking addition. The existing sign_transactions path is unchanged.

Summary by CodeRabbit

  • New Features

    • Single-step sign-and-send from wallet adapters with configurable options (commitment level, skip preflight, max retries, wait-for-commitment, context slot) and returned transaction signatures.
  • Behavior Changes

    • Signing/sending failures surface as non-throwing results containing error details instead of throwing exceptions.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@mstevens843 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 31 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7e632123-7e66-4a53-b20c-df5ab8216d05

📥 Commits

Reviewing files that changed from the base of the PR and between 591ea8a and 9589a54.

📒 Files selected for processing (1)
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs

Walkthrough

Adds sign-and-send transaction support: new interface method, request options and result models, client adapter RPC call, and wallet adapter overrides implementing authorization, context-slot lookup, transaction submission, and signature conversion.

Changes

Cohort / File(s) Summary
Interface Contract
Runtime/codebase/SolanaMobileStack/Interfaces/IAdapterOperations.cs
Added [Preserve] method Task<SignAndSendResult> SignAndSendTransactions(IEnumerable<byte[]> transactions, JsonRequest.SignAndSendOptions options);.
JSON-RPC Request Types
Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs
Added Options property and nested SignAndSendOptions with nullable fields (MinContextSlot, Commitment, SkipPreflight, MaxRetries, WaitForCommitmentToSendNextTransaction) serialized under "options".
JSON-RPC Response Model
Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/SignAndSendResult.cs, Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/SignAndSendResult.cs.meta
Added SignAndSendResult class with List<string> Signatures and computed List<byte[]> SignaturesBytes (base64 → bytes). Included Unity .meta for new file.
Client RPC Caller
Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
Added SignAndSendTransactions method that encodes transaction bytes to base64, attaches options, builds JSON-RPC request (sign_and_send_transactions) and dispatches expecting SignAndSendResult.
Wallet Adapter Implementations
Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs, Runtime/codebase/SolanaWalletAdapter.cs
Added high-level SignAndSendTransaction override(s) implementing authorization/reauthorization flow, fetching latest block hash to set MinContextSlot, partial signing, submission, base64→base58 signature conversion, and error/exception-to-result handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding the sign_and_send_transactions method to the MWA implementation.
Description check ✅ Passed The description covers all required sections: Problem, Solution, Test Results, and Deploy Notes. It provides clear technical details about the implementation, testing results across multiple wallets, and compatibility notes.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 193-200: The code silently swallows exceptions from
ActiveRpcClient.GetLatestBlockHashAsync and proceeds to call
sign_and_send_transactions without min_context_slot; change this to surface
failures so we don't send incompatible requests. Replace the empty catch around
the GetLatestBlockHashAsync call (the contextSlot lookup) with logic that either
rethrows the exception or returns an explicit error/result to the caller (so the
caller of the method that constructs the sign_and_send_transactions request will
not proceed), and ensure any logging uses a clear message including the
exception. Locate the block using ActiveRpcClient.GetLatestBlockHashAsync and
the contextSlot variable and make the method fail-fast instead of continuing
when that call fails so min_context_slot is never omitted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2d282995-d3c6-40bf-8cbe-91f22b5a22d6

📥 Commits

Reviewing files that changed from the base of the PR and between 054503e and b6f1a3b.

📒 Files selected for processing (7)
  • Runtime/codebase/SolanaMobileStack/Interfaces/IAdapterOperations.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/SignAndSendResult.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/SignAndSendResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
  • Runtime/codebase/SolanaWalletAdapter.cs

Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs Outdated
…ctions

Previously the GetLatestBlockHashAsync exception was swallowed and the
request proceeded without min_context_slot, which is the exact gap this
PR is meant to close for Phantom compatibility. Surface the error instead
so the caller does not silently send a request the wallet will reject.
@mstevens843 mstevens843 force-pushed the feat/sign-and-send-txs branch from b6f1a3b to 591ea8a Compare April 27, 2026 20:16
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 295-306: In SignAndSendTransaction, the catch that currently
rethrows after failing to fetch the latest block hash (catch around
ActiveRpcClient.GetLatestBlockHashAsync and variable contextSlot) should not
rethrow; instead log the error and leave contextSlot as null so the method
continues and returns a RequestResult<string> failure consistently like the
later failure path; remove the "throw" and ensure the catch only logs (e.g.,
Debug.LogError) and preserves/sets contextSlot = null; apply the same
non-throwing change to any similar catch blocks in this method (the later
failure handling region around the RequestResult<string> return).
- Around line 277-283: The code dereferences authorization and unconditionally
assigns _authToken after checking result.WasSuccessful, which can overwrite a
valid persisted token when authorization is null or contains no AuthToken;
update the post-success logic in the method handling result/res so that you
first guard that authorization is not null and authorization.AuthToken is not
null/empty before assigning _authToken, only set _authToken when a real token
exists, and preserve the existing token otherwise (do not clear or overwrite
it); reference the variables/methods result, authorization, _authToken, and the
returned res to find and adjust the logic accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2b6a2001-22b8-430d-9da6-425cc39a9d1b

📥 Commits

Reviewing files that changed from the base of the PR and between b6f1a3b and 591ea8a.

📒 Files selected for processing (7)
  • Runtime/codebase/SolanaMobileStack/Interfaces/IAdapterOperations.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/SignAndSendResult.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/SignAndSendResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
  • Runtime/codebase/SolanaWalletAdapter.cs

Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
- guard _SignAndSendAllTransactions against null authorization and avoid
  wiping a valid session token when the wallet returns no fresh token
- return a failure RequestResult<string> on context-slot fetch failure
  instead of throwing, matching the rest of SignAndSendTransaction
@mstevens843
Copy link
Copy Markdown
Contributor Author

Pushed another round of fixes to address the new CodeRabbit feedback after the rebase.

  • _SignAndSendAllTransactions now guards against a null authorization and only updates _authToken when the wallet actually returns a fresh one, matching the pattern in _Login (no more wiping a valid keep-alive session if the wallet
    response omits the token) - The context-slot fetch failure now returns a failure RequestResult with WasHttpRequestSuccessful=false and a clear Reason, instead of throwing. Same outcome (caller sees a clean failure instead of silently sending without
    min_context_slot) but consistent with how the rest of SignAndSendTransaction handles errors

Ready for another look when you have time.

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.

1 participant