Skip to content

feat(mwa-v2): rewrite adapter with full v2 session lifecycle#5

Open
Zurcusa wants to merge 6 commits into
pr/4-wire-client-cachefrom
pr/5-adapter-session
Open

feat(mwa-v2): rewrite adapter with full v2 session lifecycle#5
Zurcusa wants to merge 6 commits into
pr/4-wire-client-cachefrom
pr/5-adapter-session

Conversation

@Zurcusa
Copy link
Copy Markdown
Owner

@Zurcusa Zurcusa commented May 4, 2026

⚠️ NOTE: This is PR 3 of 3 in the MWA v2 stack. Targets pr/4-wire-client-cache. Completes the MWA v2 migration and removes the v1 backward-compat shims from PR #4.

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

Problem

The existing SolanaMobileWalletAdapter has multiple gaps reported in #273:

  1. No built-in auth token cache — developers must build their own (MWA integration issues found on Solana Seeker with Seed Vault — Unity MWA Example App magicblock-labs/Solana.Unity-SDK#273 item 3)
  2. Logout() only clears local state — no way to revoke MWA authorization (MWA integration issues found on Solana Seeker with Seed Vault — Unity MWA Example App magicblock-labs/Solana.Unity-SDK#273 item 2)
  3. GetCapabilities not exposed (MWA integration issues found on Solana Seeker with Seed Vault — Unity MWA Example App magicblock-labs/Solana.Unity-SDK#273 item 4)
  4. No Sign-In With Solana — users must authorize + sign separately (#189)
  5. No silent reconnect — every app launch prompts the wallet picker
  6. No concurrency protection — overlapping wallet calls cause race conditions

Solution

Complete rewrite of SolanaMobileWalletAdapter with:

Auth cache integration (magicblock-labs#272, magicblock-labs#273 item 3): Constructor-injected IAuthorizationCache with PlayerPrefsAuthorizationCache default. Schema-versioned records with chain-aware validation. Legacy key migration from both pre-v2 "pk"/"authToken" and PR#269 "solana_sdk.mwa.*" keys.

Session lifecycle (magicblock-labs#273 items 2, 4): Disconnect(), Reconnect() (4-state result), Deauthorize() (tri-state), GetCapabilities(), [Obsolete] Logout() with migration path.

Sign-In With Solana (#189): LoginWithSignIn(SignInPayload) — single wallet round-trip that authorizes + signs a SIWS message. Falls back to authorize-then-sign for wallets without native SIWS support.

Concurrency & reliability: SemaphoreSlim(1,1) gate prevents overlapping wallet calls. SignAndSendTransactions auto-populates MinContextSlot from latest blockhash. Error code mapping updated for v2 spec.

Silent-first login: _Login() checks cached auth token first, attempts silent authorize, falls back to fresh authorize only on cache miss or auth_token_rejected.

Shim removal: Removes [Obsolete] Authorize/Reauthorize from IAdapterOperations and MobileWalletAdapterClient. Removes [Obsolete] PublicKey/AccountLabel from AuthorizationResult.

Tests: 5 new EditMode test suites in Tests/EditMode/MwaAdapter/ covering deauthorize, sign-and-send, concurrency, disconnect, and reconnect flows.

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 on-chain RPC submission phase (~23s into processing), 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 required). See docs/sign-and-send-investigation.md for full analysis.

Pre-existing LocalAssociationScenario Issues

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

  1. Missing return in ExecuteNextAction (line 134-135): After CloseAssociation(response), execution falls through to _actions.Dequeue() on an empty queue, throwing InvalidOperationException.
  2. Unbounded OnClose reconnect loop (line 46-50): Reconnects immediately with no delay, no retry limit, and no close-awareness. Creates a thread pool storm when the wallet server is gone.
  3. Wrong timeout unit (line 33): TimeSpan.FromSeconds(clientTimeoutMs) where clientTimeoutMs=9000 creates a 2.5-hour timeout instead of 9 seconds. Should be FromMilliseconds.
  4. Null-unsafe scenarioResult access (line 449 in adapter): scenarioResult.WasSuccessful throws NullReferenceException if the scenario returns null.

Pre-existing test limitations

5 tests that instantiate SolanaMobileWalletAdapter via reflection fail on non-Android platforms because the constructor throws "SolanaMobileWalletAdapter can only be used on Android". This is the same pre-existing limitation documented in upstream PR #276. These tests pass on Android devices.

Deploy Notes

Breaking changes (relative to PR #4):

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

New public API:

  • SolanaMobileWalletAdapter.Disconnect()
  • SolanaMobileWalletAdapter.Reconnect()
  • SolanaMobileWalletAdapter.Deauthorize()
  • SolanaMobileWalletAdapter.LoginWithSignIn(SignInPayload)
  • SolanaMobileWalletAdapter.GetCapabilities()
  • SolanaMobileWalletAdapter.SignAndSendTransactions(Transaction[], SendOptions)

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

@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 41b02b1 to 974d62d Compare May 4, 2026 11:42
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from 39baefc to c22530d Compare May 4, 2026 11:43
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 974d62d to 21cacf8 Compare May 4, 2026 11:52
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from c22530d to 83bc225 Compare May 4, 2026 11:52
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 21cacf8 to c55d626 Compare May 4, 2026 12:11
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch 3 times, most recently from 3fc7019 to 7171262 Compare May 4, 2026 12:56
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from cf349d8 to 650bfe6 Compare May 4, 2026 12:56
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from 7171262 to 3ea6cc9 Compare May 4, 2026 12:56
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 650bfe6 to 83e8569 Compare May 4, 2026 13:10
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch 2 times, most recently from 4707812 to 50ab718 Compare May 5, 2026 11:39
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 83e8569 to 4753dff Compare May 7, 2026 08:19
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from 50ab718 to 37fc64b Compare May 7, 2026 08:19
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from 780f463 to 7912091 Compare May 7, 2026 11:24
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 4753dff to 58bcdfd Compare May 7, 2026 11:26
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from de05078 to 8f9883c Compare May 11, 2026 12:50
Zurcusa added 6 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
@Zurcusa Zurcusa force-pushed the pr/4-wire-client-cache branch from 58bcdfd to 6e74e5e Compare May 11, 2026 13:51
@Zurcusa Zurcusa force-pushed the pr/5-adapter-session branch from 8f9883c to fbbfc6c Compare May 11, 2026 13:51
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