From c1b15bd228aad10dec10414bebc68dfe46d33102 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 12 Feb 2026 14:29:03 -0800 Subject: [PATCH 1/7] WIP --- .../multichainApiClientWrapper/index.ts | 3 ++- .../src/multichain/transports/mwp/index.ts | 10 ++++--- playground/browser-playground/src/App.tsx | 5 +++- .../src/components/SolanaWalletCard.tsx | 26 ++++++++++++------- playground/playground-ui/src/testIds/index.ts | 21 +++++++++++++++ 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/packages/connect-multichain/src/multichain/transports/multichainApiClientWrapper/index.ts b/packages/connect-multichain/src/multichain/transports/multichainApiClientWrapper/index.ts index 2be359cb..f0940c99 100644 --- a/packages/connect-multichain/src/multichain/transports/multichainApiClientWrapper/index.ts +++ b/packages/connect-multichain/src/multichain/transports/multichainApiClientWrapper/index.ts @@ -73,7 +73,8 @@ export class MultichainApiClientWrapperTransport implements Transport { async connect(): Promise { console.log('📚 connect'); - await this.metamaskConnectMultichain.emitSessionChanged(); + // DOES THIS NEED TO BE FIRED?... + // await this.metamaskConnectMultichain.emitSessionChanged(); } async disconnect(): Promise { diff --git a/packages/connect-multichain/src/multichain/transports/mwp/index.ts b/packages/connect-multichain/src/multichain/transports/mwp/index.ts index d8067b91..4f74ad92 100644 --- a/packages/connect-multichain/src/multichain/transports/mwp/index.ts +++ b/packages/connect-multichain/src/multichain/transports/mwp/index.ts @@ -538,8 +538,11 @@ export class MWPTransport implements ExtendedTransport { ); // This might not actually get excuted on the wallet if the user doesn't open - // their wallet before the message TTL - this.request({ method: 'wallet_revokeSession', params: { scopes } }); + // their wallet before the message TTL or if the underlying transport isn't actually connected + // Purposely not awaiting this to avoid blocking the disconnect flow + this.request({ method: 'wallet_revokeSession', params: { scopes } }).catch((err) => { + console.error('error revoking session', err); + }); // Clear the cached values for eth_accounts and eth_chainId if all eip155 scopes were removed. const remainingScopesIncludeEip155 = remainingScopes.some((scope) => scope.includes('eip155')); @@ -619,7 +622,7 @@ export class MWPTransport implements ExtendedTransport { }); } catch (error) { return Promise.reject( - new Error(`Failed to resume session: ${error.message}`), + new Error(`Failed to resume session: ${error.message}`), // ); } } @@ -701,6 +704,7 @@ export class MWPTransport implements ExtendedTransport { } if (!this.isConnected()) { + console.log('attempting to resume session', payload); await this.attemptResumeSession(); } diff --git a/playground/browser-playground/src/App.tsx b/playground/browser-playground/src/App.tsx index c97180a2..21ce7fa0 100644 --- a/playground/browser-playground/src/App.tsx +++ b/playground/browser-playground/src/App.tsx @@ -55,12 +55,15 @@ function App() { const { connected: solanaConnected, + connecting: solanaConnecting, + disconnecting: solanaDisconnecting, publicKey: solanaPublicKey, wallets, select, - connect: solanaConnect, } = useWallet(); + console.log({solanaConnected, solanaConnecting, solanaDisconnecting, solanaPublicKey, wallets}); + const handleCheckboxChange = useCallback( (value: string, isChecked: boolean) => { if (isChecked) { diff --git a/playground/browser-playground/src/components/SolanaWalletCard.tsx b/playground/browser-playground/src/components/SolanaWalletCard.tsx index b55d14c2..6eae94c5 100644 --- a/playground/browser-playground/src/components/SolanaWalletCard.tsx +++ b/playground/browser-playground/src/components/SolanaWalletCard.tsx @@ -2,6 +2,7 @@ import { useConnection, useWallet } from '@solana/wallet-adapter-react'; import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'; import { PublicKey, SystemProgram, Transaction, LAMPORTS_PER_SOL } from '@solana/web3.js'; import { useCallback, useState } from 'react'; +import { TEST_IDS } from '@metamask/playground-ui'; /** * SolanaWalletCard component displays Solana wallet connection status @@ -111,15 +112,16 @@ export const SolanaWalletCard: React.FC = () => { }, [publicKey, sendTransaction, connection]); return ( -
+
-

+

☀️ Solana Wallet

{connected && ( {signedMessage && ( -
+

Signed Message

{signedMessage} @@ -189,13 +193,14 @@ export const SolanaWalletCard: React.FC = () => { {/* Transaction Section */} {connected && ( -

+

Transactions

{transactionSignature && ( -
+

Transaction Signature

{transactionSignature} @@ -225,7 +231,7 @@ export const SolanaWalletCard: React.FC = () => { {/* Error Display */} {error && ( -

+

{error}

)} diff --git a/playground/playground-ui/src/testIds/index.ts b/playground/playground-ui/src/testIds/index.ts index 211f736a..0ba0c17c 100644 --- a/playground/playground-ui/src/testIds/index.ts +++ b/playground/playground-ui/src/testIds/index.ts @@ -209,6 +209,27 @@ export const TEST_IDS = { connectorChainId: 'wagmi-connector-chain-id', }, + // ============================================ + // SOLANA CARD + // ============================================ + solana: { + card: 'solana-card', + title: 'solana-title', + btnConnect: 'solana-btn-connect', + btnDisconnect: 'solana-btn-disconnect', + status: 'solana-status', + addressContainer: 'solana-address-container', + signMessageSection: 'solana-section-sign-message', + inputMessage: 'solana-input-message', + btnSignMessage: 'solana-btn-sign-message', + signedMessageResult: 'solana-signed-message-result', + transactionsSection: 'solana-section-transactions', + btnSignTransaction: 'solana-btn-sign-transaction', + btnSendTransaction: 'solana-btn-send-transaction', + transactionSignatureResult: 'solana-transaction-signature-result', + errorContainer: 'solana-error-container', + }, + // ============================================ // WALLET LIST (Browser only) // ============================================ From f191ad4a81e998d980d4f1dbe794591936a46981 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 13 Feb 2026 13:11:05 -0800 Subject: [PATCH 2/7] patch beforeunload bug in @solana/wallet-adapter-react --- ...adapter-react-npm-0.15.39-86277fdcc0.patch | 165 ++++++++++++++++++ package.json | 3 +- playground/browser-playground/package.json | 2 +- yarn.lock | 18 +- 4 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 .yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch diff --git a/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch b/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch new file mode 100644 index 00000000..bed7f3f2 --- /dev/null +++ b/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch @@ -0,0 +1,165 @@ +diff --git a/lib/cjs/WalletProvider.js b/lib/cjs/WalletProvider.js +index 9c3d07cabf94c064cf9750480a77a856ab799fda..9e7f01bd258288fe6705190842232f0c3788c619 100644 +--- a/lib/cjs/WalletProvider.js ++++ b/lib/cjs/WalletProvider.js +@@ -144,26 +144,30 @@ function WalletProvider({ children, wallets: adapters, autoConnect, localStorage + }); + }, [autoConnect, adapter]); + const isUnloadingRef = (0, react_1.useRef)(false); +- (0, react_1.useEffect)(() => { +- if (walletName === wallet_adapter_mobile_1.SolanaMobileWalletAdapterWalletName && getIsMobile(adaptersWithStandardAdapters)) { +- isUnloadingRef.current = false; +- return; +- } +- function handleBeforeUnload() { +- isUnloadingRef.current = true; +- } +- /** +- * Some wallets fire disconnection events when the window unloads. Since there's no way to +- * distinguish between a disconnection event received because a user initiated it, and one +- * that was received because they've closed the window, we have to track window unload +- * events themselves. Downstream components use this information to decide whether to act +- * upon or drop wallet events and errors. +- */ +- window.addEventListener('beforeunload', handleBeforeUnload); +- return () => { +- window.removeEventListener('beforeunload', handleBeforeUnload); +- }; +- }, [adaptersWithStandardAdapters, walletName]); ++ // beforeunload event removed to avoid bug with Chrome on Android ++ // We also only except to handle the MetaMask solana wallet standard provider ++ // which does not emit a disconnect event when the window is unloaded which ++ // the commented code block was trying to handle. ++ // (0, react_1.useEffect)(() => { ++ // if (walletName === wallet_adapter_mobile_1.SolanaMobileWalletAdapterWalletName && getIsMobile(adaptersWithStandardAdapters)) { ++ // isUnloadingRef.current = false; ++ // return; ++ // } ++ // function handleBeforeUnload() { ++ // isUnloadingRef.current = true; ++ // } ++ // /** ++ // * Some wallets fire disconnection events when the window unloads. Since there's no way to ++ // * distinguish between a disconnection event received because a user initiated it, and one ++ // * that was received because they've closed the window, we have to track window unload ++ // * events themselves. Downstream components use this information to decide whether to act ++ // * upon or drop wallet events and errors. ++ // */ ++ // window.addEventListener('beforeunload', handleBeforeUnload); ++ // return () => { ++ // window.removeEventListener('beforeunload', handleBeforeUnload); ++ // }; ++ // }, [adaptersWithStandardAdapters, walletName]); + const handleConnectError = (0, react_1.useCallback)(() => { + if (adapter) { + // If any error happens while connecting, unset the adapter. +diff --git a/lib/esm/WalletProvider.js b/lib/esm/WalletProvider.js +index d5dcd36332ead1ede05424537577158a033fa2f0..6eb1042aad4f52bf331023551dcbbc8b5e98877c 100644 +--- a/lib/esm/WalletProvider.js ++++ b/lib/esm/WalletProvider.js +@@ -95,26 +95,30 @@ export function WalletProvider({ children, wallets: adapters, autoConnect, local + }; + }, [autoConnect, adapter]); + const isUnloadingRef = useRef(false); +- useEffect(() => { +- if (walletName === SolanaMobileWalletAdapterWalletName && getIsMobile(adaptersWithStandardAdapters)) { +- isUnloadingRef.current = false; +- return; +- } +- function handleBeforeUnload() { +- isUnloadingRef.current = true; +- } +- /** +- * Some wallets fire disconnection events when the window unloads. Since there's no way to +- * distinguish between a disconnection event received because a user initiated it, and one +- * that was received because they've closed the window, we have to track window unload +- * events themselves. Downstream components use this information to decide whether to act +- * upon or drop wallet events and errors. +- */ +- window.addEventListener('beforeunload', handleBeforeUnload); +- return () => { +- window.removeEventListener('beforeunload', handleBeforeUnload); +- }; +- }, [adaptersWithStandardAdapters, walletName]); ++ // beforeunload event removed to avoid bug with Chrome on Android ++ // We also only except to handle the MetaMask solana wallet standard provider ++ // which does not emit a disconnect event when the window is unloaded which ++ // the commented code block was trying to handle. ++ // useEffect(() => { ++ // if (walletName === SolanaMobileWalletAdapterWalletName && getIsMobile(adaptersWithStandardAdapters)) { ++ // isUnloadingRef.current = false; ++ // return; ++ // } ++ // function handleBeforeUnload() { ++ // isUnloadingRef.current = true; ++ // } ++ // /** ++ // * Some wallets fire disconnection events when the window unloads. Since there's no way to ++ // * distinguish between a disconnection event received because a user initiated it, and one ++ // * that was received because they've closed the window, we have to track window unload ++ // * events themselves. Downstream components use this information to decide whether to act ++ // * upon or drop wallet events and errors. ++ // */ ++ // window.addEventListener('beforeunload', handleBeforeUnload); ++ // return () => { ++ // window.removeEventListener('beforeunload', handleBeforeUnload); ++ // }; ++ // }, [adaptersWithStandardAdapters, walletName]); + const handleConnectError = useCallback(() => { + if (adapter) { + // If any error happens while connecting, unset the adapter. +diff --git a/src/WalletProvider.tsx b/src/WalletProvider.tsx +index 7d4723ef7877ed1c2da3bd57f74ec7b12743e085..601470de23a877232ce11262cca1758990e64425 100644 +--- a/src/WalletProvider.tsx ++++ b/src/WalletProvider.tsx +@@ -124,26 +124,30 @@ export function WalletProvider({ + }; + }, [autoConnect, adapter]); + const isUnloadingRef = useRef(false); +- useEffect(() => { +- if (walletName === SolanaMobileWalletAdapterWalletName && getIsMobile(adaptersWithStandardAdapters)) { +- isUnloadingRef.current = false; +- return; +- } +- function handleBeforeUnload() { +- isUnloadingRef.current = true; +- } +- /** +- * Some wallets fire disconnection events when the window unloads. Since there's no way to +- * distinguish between a disconnection event received because a user initiated it, and one +- * that was received because they've closed the window, we have to track window unload +- * events themselves. Downstream components use this information to decide whether to act +- * upon or drop wallet events and errors. +- */ +- window.addEventListener('beforeunload', handleBeforeUnload); +- return () => { +- window.removeEventListener('beforeunload', handleBeforeUnload); +- }; +- }, [adaptersWithStandardAdapters, walletName]); ++ // beforeunload event removed to avoid bug with Chrome on Android ++ // We also only except to handle the MetaMask solana wallet standard provider ++ // which does not emit a disconnect event when the window is unloaded which ++ // the commented code block was trying to handle. ++ // useEffect(() => { ++ // if (walletName === SolanaMobileWalletAdapterWalletName && getIsMobile(adaptersWithStandardAdapters)) { ++ // isUnloadingRef.current = false; ++ // return; ++ // } ++ // function handleBeforeUnload() { ++ // isUnloadingRef.current = true; ++ // } ++ // /** ++ // * Some wallets fire disconnection events when the window unloads. Since there's no way to ++ // * distinguish between a disconnection event received because a user initiated it, and one ++ // * that was received because they've closed the window, we have to track window unload ++ // * events themselves. Downstream components use this information to decide whether to act ++ // * upon or drop wallet events and errors. ++ // */ ++ // window.addEventListener('beforeunload', handleBeforeUnload); ++ // return () => { ++ // window.removeEventListener('beforeunload', handleBeforeUnload); ++ // }; ++ // }, [adaptersWithStandardAdapters, walletName]); + const handleConnectError = useCallback(() => { + if (adapter) { + // If any error happens while connecting, unset the adapter. diff --git a/package.json b/package.json index 430ec374..e8d0d776 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "pre-push": "yarn lint" }, "resolutions": { - "bs58": "4.0.1" + "bs58": "4.0.1", + "@solana/wallet-adapter-react@npm:^0.15.39": "patch:@solana/wallet-adapter-react@npm%3A0.15.39#~/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch" }, "devDependencies": { "@babel/core": "^7.28.4", diff --git a/playground/browser-playground/package.json b/playground/browser-playground/package.json index 0ba4148c..55dad2b2 100644 --- a/playground/browser-playground/package.json +++ b/playground/browser-playground/package.json @@ -63,7 +63,7 @@ "@metamask/utils": "^11.8.1", "@open-rpc/meta-schema": "^1.14.9", "@open-rpc/schema-utils-js": "^2.0.5", - "@solana/wallet-adapter-react": "^0.15.35", + "@solana/wallet-adapter-react": "patch:@solana/wallet-adapter-react@npm%3A0.15.39#~/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch", "@solana/wallet-adapter-react-ui": "^0.9.35", "@solana/wallet-standard-chains": "^1.1.1", "@solana/web3.js": "^1.98.0", diff --git a/yarn.lock b/yarn.lock index dccd4d30..b6ec0686 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4016,7 +4016,7 @@ __metadata: "@metamask/utils": "npm:^11.8.1" "@open-rpc/meta-schema": "npm:^1.14.9" "@open-rpc/schema-utils-js": "npm:^2.0.5" - "@solana/wallet-adapter-react": "npm:^0.15.35" + "@solana/wallet-adapter-react": "patch:@solana/wallet-adapter-react@npm%3A0.15.39#~/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch" "@solana/wallet-adapter-react-ui": "npm:^0.9.35" "@solana/wallet-standard-chains": "npm:^1.1.1" "@solana/web3.js": "npm:^1.98.0" @@ -7572,7 +7572,7 @@ __metadata: languageName: node linkType: hard -"@solana/wallet-adapter-react@npm:^0.15.35, @solana/wallet-adapter-react@npm:^0.15.39": +"@solana/wallet-adapter-react@npm:0.15.39": version: 0.15.39 resolution: "@solana/wallet-adapter-react@npm:0.15.39" dependencies: @@ -7586,6 +7586,20 @@ __metadata: languageName: node linkType: hard +"@solana/wallet-adapter-react@patch:@solana/wallet-adapter-react@npm%3A0.15.39#~/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch": + version: 0.15.39 + resolution: "@solana/wallet-adapter-react@patch:@solana/wallet-adapter-react@npm%3A0.15.39#~/.yarn/patches/@solana-wallet-adapter-react-npm-0.15.39-86277fdcc0.patch::version=0.15.39&hash=9a4810" + dependencies: + "@solana-mobile/wallet-adapter-mobile": "npm:^2.2.0" + "@solana/wallet-adapter-base": "npm:^0.9.27" + "@solana/wallet-standard-wallet-adapter-react": "npm:^1.1.4" + peerDependencies: + "@solana/web3.js": ^1.98.0 + react: "*" + checksum: 10/f1e63779183334265164d1d44caa2d14d9a3f7524dc216afb1a35662832effe9316d834cf34d2d31a42bf6f004d85928421ef8faeaddc6d1db21b3318dc89fa1 + languageName: node + linkType: hard + "@solana/wallet-standard-chains@npm:^1.1.0, @solana/wallet-standard-chains@npm:^1.1.1": version: 1.1.1 resolution: "@solana/wallet-standard-chains@npm:1.1.1" From 0c58cc9bb8a5fa57a3c41d1257deeea166e89c6d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 13 Feb 2026 14:12:41 -0800 Subject: [PATCH 3/7] add evm and legacy card disconnect test ids --- playground/browser-playground/src/components/LegacyEVMCard.tsx | 1 + playground/browser-playground/src/components/WagmiCard.tsx | 1 + playground/playground-ui/src/testIds/index.ts | 2 ++ 3 files changed, 4 insertions(+) diff --git a/playground/browser-playground/src/components/LegacyEVMCard.tsx b/playground/browser-playground/src/components/LegacyEVMCard.tsx index a3ceb6f7..4e0171f2 100644 --- a/playground/browser-playground/src/components/LegacyEVMCard.tsx +++ b/playground/browser-playground/src/components/LegacyEVMCard.tsx @@ -177,6 +177,7 @@ export function LegacyEVMCard({