Skip to content

feat(mwa-v2): complete MWA v2 rewrite with docs#285

Open
Zurcusa wants to merge 24 commits into
magicblock-labs:mainfrom
Zurcusa:pr/6-mwa-docs
Open

feat(mwa-v2): complete MWA v2 rewrite with docs#285
Zurcusa wants to merge 24 commits into
magicblock-labs:mainfrom
Zurcusa:pr/6-mwa-docs

Conversation

@Zurcusa
Copy link
Copy Markdown

@Zurcusa Zurcusa commented May 7, 2026

This is a combined PR consolidating the full MWA v2 stack for upstream review. Individual PRs with granular diffs are linked below.

Status Type ⚠️ Core Change Issues
Ready Feature Yes Closes #273, closes #189, closes #272, partial for #250

Overview

Complete rewrite of the Mobile Wallet Adapter (MWA) implementation from v1 to v2. 120 files changed, ~4200 additions across types, wire protocol, client, adapter, cache, tests, and documentation.

Individual PRs (for granular review)

PR Scope Description
#3 Types 21 new C# files: v2 domain types, interfaces, result hierarchies, exceptions
#4 Wire + Client + Cache Wire protocol v2, client rewrite with CAIP-2 chains, PlayerPrefsAuthorizationCache, response parser with size caps, 9 test suites
#5 Adapter Full SolanaMobileWalletAdapter rewrite: session lifecycle, SIWS, concurrency gate, silent reconnect, 5 test suites
#6 Documentation Quick-start, method reference, cache guide, migration guide, Backpack investigation

What Changed

Core Types (PR #3)

  • AccountInfo, AuthorizationRecord, SignInPayload, SignInResult, SendOptions
  • IAuthorizationCache interface (#272)
  • Discriminated-union result types: DeauthorizeResult, ReconnectResult, SignAndSendTxResult
  • Typed exceptions: JsonRpcException, OperationInFlightException, TransportException

Wire Protocol + Client (PR #4)

  • v2 wire format with CAIP-2 chain identifiers and backward-compat cluster field
  • AuthorizeAsync with SIWS, DeauthorizeAsync, SignAndSendTransactionsAsync, CloneAuthorizationAsync
  • AuthorizationResponseParser with permissive parsing and size caps for untrusted wallet data
  • PlayerPrefsAuthorizationCache with scoped keys, schema versioning, corrupt-JSON recovery

Adapter (PR #5)

  • New API: Disconnect(), Reconnect() (4-state result), Deauthorize() (tri-state), GetCapabilities(), LoginWithSignIn(SignInPayload), SignAndSendTransactions(Transaction[], SendOptions)
  • Silent-first login: checks cache → silent authorize → fresh authorize fallback
  • Concurrency gate: SemaphoreSlim(1,1) prevents overlapping wallet calls
  • Legacy migration: auto-migrates "pk"/"authToken" and PR#269 keys to new cache

Documentation (PR #6)

  • mwa-quick-start.md — install, configure, first connection
  • mwa-method-reference.md — all 13 public methods with signatures and result variants
  • mwa-cache-guide.md — IAuthorizationCache interface, default/custom implementations
  • mwa-migration-v1-to-v2.md — v1 → v2 migration guide
  • sign-and-send-investigation.md — Backpack failure analysis

Known Issue: Backpack sign_and_send_transactions

⚠️ sign_and_send_transactions fails with Backpack wallet. The transaction is successfully created, signed, and submitted on-chain — the issue is only in receiving the response back in the dApp. Backpack's WebSocket server crashes during the RPC submission phase, corrupting the local association before the response can be delivered. The dApp's pending request hangs indefinitely. This is a Backpack-specific issue — Phantom and Solflare handle the same flow correctly, surviving 8–12 seconds of Android activity STOP without any WebSocket errors.

sign_transactions works with all wallets including Backpack (no RPC submission involved). Full investigation: docs/sign-and-send-investigation.md

Pre-existing LocalAssociationScenario Issues

These issues exist in the upstream LocalAssociationScenario.cs and are outside the scope of this rewrite:

  1. Missing return in ExecuteNextAction (line 134-135): Falls through to _actions.Dequeue() on an empty queue after CloseAssociation().
  2. Unbounded OnClose reconnect loop (line 46-50): No delay, no retry limit, no close-awareness. Thread pool storm when the wallet server is gone.
  3. Wrong timeout unit (line 33): TimeSpan.FromSeconds(9000) = 2.5-hour timeout instead of 9 seconds.
  4. Null-unsafe scenarioResult access: scenarioResult.WasSuccessful throws if scenario returns null.

Breaking Changes

  • IAdapterOperations.Authorize() and Reauthorize() removed — use AuthorizeAsync()
  • AuthorizationResult.PublicKey and AccountLabel removed — use AuthorizationHelpers.PrimaryAccountPublicKeyBytes() and PrimaryAccount().Label

Demo App

https://github.com/Zurcusa/unity-solana-mwa-example

Testing Matrix

Tested on Pixel 7 emulator and Solana Seeker (real device).

Wallets Tested

Wallet Networks
Seed Vault (Solana Phone) DevNet, MainNet
Phantom DevNet, MainNet
Solflare DevNet, MainNet
Backpack MainNet
Jupiter MainNet

Functionality Coverage

Feature Emulator (Pixel 7) Solana Seeker
Login Passed Passed
Disconnect Passed Passed
Reconnect Passed Passed
Deauthorize Passed Passed
Sign Transaction Passed Passed
Sign All Transactions Passed Passed
Sign Message Passed Passed
Sign and Send ⚠️ Backpack fails (see Known Issue) ⚠️ Backpack fails (see Known Issue)
Get Capabilities Passed Passed
Sign-In With Solana (SIWS) Passed Passed

Summary by CodeRabbit

  • New Features

    • Persistent, pluggable authorization cache and async wallet APIs (AuthorizeAsync/DeauthorizeAsync/Disconnect/Reconnect/CloneAuthorization).
    • Sign-and-send transactions with configurable send options; LoginWithSignIn and SignMessage(string) support.
    • Richer authorization & capability responses (accounts, chains, features, sign‑in results).
  • Documentation

    • Added Quick Start, Migration v1→v2, Method Reference, Cache Guide, and troubleshooting docs.
  • Tests

    • Extensive unit tests for parsing, cache, concurrency, wire formats, and deauthorize/reconnect flows.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Warning

Rate limit exceeded

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

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ 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: 4cef991b-965c-4066-8881-67df0d7bc3bc

📥 Commits

Reviewing files that changed from the base of the PR and between dd57ff0 and 11babea.

📒 Files selected for processing (9)
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
  • Runtime/codebase/SolanaWalletAdapter.cs
  • Tests/EditMode/Contracts/IAdapterOperationsContractTests.cs
  • Tests/EditMode/Contracts/IAdapterOperationsContractTests.cs.meta
  • Tests/EditMode/JsonRpc/CapabilitiesResultTests.cs.meta
  • Tests/EditMode/Lifecycle/SolanaMobileWalletAdapterPrefsTests.cs
  • Tests/EditMode/Lifecycle/SolanaMobileWalletAdapterPrefsTests.cs.meta
  • Tests/EditMode/MwaClient/MobileWalletAdapterClientLifecycleTests.cs
  • Tests/EditMode/MwaClient/MobileWalletAdapterClientLifecycleTests.cs.meta

Walkthrough

Adds Mobile Wallet Adapter v2: async/cancellable CAIP-2 chain APIs, raw JSON‑RPC path and JsonRpcException mapping, strict authorization parser, IAuthorizationCache + PlayerPrefs default, adapter refactor with concurrency gate, session endianness fix, extensive EditMode tests/fixtures, documentation, and repo/.meta updates.

Changes

Mobile Wallet Adapter v2 refactor and ecosystem updates

Layer / File(s) Summary
Core data & contracts
Runtime/codebase/SolanaMobileStack/*
Adds account/authorization models, SignIn payload/result, SendOptions, SignAndSendTxResult, ReconnectResult, DeauthorizeResult, ITransactionPayload + LegacyTransactionPayload, LogVerbosity, RpcMethodNames, JsonRpcErrorCodes.
Authorization helpers
Runtime/codebase/SolanaMobileStack/AuthorizationHelpers.cs
PrimaryAccount() and PrimaryAccountPublicKeyBytes() with base64 and length validation.
Authorization response parser
Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs
Parses/validates v2 authorization JToken, enforces base64/length, applies field-dropping/truncation, and emits optional structured diagnostics.
JSON‑RPC client & params
Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs, JsonRpcClient/JsonRequest.cs
Adds SendRequestRaw returning JToken, raw listener/receiver, maps JSON-RPC error → JsonRpcException; extends JsonRequest.Params with chain, features, sign_in_payload, options.
MobileWalletAdapter client
Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
Implements AuthorizeAsync/DeauthorizeAsync/CloneAuthorizationAsync/SignAndSendTransactionsAsync, chain→cluster mapping, request builders, and uses SendRequestRaw/AuthorizationResponseParser.
Session encryption fix
Runtime/codebase/SolanaMobileStack/MobileWalletAdapterSession.cs
Switches AES‑GCM sequence-number bytes to big‑endian and uses raw bytes as AAD; removes tolerant MAC-exception special-case.
Authorization cache
Runtime/codebase/SolanaMobileStack/IAuthorizationCache.cs, PlayerPrefsAuthorizationCache.cs
Introduces IAuthorizationCache and default PlayerPrefsAuthorizationCache (JSON persistence, scoped key, single-session deserialization warning).
Adapter orchestration
Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Wires IAuthorizationCache, migrates legacy prefs, adds gate/semaphore for concurrent ops, Deauthorize/Disconnect/Reconnect/LoginWithSignIn/SignMessage/SignAndSend flows, maps JsonRpcException codes to SignAndSendTxResult variants.
Cross-platform wrappers
Runtime/codebase/SolanaWalletAdapter.cs
Constructor accepts optional auth-cache; adds mobile-only wrappers (Disconnect, Reconnect, Deauthorize, LoginWithSignIn, SignAndSendTransactions, CloneAuthorization, SignMessage overload).
Exceptions
Runtime/codebase/SolanaMobileStack/Exceptions/*
Adds InvalidAuthorizationException, JsonRpcException (code + JToken data), OperationInFlightException.
Tests & fixtures
Tests/EditMode/*
Comprehensive EditMode tests: parser fixtures/limits/verbosity, wire-shape tests, concurrency/gate tests, deauthorize/disconnect/reconnect, cache tests (PlayerPrefs and custom injection), and client updates for async APIs.
Docs & investigation
docs/*
Adds cache guide, method reference, migration guide, quick start, and sign‑and‑send investigation.
Repo config & Unity meta
.gitignore, Packages/BouncyCastle.Cryptography.dll.meta, many *.meta files
Adds Unity ignore rules, enriches plugin importer .meta, and creates/normalizes numerous Unity .meta files for scripts, tests, fixtures, and folders.

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

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

@Zurcusa
Copy link
Copy Markdown
Author

Zurcusa commented May 8, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 37

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/mwa-cache-guide.md`:
- Around line 50-52: The heading "Example: In-Memory Cache" is misleading
because DemoAuthorizationCache uses PlayerPrefs.GetString/SetString
(disk-backed); update the doc by either renaming the heading to clarify it's a
PlayerPrefs-backed cache (suggested: "Example: PlayerPrefs-backed Authorization
Cache" or "Example: Custom-Key PlayerPrefs Cache") and add a short note that
DemoAuthorizationCache persists to PlayerPrefs, or replace the example body with
a true in-memory implementation that mirrors InMemoryCache (as shown in
CustomCacheInjectionTest.cs) which stores state in a private field; ensure you
reference DemoAuthorizationCache and/or InMemoryCache in the text so readers
know which implementation the heading refers to.

In `@docs/mwa-method-reference.md`:
- Around line 158-159: SignAndSendTransaction currently returns a base64
signature but must return a base58-encoded transaction ID; update the code path
in SolanaMobileWalletAdapter.cs that reads success.Signatures[0] so it encodes
that signature in base58 (not base64) before populating and returning
RequestResult<string> (the value used as result.Result by SwapScreen.cs,
ReceiveScreen.cs, NftMint.cs and RPC/ConfirmTransaction calls). Locate the
conversion around success.Signatures[0] in the SignAndSendTransaction method and
replace the base64 conversion with the library/utility call that converts the
raw signature bytes to a base58 string so explorers and ConfirmTransaction
receive a proper Solana txid.

In `@docs/mwa-quick-start.md`:
- Around line 20-23: The markdown block starting with the fenced code block that
contains the JSON line for "com.solana.unity_sdk" lacks a blank line before the
opening ```; edit the docs/mwa-quick-start.md content around that paragraph and
insert a single blank line immediately before the fenced code block so the code
fence is separated from the preceding prose (satisfying MD031).

In `@docs/sign-and-send-investigation.md`:
- Around line 21-154: The markdown violates MD022/MD031/MD040: add a blank line
before and after each heading (e.g., "### Backpack (fails)", "### Phantom
(works)", etc.), ensure fenced log blocks have blank lines separating them from
surrounding text, and annotate each fenced block with a language tag such as
"text" (e.g., change ``` to ```text) so all code fences are language-tagged and
spacing around headings/fences is normalized.

In `@Packages/BouncyCastle.Cryptography.dll.meta`:
- Around line 19-24: The BouncyCastle.Cryptography.dll plugin is disabled for
the Unity Editor (the metadata shows "enabled: 0" under the Editor platform),
which prevents EditMode tests from running; to fix, edit the
Packages/BouncyCastle.Cryptography.dll.meta entry for the Editor platform and
set enabled to 1 (enable the Editor platform for the
BouncyCastle.Cryptography.dll plugin), keeping existing settings (e.g.,
DefaultValueInitialized) intact so Editor tests like
Tests/EditMode/Crypto/EcdsaSignaturesTests.cs can load the assembly.

In `@Runtime/codebase/SolanaMobileStack/AuthorizationHelpers.cs`:
- Around line 17-33: PrimaryAccountPublicKeyBytes currently calls
Convert.FromBase64String on primary.Address which can throw
ArgumentNullException for null/empty values and bypass the intended
InvalidAuthorizationException contract; add a guard at the start of
PrimaryAccountPublicKeyBytes (after retrieving primary via PrimaryAccount())
that checks string.IsNullOrWhiteSpace(primary.Address) and if true throws
InvalidAuthorizationException with an appropriate message like
"accounts[0].address is null or empty", then proceed to
Convert.FromBase64String; keep the existing FormatException catch and the
subsequent length check against PublicKeyByteLength unchanged.

In `@Runtime/codebase/SolanaMobileStack/AuthorizationRecord.cs`:
- Line 8: AuthorizationRecord.SchemaVersion currently initializes to 1 while
SolanaMobileWalletAdapter.ExpectedSchemaVersion is 2; update the default
initializer on the SchemaVersion property in the AuthorizationRecord class to =
2 so deserialized records without an explicit field match the adapter's expected
schema version and avoid unnecessary cache invalidation.

In `@Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs`:
- Around line 5-18: The base discriminated-union class DeauthorizeResult is
missing a private constructor, allowing external assemblies to derive new
variants; add a private parameterless constructor to DeauthorizeResult (e.g.,
private DeauthorizeResult() {}) inside the DeauthorizeResult class definition so
only the declared nested sealed subclasses (FullyRevoked, LocalOnly, Failed) can
exist and prevent external subclassing.

In `@Runtime/codebase/SolanaMobileStack/Exceptions/JsonRpcException.cs.meta`:
- Around line 3-11: The .meta file contains an extraneous MonoImporter block;
update JsonRpcException.cs.meta to remove the entire MonoImporter: { ... }
section and leave only the minimal required keys (fileFormatVersion and guid) so
the file matches project convention and other C# Unity .cs.meta files.

In
`@Runtime/codebase/SolanaMobileStack/Exceptions/OperationInFlightException.cs.meta`:
- Around line 3-11: The .meta file contains an unnecessary MonoImporter block;
open
Runtime/codebase/SolanaMobileStack/Exceptions/OperationInFlightException.cs.meta
and remove the entire MonoImporter section so the file only includes the minimal
Unity meta entries (fileFormatVersion and guid) per project convention, leaving
no other keys or blocks.

In `@Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs`:
- Around line 109-112: The response envelope's "result" indexer can be null,
which leads task.SetResult(envelope["result"]) to set a null into the
Task<JToken> (causing downstream NREs); update the code in JsonRpc20Client.cs
(the block that calls task.SetResult) to detect when envelope["result"] is null
and instead pass JValue.CreateNull() into task.SetResult so the returned
Task<JToken> is a non-null JToken; ensure this change is applied where the
method returns Task<JToken> and in any related path that sets the task result.
- Around line 39-50: The Debug.Log in JsonRpc20Client.SendRequestRaw leaks
sensitive wire messages in production; wrap or remove the UnityEngine.Debug.Log
call so it only runs in non-production builds (e.g., gate it behind a
preprocessor symbol such as UNITY_EDITOR or DEBUG) or remove logging of full
message entirely, keeping the rest of SendRequestRaw (message serialization,
_messageSender.Send, TaskCompletionSource and RegisterRawListener) unchanged;
ensure the symbol check surrounds only the Debug.Log line so sensitive payloads
(auth tokens, addresses) are never written to logcat in release builds.

In `@Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs`:
- Around line 78-79: The duplicate SendOptions property
([JsonProperty("sendOptions")] public JObject SendOptions) is incorrect per MWA
v2 (the correct wire key is "options" and is already represented by the Options
property); remove the SendOptions property declaration from JsonRequest.cs so
the code uses the existing Options property for send transaction options and
avoid the unused/incorrect "sendOptions" mapping.

In `@Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs`:
- Around line 1-11: The JsonRpcErrorCodes class is currently declared in the
global namespace; wrap it in the explicit namespace
Solana.Unity.SolanaMobileStack to match other MWA types and avoid naming
conflicts. Locate the public static class JsonRpcErrorCodes and add the
namespace declaration so the class (and its constants like AuthorizationFailed,
InvalidPayloads, NotSigned, NotSubmitted, TooManyPayloads, ChainNotSupported)
are declared inside Solana.Unity.SolanaMobileStack, and remove the ReSharper
disable CheckNamespace directive if no longer needed.

In `@Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs`:
- Around line 151-164: The JsonRequest being built for
RpcMethodNames.SignAndSendTransactions incorrectly sets both
JsonRequest.JsonRequestParams.Options and SendOptions to the same wireOptions,
producing duplicate "options" and "sendOptions" fields; change the builder in
MobileWalletAdapterClient.SignAndSendTransactions (the JsonRequest creation
before calling SendRequestRaw) so only Options is assigned (or set SendOptions =
null / omit it) to ensure the outgoing JSON contains a single options field per
the MWA v2 spec.

In `@Runtime/codebase/SolanaMobileStack/PlayerPrefsAuthorizationCache.cs`:
- Line 16: The private field _warnedThisSession in PlayerPrefsAuthorizationCache
is currently an instance field but must be static to enforce "once per session"
behavior and to match the test reset logic; change its declaration to "private
static bool _warnedThisSession" so the warning gate is shared across all
instances and the existing test teardown that uses BindingFlags.Static |
BindingFlags.NonPublic will correctly reset it.
- Around line 14-27: The constructor for PlayerPrefsAuthorizationCache currently
defines LegacyPkKey but never removes it from PlayerPrefs, causing the
LegacyPk_DeletedOnConstruction test to fail; update the constructors (the
parameterless PlayerPrefsAuthorizationCache() and the
PlayerPrefsAuthorizationCache(string scope) flow) to call
PlayerPrefs.DeleteKey(LegacyPkKey) and then PlayerPrefs.Save() during
construction so the legacy "pk" entry is removed and persisted when an instance
of PlayerPrefsAuthorizationCache is created.

In `@Runtime/codebase/SolanaMobileStack/ReconnectResult.cs`:
- Around line 6-19: Add a private parameterless constructor to the
ReconnectResult base class to seal the inheritance hierarchy so external
assemblies cannot derive from it; keep the nested sealed subclasses
SilentSuccess, NoCachedSession, and Failed as-is so they can still instantiate
the base. Modify the ReconnectResult type by adding a private ReconnectResult()
{ } constructor to prevent external subclassing and preserve exhaustive pattern
matching.

In `@Runtime/codebase/SolanaMobileStack/SendOptions.cs.meta`:
- Around line 3-11: The MonoImporter block in SendOptions.cs.meta is not allowed
by project convention; open SendOptions.cs.meta, remove the entire MonoImporter:
... block (the keys externalObjects, serializedVersion, defaultReferences,
executionOrder, icon, userData, assetBundleName, assetBundleVariant) so the file
contains only the required fileFormatVersion and guid entries, then save the
cleaned .meta file (ensuring guid is preserved).

In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 456-459: The AuthorizationFailed fast-path in
SignAndSendTransactionsInternal and ReconnectInternal currently sets _authToken
= null (and clears cache in one place) but omits the same logout/disconnect
steps done in DeauthorizeInternal/DisconnectInternal; update the
AuthorizationFailed case in both SignAndSendTransactionsInternal and
ReconnectInternal to, after clearing cache and setting _authToken = null, call
LogoutSuppressed() and invoke OnWalletDisconnected?() so WalletBase.Account and
PublicKey are cleared and listeners notified, mirroring
DeauthorizeInternal/DisconnectInternal behavior.
- Around line 723-729: The SIWS fallback extraction does not validate
signedBytes length and can throw when signedBytes.Length < 64; in the code
handling siwsFallbackSig and SignedPayloadsBytes (use symbols siwsFallbackSig,
SignedPayloadsBytes, signedBytes, sigBytes, msgBytes) add a defensive check that
signedBytes != null and signedBytes.Length >= 64 before allocating
msgBytes/sigBytes and copying; if the check fails, log/return a clear error (or
skip this payload) instead of proceeding to create negative-length arrays so the
method fails gracefully.
- Around line 431-435: In the lambdas inside SignAndSendTransactionsInternal,
LoginWithSignInInternal (fallback), and CloneAuthorizationInternal you are
calling AuthorizeAsync but discarding its return value, which loses rotated auth
tokens; change each lambda to capture the returned authorization (e.g., var
authorization = await client.AuthorizeAsync(...)), update _authToken =
authorization.AuthToken ?? _authToken, and then pass the updated token
explicitly into subsequent calls (e.g.,
SignMessagesAsync/CloneAuthorizationAsync) instead of relying on mutated
closures — follow the pattern used in _SignAllTransactions where authorization
is awaited and _authToken is refreshed.
- Around line 381-385: The code in SignAndSendTransaction is returning
success.Signatures[0] as base64 which is wrong for Solana RPCs; replace
Convert.ToBase64String(success.Signatures[0]) with a base58 encoding of the raw
64-byte signature (e.g., use a Base58 encoder such as Org.BouncyCastle's Base58
utility), add the corresponding using (e.g., using
Org.BouncyCastle.Utilities.Encoders; or the appropriate Base58 class), and
return that base58 string so callers can pass the result directly to RPC methods
like ConfirmTransaction/GetSignatureStatuses.

In `@Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs.meta`:
- Around line 3-11: Remove the extraneous MonoImporter block from the
Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs.meta file so it only
contains the minimal .cs.meta fields used by this repo (fileFormatVersion and
guid); locate the "MonoImporter:" section in the shown diff and delete that
entire block (including its nested keys externalObjects, serializedVersion,
defaultReferences, executionOrder, icon, userData, assetBundleName,
assetBundleVariant) leaving only the minimal metadata to prevent metadata drift.

In `@Tests/EditMode/JsonRpc/ResponseModelTests.cs.meta`:
- Around line 3-11: The .meta file contains an extra MonoImporter block that
violates the project's minimal .cs.meta convention; remove the entire
MonoImporter section (the keys starting with "MonoImporter:" and its nested
fields like externalObjects, serializedVersion, defaultReferences,
executionOrder, icon, userData, assetBundleName, assetBundleVariant) from
ResponseModelTests.cs.meta and ensure the file contains only the canonical keys
fileFormatVersion and guid with their values to match other C# Unity .cs.meta
files in the repo.

In `@Tests/EditMode/Mocks/MockMessageSender.cs.meta`:
- Around line 3-11: The .meta file contains an unnecessary MonoImporter block;
remove the entire MonoImporter: { ... } section from
Tests/EditMode/Mocks/MockMessageSender.cs.meta and reduce the file to the
repository convention: only include fileFormatVersion and guid entries,
preserving the existing guid value if present and removing defaultReferences,
executionOrder, icon, userData, assetBundleName, assetBundleVariant and any
other MonoImporter keys so the file matches the minimal .cs.meta format used
across the repo.

In `@Tests/EditMode/MwaAdapter/ConcurrencyTests.cs`:
- Around line 26-31: The test uses FormatterServices.GetUninitializedObject
which bypasses field initializers so the inline _gate initializer never runs;
update the test (which creates a SolanaMobileWalletAdapter via
GetUninitializedObject) to explicitly initialize the _gate before asserting by
using the same pattern as ReconnectTests/DisconnectTests: obtain the gate field
(field/gateField) by reflection and call gateField.SetValue(adapter, new
System.Threading.SemaphoreSlim(1,1)) (or construct via a real constructor path)
and then assert that the gate is non-null and CurrentCount == 1.

In `@Tests/EditMode/MwaAdapter/ConcurrencyTests.cs.meta`:
- Around line 3-11: Remove the entire MonoImporter block (including keys like
externalObjects, serializedVersion, defaultReferences, executionOrder, icon,
userData, assetBundleName, assetBundleVariant) from the .meta file and leave
only the minimal Unity metadata fields fileFormatVersion and guid so the file
matches the project convention; ensure the resulting ConcurrencyTests.cs.meta
contains only fileFormatVersion and guid entries and nothing else.

In `@Tests/EditMode/MwaAdapter/DeauthorizeTests.cs.meta`:
- Around line 3-11: The .meta contains an unnecessary MonoImporter block; update
DeauthorizeTests.cs.meta to follow project convention by removing the entire
MonoImporter section and leaving only the minimal required keys:
fileFormatVersion and guid (ensure those two remain and no other entries like
defaultReferences, executionOrder, icon, userData, assetBundleName, or
assetBundleVariant are present).

In `@Tests/EditMode/MwaAdapter/DisconnectTests.cs`:
- Around line 27-31: The Guard() method currently asserts CacheField and
DisconnectMethod but misses asserting AuthTokenField; add an assertion for
AuthTokenField (similar to the existing ones) inside Guard() so that
AuthTokenField is verified not-null with a clear message (e.g., "AuthTokenField
not found") to fail fast if the reflected field is renamed or missing; locate
the Guard() method and add the assert alongside CacheField and DisconnectMethod
checks referencing AuthTokenField.

In `@Tests/EditMode/MwaAdapter/DisconnectTests.cs.meta`:
- Around line 3-11: Remove the entire MonoImporter block from the .cs.meta
content so the file only contains the minimal metadata (keep only
fileFormatVersion and guid entries); locate the MonoImporter mapping in the
DisconnectTests.cs.meta content and delete that mapping (the keys starting with
"MonoImporter:" and its nested fields) to match the repository convention of
minimal C# .meta files.

In `@Tests/EditMode/MwaAdapter/ReconnectTests.cs`:
- Around line 24-36: The Guard() setup currently asserts CacheField and
ReconnectMethod but misses verifying the private _gate field used in
CreateAdapter(); add an assertion in Guard() that the reflection lookup for
_gate (the same code using typeof(SolanaMobileWalletAdapter).GetField("_gate",
BindingFlags.Instance | BindingFlags.NonPublic)) returns a non-null FieldInfo
with a clear message like "_gate not found" so reflection drift fails fast; keep
CreateAdapter() unchanged but reference the same field lookup (gateField) used
to set the SemaphoreSlim to ensure consistency.

In `@Tests/EditMode/MwaCache/CustomCacheInjectionTest.cs`:
- Around line 1-2: Replace calls to the obsolete
FormatterServices.GetUninitializedObject(Type) with
RuntimeHelpers.GetUninitializedObject(Type) throughout the test (e.g., in
CustomCacheInjectionTest where FormatterServices.GetUninitializedObject is
used); add the using directive for System.Runtime.CompilerServices if missing
and update all occurrences (including the ones noted at lines 31-32) to call
RuntimeHelpers.GetUninitializedObject(Type) instead of
FormatterServices.GetUninitializedObject(Type).

In `@Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs`:
- Around line 21-23: The test attempts to reset the private field
_warnedThisSession on PlayerPrefsAuthorizationCache using GetField with
BindingFlags.Static which returns null because the field is an instance field;
update the reflection call to use BindingFlags.Instance | BindingFlags.NonPublic
and call SetValue with the specific PlayerPrefsAuthorizationCache instance used
in the test (or, if the implementation makes _warnedThisSession static, switch
to BindingFlags.Static and keep null as the instance argument) so the field is
actually reset before assertions.

In `@Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs.meta`:
- Around line 3-11: The .meta file contains an unwanted MonoImporter block;
remove the entire MonoImporter section so the file only includes the required
minimal keys (fileFormatVersion and guid) following the project's convention;
ensure no other keys like defaultReferences, executionOrder, icon, userData,
assetBundleName, or assetBundleVariant remain and that the metadata for
PlayerPrefsAuthorizationCacheTests.cs only has fileFormatVersion and guid.

In `@Tests/EditMode/MwaClient/DeauthorizeRequestWireTest.cs`:
- Around line 29-30: Replace the hard-coded method name in the assertion with
the constant to keep tests in sync: update the assertion that currently checks
for "\"method\":\"deauthorize\"" to instead use RpcMethodNames.Deauthorize
(e.g., build the expected substring dynamically from RpcMethodNames.Deauthorize)
so the test references the same symbol used to construct the request
(RpcMethodNames.Deauthorize) while leaving the auth_token assertion as-is.

In `@Tests/EditMode/MwaParser/ParserVerbosityTests.cs.meta`:
- Around line 3-11: Remove the MonoImporter block from the
Tests/EditMode/MwaParser/ParserVerbosityTests.cs.meta file so the .cs.meta
adheres to project convention; keep only the minimal metadata fields
(fileFormatVersion and guid) in the file, deleting the entire MonoImporter: {
... } section (the long block containing externalObjects, serializedVersion,
defaultReferences, executionOrder, icon, userData, assetBundleName,
assetBundleVariant) so the metadata is minimal and consistent with other C#
.meta files.
🪄 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: b27193e2-874e-4bf7-bd4c-e50e003289e7

📥 Commits

Reviewing files that changed from the base of the PR and between 74a2179 and c92dac5.

📒 Files selected for processing (120)
  • .gitignore
  • Packages/BouncyCastle.Cryptography.dll.meta
  • Runtime/codebase/SolanaMobileStack/AccountInfo.cs
  • Runtime/codebase/SolanaMobileStack/AccountInfo.cs.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationHelpers.cs
  • Runtime/codebase/SolanaMobileStack/AuthorizationHelpers.cs.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationRecord.cs
  • Runtime/codebase/SolanaMobileStack/AuthorizationRecord.cs.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs
  • Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs.meta
  • Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs
  • Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions/InvalidAuthorizationException.cs
  • Runtime/codebase/SolanaMobileStack/Exceptions/InvalidAuthorizationException.cs.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions/JsonRpcException.cs
  • Runtime/codebase/SolanaMobileStack/Exceptions/JsonRpcException.cs.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions/OperationInFlightException.cs
  • Runtime/codebase/SolanaMobileStack/Exceptions/OperationInFlightException.cs.meta
  • Runtime/codebase/SolanaMobileStack/IAuthorizationCache.cs
  • Runtime/codebase/SolanaMobileStack/IAuthorizationCache.cs.meta
  • Runtime/codebase/SolanaMobileStack/ITransactionPayload.cs
  • Runtime/codebase/SolanaMobileStack/ITransactionPayload.cs.meta
  • Runtime/codebase/SolanaMobileStack/Interfaces/IAdapterOperations.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/AuthorizationResult.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/CapabilitiesResult.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/CapabilitiesResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs.meta
  • Runtime/codebase/SolanaMobileStack/LegacyTransactionPayload.cs
  • Runtime/codebase/SolanaMobileStack/LegacyTransactionPayload.cs.meta
  • Runtime/codebase/SolanaMobileStack/LogVerbosity.cs
  • Runtime/codebase/SolanaMobileStack/LogVerbosity.cs.meta
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterSession.cs
  • Runtime/codebase/SolanaMobileStack/PlayerPrefsAuthorizationCache.cs
  • Runtime/codebase/SolanaMobileStack/PlayerPrefsAuthorizationCache.cs.meta
  • Runtime/codebase/SolanaMobileStack/ReconnectResult.cs
  • Runtime/codebase/SolanaMobileStack/ReconnectResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/RpcMethodNames.cs
  • Runtime/codebase/SolanaMobileStack/RpcMethodNames.cs.meta
  • Runtime/codebase/SolanaMobileStack/SendOptions.cs
  • Runtime/codebase/SolanaMobileStack/SendOptions.cs.meta
  • Runtime/codebase/SolanaMobileStack/SignAndSendTxResult.cs
  • Runtime/codebase/SolanaMobileStack/SignAndSendTxResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/SignInPayload.cs
  • Runtime/codebase/SolanaMobileStack/SignInPayload.cs.meta
  • Runtime/codebase/SolanaMobileStack/SignInResult.cs
  • Runtime/codebase/SolanaMobileStack/SignInResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
  • Runtime/codebase/SolanaWalletAdapter.cs
  • Tests/EditMode/Crypto/EcdsaSignaturesTests.cs
  • Tests/EditMode/Crypto/EcdsaSignaturesTests.cs.meta
  • Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs
  • Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs.meta
  • Tests/EditMode/EditMode.asmdef
  • Tests/EditMode/JsonRpc/ResponseModelTests.cs
  • Tests/EditMode/JsonRpc/ResponseModelTests.cs.meta
  • Tests/EditMode/Mocks/MockMessageSender.cs
  • Tests/EditMode/Mocks/MockMessageSender.cs.meta
  • Tests/EditMode/MwaAdapter.meta
  • Tests/EditMode/MwaAdapter/ConcurrencyTests.cs
  • Tests/EditMode/MwaAdapter/ConcurrencyTests.cs.meta
  • Tests/EditMode/MwaAdapter/DeauthorizeTests.cs
  • Tests/EditMode/MwaAdapter/DeauthorizeTests.cs.meta
  • Tests/EditMode/MwaAdapter/DisconnectTests.cs
  • Tests/EditMode/MwaAdapter/DisconnectTests.cs.meta
  • Tests/EditMode/MwaAdapter/ReconnectTests.cs
  • Tests/EditMode/MwaAdapter/ReconnectTests.cs.meta
  • Tests/EditMode/MwaAdapter/SignAndSendTests.cs
  • Tests/EditMode/MwaAdapter/SignAndSendTests.cs.meta
  • Tests/EditMode/MwaCache.meta
  • Tests/EditMode/MwaCache/CustomCacheInjectionTest.cs
  • Tests/EditMode/MwaCache/CustomCacheInjectionTest.cs.meta
  • Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs
  • Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs.meta
  • Tests/EditMode/MwaCache/SchemaVersionValidationTests.cs
  • Tests/EditMode/MwaCache/SchemaVersionValidationTests.cs.meta
  • Tests/EditMode/MwaClient/AuthorizeRequestWireTests.cs
  • Tests/EditMode/MwaClient/AuthorizeRequestWireTests.cs.meta
  • Tests/EditMode/MwaClient/DeauthorizeRequestWireTest.cs
  • Tests/EditMode/MwaClient/DeauthorizeRequestWireTest.cs.meta
  • Tests/EditMode/MwaClient/LoginShapeCompatibilityTest.cs
  • Tests/EditMode/MwaClient/LoginShapeCompatibilityTest.cs.meta
  • Tests/EditMode/MwaClient/MobileWalletAdapterClientTests.cs
  • Tests/EditMode/MwaClient/MobileWalletAdapterClientTests.cs.meta
  • Tests/EditMode/MwaParser.meta
  • Tests/EditMode/MwaParser/AuthorizationResponseParserTests.cs
  • Tests/EditMode/MwaParser/AuthorizationResponseParserTests.cs.meta
  • Tests/EditMode/MwaParser/Fixtures.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-bad-base64.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-bad-base64.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-accounts.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-accounts.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-authtoken.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-authtoken.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-full.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-full.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-minimal.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-minimal.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-accounts.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-accounts.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-chains.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-chains.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-display.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-display.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-icon.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-icon.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-wrong-length.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-wrong-length.json.meta
  • Tests/EditMode/MwaParser/ParserVerbosityTests.cs
  • Tests/EditMode/MwaParser/ParserVerbosityTests.cs.meta
  • docs.meta
  • docs/mwa-cache-guide.md
  • docs/mwa-method-reference.md
  • docs/mwa-migration-v1-to-v2.md
  • docs/mwa-quick-start.md
  • docs/sign-and-send-investigation.md
💤 Files with no reviewable changes (4)
  • Tests/EditMode/Crypto/EcdsaSignaturesTests.cs
  • Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs
  • Tests/EditMode/JsonRpc/ResponseModelTests.cs
  • Tests/EditMode/Mocks/MockMessageSender.cs

Comment thread docs/mwa-cache-guide.md Outdated
Comment thread docs/mwa-method-reference.md Outdated
Comment thread docs/mwa-quick-start.md
Comment thread docs/sign-and-send-investigation.md
Comment thread Packages/BouncyCastle.Cryptography.dll.meta
Comment thread Tests/EditMode/MwaCache/CustomCacheInjectionTest.cs
Comment thread Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs
Comment thread Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs.meta
Comment thread Tests/EditMode/MwaClient/DeauthorizeRequestWireTest.cs Outdated
Comment thread Tests/EditMode/MwaParser/ParserVerbosityTests.cs.meta
@Zurcusa
Copy link
Copy Markdown
Author

Zurcusa commented May 11, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 7

♻️ Duplicate comments (2)
Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs (2)

457-460: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear wallet state on auth revocation, not just the token.

These revocation paths clear _authToken and cache, but they skip LogoutSuppressed() and OnWalletDisconnected. WalletBase.Account / PublicKey therefore stays populated after the session has already been revoked.

🐛 Proposed fix
                         try { await _cache.ClearAsync(); } catch (Exception ex) { Debug.LogWarning($"[MWA] Cache clear failed after auth revocation: {ex.Message}"); }
                         _authToken = null;
+                        LogoutSuppressed();
+                        OnWalletDisconnected?.Invoke();
                         return new SignAndSendTxResult.AuthRevoked();
                 try { await _cache.ClearAsync(); } catch (Exception ex) { Debug.LogWarning($"[MWA] Cache clear failed after auth revocation: {ex.Message}"); }
                 _authToken = null;
+                LogoutSuppressed();
+                OnWalletDisconnected?.Invoke();
                 return new ReconnectResult.NoCachedSession();

Also applies to: 571-575

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs` around lines
457 - 460, The AuthorizationFailed handling only clears _authToken and cache but
leaves wallet state populated; update the JsonRpcErrorCodes.AuthorizationFailed
case in the SignAndSendTx flow (and the similar block around the other
occurrence) to call LogoutSuppressed() and OnWalletDisconnected() after clearing
cache and clearing _authToken so WalletBase.Account/PublicKey are reset; ensure
you maintain the existing try/catch around _cache.ClearAsync() and keep
returning new SignAndSendTxResult.AuthRevoked() after invoking those two
methods.

432-437: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Persist rotated auth tokens from these reauthorization calls.

These lambdas await AuthorizeAsync and discard the returned AuthorizationResult. If the wallet rotates the auth token during reauthorization, _authToken stays stale; CloneAuthorizationInternal then immediately uses that stale token, and the sign-and-send / SIWS fallback paths never persist the refreshed session.

🐛 Proposed fix pattern
-                    async client =>
-                    {
-                        await client.AuthorizeAsync(
-                            _identityUri, _iconRelativeUri,
-                            _walletOptions.name, chain, _authToken, CancellationToken.None);
-                    },
+                    async client =>
+                    {
+                        var reauth = await client.AuthorizeAsync(
+                            _identityUri, _iconRelativeUri,
+                            _walletOptions.name, chain, _authToken, CancellationToken.None);
+                        _authToken = reauth?.AuthToken ?? _authToken;
+                    },

Based on learnings: cross-lambda variable sharing via mutated closures is unsafe in the current async void Action<IAdapterOperations> pattern; pass the refreshed token explicitly.

Also applies to: 699-703, 768-777

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs` around lines
432 - 437, The lambda passed to the Action<IAdapterOperations> invokes
AuthorizeAsync but discards the returned AuthorizationResult so rotated tokens
aren’t persisted; update the call sites (the async lambda wrapping
AuthorizeAsync at the shown block and the similar blocks around
CloneAuthorizationInternal usage) to capture the AuthorizationResult, extract
the refreshed token from it, and pass that token explicitly into
CloneAuthorizationInternal (or into subsequent sign/send flows) instead of
relying on the mutated closure _authToken; refactor the async void
Action<IAdapterOperations> usage so the refreshed token is threaded out of the
lambda (e.g., return or callback the AuthorizationResult) and persist the new
token back to the field where appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs`:
- Around line 47-50: The raw response listeners (RegisterRawListener /
ReceiverRaw handling TaskCompletionSource<JToken>) currently complete any
pending task on the next envelope without checking envelope["id"], causing
overlapping requests to complete the wrong TaskCompletionSource and possible
exceptions when a late message hits an already-completed task; update the
listener logic so it reads the JSON-RPC "id" from the incoming envelope and only
Call SetResult/SetException on the TaskCompletionSource whose id matches the
request's id, and immediately unregister the listener as soon as a matching
envelope is processed (do not rely on a delayed ContinueWith removal); apply the
same id-check/unregister pattern to the other raw-listener sites identified (the
blocks around lines 67–71 and 98–114) to ensure each TaskCompletionSource is
correlated to its JSON-RPC id before completing.

In `@Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs`:
- Around line 127-129: The current guard only checks the array but allows null
or empty entries; in MobileWalletAdapterClient (where base64Payloads is used)
validate each entry in base64Payloads and throw an ArgumentException (or
ArgumentNullException for null elements) referencing nameof(base64Payloads) if
any element is null or string.IsNullOrWhiteSpace, before
ct.ThrowIfCancellationRequested(); iterate the array, check each element and
include the index in the exception message to aid debugging so no null/empty
payload is serialized and sent.
- Around line 227-228: The code currently returns null when the JSON response
from SendRequestRaw lacks "auth_token", which can silently treat a malformed
clone_authorization response as success; update the method that calls
SendRequestRaw so it checks raw?["auth_token"] and, if missing or null, throws a
protocol-level exception (e.g., InvalidOperationException or a custom
ProtocolException) with a clear message like "Malformed clone_authorization
response: missing auth_token" instead of returning null, ensuring callers cannot
overwrite valid cached auth state with a null token.

In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 184-190: ReloadAuthTokenFromCacheIfNeeded and the SignMessage
public-key fallback are reading raw cache entries and may reuse
schema-mismatched or wrong-chain records; instead, call and use
LoadValidCachedRecordAsync to validate cache records before assigning _authToken
or using cached data. Update ReloadAuthTokenFromCacheIfNeeded to await
LoadValidCachedRecordAsync() and set _authToken only from its validated result,
and change the SignMessage fallback path to retrieve the validated record via
LoadValidCachedRecordAsync (or factor out the validation step) rather than
calling _cache.GetAsync() directly.
- Around line 581-590: ReconnectInternal currently assumes authorization is
non-null after a successful result; add a null check for the authorization
variable before dereferencing it (before using authorization.AuthToken,
CacheAuthorizationAsync(authorization), and new
PublicKey(authorization.PrimaryAccountPublicKeyBytes())). If authorization is
null, return a ReconnectResult.Failed with an Error describing the missing
payload (e.g., "Authorization payload missing") so the method returns a
structured failure instead of throwing NullReferenceException.
- Around line 88-92: The constructor currently dereferences solanaWalletOptions
directly for identity/icon URIs causing a null crash; normalize the options
first by assigning _walletOptions = solanaWalletOptions ?? new
SolanaWalletOptions() (or another sensible default) and then use
_walletOptions.identityUri and _walletOptions.iconUri when creating _identityUri
and _iconRelativeUri, ensuring you also handle null/empty URI strings before new
Uri(...) so defaults are used consistently for _cache, _verbosity, and later
uses of _walletOptions.name; update the code that sets _cache, _verbosity,
_identityUri, and _iconRelativeUri to reference the normalized _walletOptions
and guard Uri construction.

In `@Tests/EditMode/MwaAdapter/ReconnectTests.cs`:
- Around line 31-37: CreateAdapter uses FormatterServices.GetUninitializedObject
which leaves important fields null so Reconnect() runs on a partially
initialized SolanaMobileWalletAdapter; instead either instantiate the adapter
via its real constructor or explicitly set all fields Reconnect reads (set
_walletOptions with a non-null name, _identityUri, _iconRelativeUri, and the
base RpcCluster property) after using GetUninitializedObject, and ensure any
background gating/state the constructor would set is replicated (you already set
_gate and CacheField but must also populate _walletOptions, _identityUri,
_iconRelativeUri and base RpcCluster) so Reconnect() won't hit
NullReferenceExceptions and tests validate reconnect semantics rather than
masking failures.

---

Duplicate comments:
In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 457-460: The AuthorizationFailed handling only clears _authToken
and cache but leaves wallet state populated; update the
JsonRpcErrorCodes.AuthorizationFailed case in the SignAndSendTx flow (and the
similar block around the other occurrence) to call LogoutSuppressed() and
OnWalletDisconnected() after clearing cache and clearing _authToken so
WalletBase.Account/PublicKey are reset; ensure you maintain the existing
try/catch around _cache.ClearAsync() and keep returning new
SignAndSendTxResult.AuthRevoked() after invoking those two methods.
- Around line 432-437: The lambda passed to the Action<IAdapterOperations>
invokes AuthorizeAsync but discards the returned AuthorizationResult so rotated
tokens aren’t persisted; update the call sites (the async lambda wrapping
AuthorizeAsync at the shown block and the similar blocks around
CloneAuthorizationInternal usage) to capture the AuthorizationResult, extract
the refreshed token from it, and pass that token explicitly into
CloneAuthorizationInternal (or into subsequent sign/send flows) instead of
relying on the mutated closure _authToken; refactor the async void
Action<IAdapterOperations> usage so the refreshed token is threaded out of the
lambda (e.g., return or callback the AuthorizationResult) and persist the new
token back to the field where appropriate.
🪄 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: 76081900-05d6-47a9-ae4e-26fcb7e7c49b

📥 Commits

Reviewing files that changed from the base of the PR and between c92dac5 and a5c190b.

📒 Files selected for processing (14)
  • Packages/BouncyCastle.Cryptography.dll.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationRecord.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs.meta
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
  • Runtime/codebase/SolanaMobileStack/PlayerPrefsAuthorizationCache.cs
  • Runtime/codebase/SolanaMobileStack/RpcMethodNames.cs.meta
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
  • Tests/EditMode/MwaAdapter/ConcurrencyTests.cs
  • Tests/EditMode/MwaAdapter/DeauthorizeTests.cs
  • Tests/EditMode/MwaAdapter/ReconnectTests.cs
  • Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs

Comment thread Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs
Comment thread Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
Comment thread Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs Outdated
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs Outdated
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Comment thread Tests/EditMode/MwaAdapter/ReconnectTests.cs
@Zurcusa Zurcusa force-pushed the pr/6-mwa-docs branch 3 times, most recently from 67e50a7 to 8863367 Compare May 11, 2026 12:54
Zurcusa added 19 commits May 11, 2026 16:44
- Guard null/empty address in PrimaryAccountPublicKeyBytes
- Private constructors on DeauthorizeResult and ReconnectResult
- Null-result check in JsonRpc20Client.ReceiverRaw
- Cache auth token refresh in sign_and_send authorize action
- LogoutSuppressed + OnWalletDisconnected on auth revocation paths
- Fix base64 → base58 typo in method reference
- Guard null/empty address in PrimaryAccountPublicKeyBytes
- Private constructors on DeauthorizeResult and ReconnectResult
- Null-result check in JsonRpc20Client.ReceiverRaw
- Cache auth token refresh in sign_and_send authorize action
- LogoutSuppressed + OnWalletDisconnected on auth revocation paths
- Fix base64 → base58 typo in method reference
- Return base58-encoded signatures instead of base64 (Solana RPC compat)
- Remove duplicate sendOptions wire field (MWA v2 spec uses "options" only)
- Gate Debug.Log wire messages behind UNITY_EDITOR || MWA_VERBOSE_WIRE
- Remove unused LegacyPkKey constant from PlayerPrefsAuthorizationCache
- Make _warnedThisSession static for once-per-session semantics
- Add SIWS fallback length guard for signed payloads < 64 bytes
- Enable BouncyCastle for Editor platform (fixes EditMode test loading)
- Add namespace to JsonRpcErrorCodes
- Fix SchemaVersion default to match ExpectedSchemaVersion (2)
- Fix DeauthorizeTests missing _gate reflection init (4 test failures)
- Fix ConcurrencyTests assertion for GetUninitializedObject compatibility
- Fix ReconnectTests platform guard for non-Android EditMode runs
- Remove invalid LegacyPk_DeletedOnConstruction test
Zurcusa added 4 commits May 11, 2026 16:44
- DeauthorizeRequestWireTest: use RpcMethodNames.Deauthorize in assertion
  instead of hardcoded string
- ReconnectTests: promote _gate lookup to static field with OneTimeSetUp
  guard so reflection drift fails fast with a clear message
LoginWithSignInInternal SIWS fallback was the last AuthorizeAsync call
site that discarded the return value, losing token rotations.
- Null-safe constructor: default solanaWalletOptions to prevent NPE
- Validate payload elements are non-null before sending to wallet
- Treat missing auth_token in clone_authorization as protocol error
- Use LoadValidCachedRecordAsync in ReloadAuthTokenFromCacheIfNeeded
  to enforce schema/chain validation on cached records
- Guard null authorization in ReconnectInternal success path
- Remove unused using in ConcurrencyTests
@Zurcusa
Copy link
Copy Markdown
Author

Zurcusa commented May 11, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Runtime/codebase/SolanaMobileStack/MobileWalletAdapterSession.cs (1)

162-183: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate frame length first and only commit Rx after auth succeeds.

A short payload now fails via raw indexing/ArraySegment before this method can reject it cleanly, and _mSeqNumberRx is advanced before DoFinal() verifies the GCM tag. One tampered frame with the expected next counter can therefore permanently desync the session and cause the real next message to be rejected.

💡 Suggested fix
 public byte[] DecryptSessionPayload(byte[] payload)
 {
     if (_encryptionKey == null)
     {
         const string e = "Cannot decrypt, no session key has been established";
         Debug.LogError(e);
         throw new InvalidOperationException(e);
     }
+
+    if (payload == null || payload.Length < SeqNumLengthBytes + AesIvLengthBytes + AesTagLengthBytes)
+    {
+        const string e = "Encrypted payload is too short";
+        Debug.LogError(e);
+        throw new InvalidOperationException(e);
+    }
 
     var seqNumRaw = new ArraySegment<byte>(payload, 0, SeqNumLengthBytes).ToArray();
     uint seqNum = (uint)(payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]);
 
     if (seqNum != _mSeqNumberRx + 1)
     {
         const string e = "Encrypted messages has invalid sequence number";
         Debug.LogError(e);
         throw new InvalidOperationException(e);
     }
-    _mSeqNumberRx = (int)seqNum;
 
     try
     {
         var keyParam = new KeyParameter(_encryptionKey);
         var iv = new ArraySegment<byte>(payload, SeqNumLengthBytes, AesIvLengthBytes).ToArray();
@@
         var toDecipher = new ArraySegment<byte>( payload, SeqNumLengthBytes + AesIvLengthBytes, payload.Length - SeqNumLengthBytes - AesIvLengthBytes).ToArray();
         var decipherText = new byte[cipher.GetOutputSize(toDecipher.Length)];
         int len = cipher.ProcessBytes(payload, SeqNumLengthBytes + AesIvLengthBytes, toDecipher.Length, decipherText, 0);
         cipher.DoFinal(decipherText, len);
+        _mSeqNumberRx = (int)seqNum;
         return decipherText;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Runtime/codebase/SolanaMobileStack/MobileWalletAdapterSession.cs` around
lines 162 - 183, Validate the incoming payload length before any indexing
(ensure payload.Length >= SeqNumLengthBytes + AesIvLengthBytes +
AesTagLengthBytes and enough ciphertext) in MobileWalletAdapterSession so
ArraySegment/byte indexing cannot throw; compute seqNum using the safe seqNumRaw
span rather than raw indexing, but do NOT assign to _mSeqNumberRx until after
GCM authentication succeeds (after cipher.DoFinal completes without exception).
Also fix the cipher.ProcessBytes call to pass the ciphertext buffer (toDecipher)
and correct lengths, and only update _mSeqNumberRx once ProcessBytes/DoFinal
have completed successfully.
♻️ Duplicate comments (1)
Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs (1)

817-822: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the validated cache path for the SignMessage public-key fallback.

This reintroduces the earlier cache-validation problem: a schema-mismatched or wrong-chain record can still supply cachedPk here even though ReloadAuthTokenFromCacheIfNeeded() now filters those records out. Please go through LoadValidCachedRecordAsync() instead of _cache.GetAsync().

💡 Minimal fix
             string cachedPk = Account?.PublicKey?.ToString();
             if (string.IsNullOrEmpty(cachedPk))
             {
-                var record = await _cache.GetAsync();
+                var record = await LoadValidCachedRecordAsync();
                 cachedPk = record?.AccountAddress;
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs` around lines
817 - 822, In SignMessage, the fallback path obtains cachedPk from
_cache.GetAsync(), which can return schema/chain-mismatched records; instead
call LoadValidCachedRecordAsync() (the same validated loader used by
ReloadAuthTokenFromCacheIfNeeded()) and use its returned record?.AccountAddress
for cachedPk so only validated cache entries are used for the public-key
fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/mwa-method-reference.md`:
- Line 3: The doc states "Methods marked with a lock require exclusive access"
but no methods show a lock; update the `SolanaMobileWalletAdapter` method list
to either remove the "marked with a lock" phrase and state that all auth/signing
methods are gated by the `SemaphoreSlim` concurrency gate, or add a consistent
lock indicator (e.g., "🔒") next to each gated method heading (at minimum:
Login, LoginWithSignIn, Reconnect, Deauthorize, CloneAuthorization and all
signing methods) so readers can immediately see which methods throw
OperationInFlightException when called concurrently.

In `@Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs`:
- Around line 60-129: The loop currently only validates the primary address but
leaves non-primary AccountInfo.Address values unchecked; update the address
handling in the accounts construction loop (where AccountInfo is created) to run
the same base64 extraction and 32-byte length check used for PrimaryAccount() —
i.e., call ExtractBase64Payload and EstimateBase64DecodedLength for each
entry["address"], log and add to droppedFields when the decoded length exceeds
the expected byte size, and either set Address to null/empty or skip adding that
AccountInfo (consistent with existing drop behavior) so downstream code never
receives unvalidated raw address strings.

In `@Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs`:
- Around line 11-19: The LocalOnly and Failed result variants expose mutable
properties; change WalletPackage on LocalOnly and Error on Failed to be
immutable by adding constructors that accept the respective values and making
the properties get-only (constructor-initialized) instead of having public
setters; update any instantiation sites to pass the values via the new
constructors and remove code that mutates these properties after creation.

In `@Runtime/codebase/SolanaMobileStack/ReconnectResult.cs`:
- Around line 10-20: SilentSuccess and Failed currently allow creation without
required payloads (Account and Error respectively); change both classes to
require non-null constructor parameters (e.g., SilentSuccess(Account account)
and Failed(Exception error)), make the properties get-only (public Account
Account { get; } and public Exception Error { get; }) and validate against null
(throw ArgumentNullException) to enforce the non-null invariant at construction
time.

In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 336-348: The semaphore only protects parts of the state; update
the methods _Login, _SignAllTransactions, SignMessage(byte[]), and
GetCapabilities() to acquire the same gate via TryAcquireGate before touching
shared state (_authToken, Account, the auth cache) and always ReleaseGate in a
finally block so Disconnect()/Deauthorize() cannot concurrently clear/recreate
state during those operations; ensure you set _currentOperation appropriately
(use the existing MwaOperation enum/values) and handle the case where
TryAcquireGate returns false by failing fast or returning an appropriate error
result.
- Around line 700-706: The code caches the auth session and returns a new
Account tuple on successful LoginWithSignIn but never sets the adapter's own
Account (and thus PublicKey) property; after creating the new Account (via new
PublicKey(authorization.PrimaryAccountPublicKeyBytes()) and new Account(...))
assign it to the adapter's Account property (and update any related PublicKey
field if present) before returning, ensuring the adapter's state reflects the
successful sign-in (update symbols: _authToken, CacheAuthorizationAsync,
PublicKey(...), Account(...), and the adapter's Account/PublicKey properties).
- Around line 403-416: The code recreates a SendOptions instance when populating
MinContextSlot and accidentally omits the WaitForCommitmentToSendNextTransaction
flag; update the branch that builds the new SendOptions (inside the
GetLatestBlockHashAsync result handling) to copy
WaitForCommitmentToSendNextTransaction from the original options into the new
SendOptions so callers that set WaitForCommitmentToSendNextTransaction are not
changed when MinContextSlot is backfilled.

In `@Runtime/codebase/SolanaWalletAdapter.cs`:
- Around line 31-34: The code currently ignores authCache when
options.solanaMobileWalletAdapterOptions is null, causing
SolanaMobileWalletAdapter to default to PlayerPrefsAuthorizationCache; to fix,
ensure you create a default SolanaMobileWalletAdapterOptions instance when
options.solanaMobileWalletAdapterOptions is null before assigning Cache: replace
or precede the use of options.solanaMobileWalletAdapterOptions with a non-null
mwaOptions (e.g., instantiate new SolanaMobileWalletAdapterOptions if null),
then apply mwaOptions.Cache ??= authCache, and finally pass that mwaOptions into
the SolanaMobileWalletAdapter constructor used to set _internalWallet.

In `@Tests/EditMode/MwaParser/AuthorizationResponseParserTests.cs`:
- Around line 99-129: Add unit tests that cover the untested branches in
AuthorizationResponseParser.Parse: create three new tests mirroring the existing
patterns using fixtures authorize-v2-oversize-features.json,
authorize-v2-oversize-wallet-icon.json, and authorize-v2-with-signin.json; for
features assert result.Accounts[0].Features.Length == 32 (features[] > 32 must
be truncated), for wallet_icon assert result.Accounts[0].WalletIcon is null
(wallet_icon > 64 KiB post-decode must be dropped), and for sign-in assert that
the parsed SignInResult object (result.SignInResult or
result.Accounts[0].SignInResult depending on model) contains the expected mapped
fields to validate the sign_in_result mapping into SignInResult. Ensure test
names follow the existing naming convention (e.g., V2OversizeFeatures_Truncates,
V2OversizeWalletIcon_DropsField, V2WithSignIn_ParsesSignInResult) and use
AuthorizationResponseParser.Parse to load the fixture.

---

Outside diff comments:
In `@Runtime/codebase/SolanaMobileStack/MobileWalletAdapterSession.cs`:
- Around line 162-183: Validate the incoming payload length before any indexing
(ensure payload.Length >= SeqNumLengthBytes + AesIvLengthBytes +
AesTagLengthBytes and enough ciphertext) in MobileWalletAdapterSession so
ArraySegment/byte indexing cannot throw; compute seqNum using the safe seqNumRaw
span rather than raw indexing, but do NOT assign to _mSeqNumberRx until after
GCM authentication succeeds (after cipher.DoFinal completes without exception).
Also fix the cipher.ProcessBytes call to pass the ciphertext buffer (toDecipher)
and correct lengths, and only update _mSeqNumberRx once ProcessBytes/DoFinal
have completed successfully.

---

Duplicate comments:
In `@Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs`:
- Around line 817-822: In SignMessage, the fallback path obtains cachedPk from
_cache.GetAsync(), which can return schema/chain-mismatched records; instead
call LoadValidCachedRecordAsync() (the same validated loader used by
ReloadAuthTokenFromCacheIfNeeded()) and use its returned record?.AccountAddress
for cachedPk so only validated cache entries are used for the public-key
fallback.
🪄 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: c932c445-6e8d-41d6-a73b-1c225f39d98a

📥 Commits

Reviewing files that changed from the base of the PR and between a5c190b and dd57ff0.

📒 Files selected for processing (120)
  • .gitignore
  • Packages/BouncyCastle.Cryptography.dll.meta
  • Runtime/codebase/SolanaMobileStack/AccountInfo.cs
  • Runtime/codebase/SolanaMobileStack/AccountInfo.cs.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationHelpers.cs
  • Runtime/codebase/SolanaMobileStack/AuthorizationHelpers.cs.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationRecord.cs
  • Runtime/codebase/SolanaMobileStack/AuthorizationRecord.cs.meta
  • Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs
  • Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs.meta
  • Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs
  • Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions/InvalidAuthorizationException.cs
  • Runtime/codebase/SolanaMobileStack/Exceptions/InvalidAuthorizationException.cs.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions/JsonRpcException.cs
  • Runtime/codebase/SolanaMobileStack/Exceptions/JsonRpcException.cs.meta
  • Runtime/codebase/SolanaMobileStack/Exceptions/OperationInFlightException.cs
  • Runtime/codebase/SolanaMobileStack/Exceptions/OperationInFlightException.cs.meta
  • Runtime/codebase/SolanaMobileStack/IAuthorizationCache.cs
  • Runtime/codebase/SolanaMobileStack/IAuthorizationCache.cs.meta
  • Runtime/codebase/SolanaMobileStack/ITransactionPayload.cs
  • Runtime/codebase/SolanaMobileStack/ITransactionPayload.cs.meta
  • Runtime/codebase/SolanaMobileStack/Interfaces/IAdapterOperations.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpc20Client.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/JsonRequest.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/AuthorizationResult.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/CapabilitiesResult.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcClient/Responses/CapabilitiesResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs
  • Runtime/codebase/SolanaMobileStack/JsonRpcErrorCodes.cs.meta
  • Runtime/codebase/SolanaMobileStack/LegacyTransactionPayload.cs
  • Runtime/codebase/SolanaMobileStack/LegacyTransactionPayload.cs.meta
  • Runtime/codebase/SolanaMobileStack/LogVerbosity.cs
  • Runtime/codebase/SolanaMobileStack/LogVerbosity.cs.meta
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterClient.cs
  • Runtime/codebase/SolanaMobileStack/MobileWalletAdapterSession.cs
  • Runtime/codebase/SolanaMobileStack/PlayerPrefsAuthorizationCache.cs
  • Runtime/codebase/SolanaMobileStack/PlayerPrefsAuthorizationCache.cs.meta
  • Runtime/codebase/SolanaMobileStack/ReconnectResult.cs
  • Runtime/codebase/SolanaMobileStack/ReconnectResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/RpcMethodNames.cs
  • Runtime/codebase/SolanaMobileStack/RpcMethodNames.cs.meta
  • Runtime/codebase/SolanaMobileStack/SendOptions.cs
  • Runtime/codebase/SolanaMobileStack/SendOptions.cs.meta
  • Runtime/codebase/SolanaMobileStack/SignAndSendTxResult.cs
  • Runtime/codebase/SolanaMobileStack/SignAndSendTxResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/SignInPayload.cs
  • Runtime/codebase/SolanaMobileStack/SignInPayload.cs.meta
  • Runtime/codebase/SolanaMobileStack/SignInResult.cs
  • Runtime/codebase/SolanaMobileStack/SignInResult.cs.meta
  • Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
  • Runtime/codebase/SolanaWalletAdapter.cs
  • Tests/EditMode/Crypto/EcdsaSignaturesTests.cs
  • Tests/EditMode/Crypto/EcdsaSignaturesTests.cs.meta
  • Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs
  • Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs.meta
  • Tests/EditMode/EditMode.asmdef
  • Tests/EditMode/JsonRpc/ResponseModelTests.cs
  • Tests/EditMode/JsonRpc/ResponseModelTests.cs.meta
  • Tests/EditMode/Mocks/MockMessageSender.cs
  • Tests/EditMode/Mocks/MockMessageSender.cs.meta
  • Tests/EditMode/MwaAdapter.meta
  • Tests/EditMode/MwaAdapter/ConcurrencyTests.cs
  • Tests/EditMode/MwaAdapter/ConcurrencyTests.cs.meta
  • Tests/EditMode/MwaAdapter/DeauthorizeTests.cs
  • Tests/EditMode/MwaAdapter/DeauthorizeTests.cs.meta
  • Tests/EditMode/MwaAdapter/DisconnectTests.cs
  • Tests/EditMode/MwaAdapter/DisconnectTests.cs.meta
  • Tests/EditMode/MwaAdapter/ReconnectTests.cs
  • Tests/EditMode/MwaAdapter/ReconnectTests.cs.meta
  • Tests/EditMode/MwaAdapter/SignAndSendTests.cs
  • Tests/EditMode/MwaAdapter/SignAndSendTests.cs.meta
  • Tests/EditMode/MwaCache.meta
  • Tests/EditMode/MwaCache/CustomCacheInjectionTest.cs
  • Tests/EditMode/MwaCache/CustomCacheInjectionTest.cs.meta
  • Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs
  • Tests/EditMode/MwaCache/PlayerPrefsAuthorizationCacheTests.cs.meta
  • Tests/EditMode/MwaCache/SchemaVersionValidationTests.cs
  • Tests/EditMode/MwaCache/SchemaVersionValidationTests.cs.meta
  • Tests/EditMode/MwaClient/AuthorizeRequestWireTests.cs
  • Tests/EditMode/MwaClient/AuthorizeRequestWireTests.cs.meta
  • Tests/EditMode/MwaClient/DeauthorizeRequestWireTest.cs
  • Tests/EditMode/MwaClient/DeauthorizeRequestWireTest.cs.meta
  • Tests/EditMode/MwaClient/LoginShapeCompatibilityTest.cs
  • Tests/EditMode/MwaClient/LoginShapeCompatibilityTest.cs.meta
  • Tests/EditMode/MwaClient/MobileWalletAdapterClientTests.cs
  • Tests/EditMode/MwaClient/MobileWalletAdapterClientTests.cs.meta
  • Tests/EditMode/MwaParser.meta
  • Tests/EditMode/MwaParser/AuthorizationResponseParserTests.cs
  • Tests/EditMode/MwaParser/AuthorizationResponseParserTests.cs.meta
  • Tests/EditMode/MwaParser/Fixtures.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-bad-base64.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-bad-base64.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-accounts.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-accounts.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-authtoken.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-empty-authtoken.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-full.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-full.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-minimal.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-minimal.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-accounts.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-accounts.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-chains.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-chains.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-display.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-display.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-icon.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-oversize-icon.json.meta
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-wrong-length.json
  • Tests/EditMode/MwaParser/Fixtures/authorize-v2-wrong-length.json.meta
  • Tests/EditMode/MwaParser/ParserVerbosityTests.cs
  • Tests/EditMode/MwaParser/ParserVerbosityTests.cs.meta
  • docs.meta
  • docs/mwa-cache-guide.md
  • docs/mwa-method-reference.md
  • docs/mwa-migration-v1-to-v2.md
  • docs/mwa-quick-start.md
  • docs/sign-and-send-investigation.md
💤 Files with no reviewable changes (4)
  • Tests/EditMode/JsonRpc/ResponseModelTests.cs
  • Tests/EditMode/Mocks/MockMessageSender.cs
  • Tests/EditMode/Crypto/EcdsaSignaturesTests.cs
  • Tests/EditMode/Crypto/MobileWalletAdapterSessionTests.cs

Comment thread docs/mwa-method-reference.md
Comment thread Runtime/codebase/SolanaMobileStack/AuthorizationResponseParser.cs
Comment thread Runtime/codebase/SolanaMobileStack/DeauthorizeResult.cs
Comment thread Runtime/codebase/SolanaMobileStack/ReconnectResult.cs
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Comment thread Runtime/codebase/SolanaMobileStack/SolanaMobileWalletAdapter.cs
Comment thread Runtime/codebase/SolanaWalletAdapter.cs Outdated
Comment thread Tests/EditMode/MwaParser/AuthorizationResponseParserTests.cs
Semaphore gate:
- Wrap _Login, _SignAllTransactions, SignMessage, GetCapabilities in
  TryAcquireGate/ReleaseGate to prevent concurrent state corruption

Bug fixes:
- Preserve WaitForCommitmentToSendNextTransaction when backfilling MinContextSlot
- Set WalletBase.Account on LoginWithSignIn success
- Default null mwaOptions in SolanaWalletAdapter so cache injection works

Upstream PR magicblock-labs#283 test compat:
- Update IAdapterOperationsContractTests for v2 AuthorizeAsync API
- Rewrite SolanaMobileWalletAdapterPrefsTests for v2 cache-based migration
- Fix MobileWalletAdapterClientLifecycleTests Authorize → AuthorizeAsync
@Zurcusa
Copy link
Copy Markdown
Author

Zurcusa commented May 11, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Zurcusa
Copy link
Copy Markdown
Author

Zurcusa commented May 11, 2026

@JoshhSandhu, not sure if you're the right guy to tag for a check.

@Zurcusa
Copy link
Copy Markdown
Author

Zurcusa commented May 13, 2026

Maybe @Kuldotha is the correct person to tag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant