diff --git a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/AdapterOperations.kt b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/AdapterOperations.kt index f4fbadf4c..2ffdf8c87 100644 --- a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/AdapterOperations.kt +++ b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/AdapterOperations.kt @@ -1,20 +1,49 @@ package com.solana.mobilewalletadapter.clientlib import android.net.Uri +import androidx.annotation.Nullable import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient interface AdapterOperations { - suspend fun authorize(identityUri: Uri, iconUri: Uri, identityName: String, rpcCluster: RpcCluster = RpcCluster.MainnetBeta): MobileWalletAdapterClient.AuthorizationResult + + @Deprecated( + "Replaced by updated authorize() method, which adds MWA 2.0 spec support", + replaceWith = ReplaceWith("authorize(identityUri, iconUri, identityName, chain, authToken, features, addresses)"), + DeprecationLevel.WARNING + ) + suspend fun authorize( + identityUri: Uri, + iconUri: Uri, + identityName: String, + rpcCluster: RpcCluster = RpcCluster.MainnetBeta + ): MobileWalletAdapterClient.AuthorizationResult + + suspend fun authorize( + identityUri: Uri, + iconUri: Uri, + identityName: String, + chain: String, + authToken: String? = null, + features: Array? = null, + addresses: Array? = null + ): MobileWalletAdapterClient.AuthorizationResult + suspend fun reauthorize(identityUri: Uri, iconUri: Uri, identityName: String, authToken: String): MobileWalletAdapterClient.AuthorizationResult + suspend fun deauthorize(authToken: String) + suspend fun getCapabilities(): MobileWalletAdapterClient.GetCapabilitiesResult + @Deprecated( "Replaced by signMessagesDetached, which returns the improved MobileWalletAdapterClient.SignMessagesResult type", replaceWith = ReplaceWith("signMessagesDetached(messages, addresses)"), DeprecationLevel.WARNING ) suspend fun signMessages(messages: Array, addresses: Array): MobileWalletAdapterClient.SignPayloadsResult + suspend fun signMessagesDetached(messages: Array, addresses: Array): MobileWalletAdapterClient.SignMessagesResult + suspend fun signTransactions(transactions: Array): MobileWalletAdapterClient.SignPayloadsResult + suspend fun signAndSendTransactions(transactions: Array, params: TransactionParams = DefaultTransactionParams): MobileWalletAdapterClient.SignAndSendTransactionsResult } \ No newline at end of file diff --git a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/DataModels.kt b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/DataModels.kt index cf184b753..34533a081 100644 --- a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/DataModels.kt +++ b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/DataModels.kt @@ -3,14 +3,6 @@ package com.solana.mobilewalletadapter.clientlib import android.net.Uri import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient.AuthorizationResult -sealed class CredentialState { - data class Provided( - val credentials: ConnectionIdentity - ): CredentialState() - - object NotProvided: CredentialState() -} - data class ConnectionIdentity( val identityUri: Uri, val iconUri: Uri, diff --git a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/LocalAdapterOperations.kt b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/LocalAdapterOperations.kt index 7403647be..dc86f39ae 100644 --- a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/LocalAdapterOperations.kt +++ b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/LocalAdapterOperations.kt @@ -16,6 +16,11 @@ class LocalAdapterOperations( var client: MobileWalletAdapterClient? = null + @Deprecated( + "Replaced by updated authorize() method, which adds MWA 2.0 spec support", + replaceWith = ReplaceWith("authorize(identityUri, iconUri, identityName, chain, authToken, features, addresses)"), + DeprecationLevel.WARNING + ) override suspend fun authorize(identityUri: Uri, iconUri: Uri, identityName: String, rpcCluster: RpcCluster): MobileWalletAdapterClient.AuthorizationResult { return withContext(ioDispatcher) { @Suppress("BlockingMethodInNonBlockingContext") @@ -24,6 +29,22 @@ class LocalAdapterOperations( } } + override suspend fun authorize( + identityUri: Uri, + iconUri: Uri, + identityName: String, + chain: String, + authToken: String?, + features: Array?, + addresses: Array? + ): MobileWalletAdapterClient.AuthorizationResult { + return withContext(ioDispatcher) { + @Suppress("BlockingMethodInNonBlockingContext") + client?.authorize(identityUri, iconUri, identityName, chain, authToken, features, addresses)?.get() + ?: throw InvalidObjectException("Provide a client before performing adapter operations") + } + } + override suspend fun reauthorize(identityUri: Uri, iconUri: Uri, identityName: String, authToken: String): MobileWalletAdapterClient.AuthorizationResult { return withContext(ioDispatcher) { @Suppress("BlockingMethodInNonBlockingContext") @@ -48,6 +69,11 @@ class LocalAdapterOperations( } } + @Deprecated( + "Replaced by signMessagesDetached, which returns the improved MobileWalletAdapterClient.SignMessagesResult type", + replaceWith = ReplaceWith("signMessagesDetached(messages, addresses)"), + DeprecationLevel.WARNING + ) override suspend fun signMessages(messages: Array, addresses: Array): MobileWalletAdapterClient.SignPayloadsResult { return withContext(ioDispatcher) { @Suppress("BlockingMethodInNonBlockingContext") diff --git a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapter.kt b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapter.kt index aafe29332..58ff83375 100644 --- a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapter.kt +++ b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapter.kt @@ -7,6 +7,7 @@ import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClie import com.solana.mobilewalletadapter.clientlib.scenario.LocalAssociationIntentCreator import com.solana.mobilewalletadapter.clientlib.scenario.Scenario import com.solana.mobilewalletadapter.common.ProtocolContract +import com.solana.mobilewalletadapter.common.protocol.SessionProperties import kotlinx.coroutines.* import java.io.IOException import java.util.concurrent.CancellationException @@ -15,14 +16,12 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException class MobileWalletAdapter( + private val connectionIdentity: ConnectionIdentity, private val timeout: Int = Scenario.DEFAULT_CLIENT_TIMEOUT_MS, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, private val scenarioProvider: AssociationScenarioProvider = AssociationScenarioProvider(), - connectionIdentity: ConnectionIdentity? = null, ) { - private var credsState: CredentialState = CredentialState.NotProvided - private val adapterOperations = LocalAdapterOperations(ioDispatcher) var authToken: String? = null @@ -31,7 +30,7 @@ class MobileWalletAdapter( * Specify the RPC cluster used for all operations. Note: changing at runtime will invalidate * the auth token and reauthorization will be required */ - var rpcCluster: RpcCluster = RpcCluster.Devnet + var blockchain: Blockchain = Solana.Devnet set(value) { if (value != field) { authToken = null @@ -40,18 +39,31 @@ class MobileWalletAdapter( field = value } - init { - connectionIdentity?.let { - credsState = CredentialState.Provided(it) + @Deprecated( + "RpcCluster provides only Solana clusters; use the Blockchain object for full multi-chain support.", + replaceWith = ReplaceWith("Set `blockchain` property moving forward."), + DeprecationLevel.WARNING + ) + var rpcCluster: RpcCluster = RpcCluster.Devnet + set(value) { + when (value) { + RpcCluster.MainnetBeta -> { + blockchain = Solana.Mainnet + } + RpcCluster.Devnet -> { + blockchain = Solana.Devnet + } + RpcCluster.Testnet -> { + blockchain = Solana.Testnet + } + else -> { } + } + + field = value } - } suspend fun connect(sender: ActivityResultSender): TransactionResult { - return transact(sender) { - if (credsState is CredentialState.NotProvided) { - throw IllegalStateException("App identity credentials must be provided via the constructor to use the connect method.") - } - } + return transact(sender) { } } suspend fun transact( @@ -94,20 +106,23 @@ class MobileWalletAdapter( val client = scenario.start().get(ASSOCIATION_CONNECT_DISCONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) adapterOperations.client = client - val authResult = credsState.let { creds -> - if (creds is CredentialState.Provided) { - with (creds.credentials) { - val authResult = authToken?.let { token -> - adapterOperations.reauthorize(identityUri, iconUri, identityName, token) - } ?: run { - adapterOperations.authorize(identityUri, iconUri, identityName, rpcCluster) - } - - authToken = authResult.authToken - authResult - } + val protocolVersion = scenario.session.sessionProperties.protocolVersion + + val authResult = with (connectionIdentity) { + if (protocolVersion == SessionProperties.ProtocolVersion.V1) { + /** + * TODO: Full MWA 2.0 support has feature & multi-address params. Will be implemented in a future minor release. + * Both the features & addresses params are set to null for now. + */ + adapterOperations.authorize(identityUri, iconUri, identityName, blockchain.fullName, authToken, null, null) } else { - null + authToken?.let { token -> + adapterOperations.reauthorize(identityUri, iconUri, identityName, token) + } ?: run { + adapterOperations.authorize(identityUri, iconUri, identityName, RpcCluster.Custom(blockchain.cluster)) + }.also { + authToken = it.authToken + } } } diff --git a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/TransactionParams.kt b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/TransactionParams.kt index 08f31b0af..95644694a 100644 --- a/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/TransactionParams.kt +++ b/android/clientlib-ktx/src/main/java/com/solana/mobilewalletadapter/clientlib/TransactionParams.kt @@ -2,10 +2,30 @@ package com.solana.mobilewalletadapter.clientlib import com.solana.mobilewalletadapter.common.ProtocolContract +@Deprecated( + "RpcCluster is deprecated as of MWA 2.0", + replaceWith = ReplaceWith("Use the Blockchain object for multi-chain support"), + DeprecationLevel.WARNING +) sealed class RpcCluster(val name: String) { object MainnetBeta : RpcCluster(ProtocolContract.CLUSTER_MAINNET_BETA) object Testnet : RpcCluster(ProtocolContract.CLUSTER_TESTNET) object Devnet : RpcCluster(ProtocolContract.CLUSTER_DEVNET) + class Custom(name: String) : RpcCluster(name) +} + +sealed class Blockchain( + val name: String, + val cluster: String +) { + val fullName + get() = "$name:$cluster" +} + +sealed class Solana { + object Mainnet: Blockchain("solana", "mainnet") + object Testnet: Blockchain("solana", ProtocolContract.CLUSTER_TESTNET) + object Devnet: Blockchain("solana", ProtocolContract.CLUSTER_DEVNET) } open class TransactionParams( diff --git a/android/clientlib-ktx/src/test/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapterTest.kt b/android/clientlib-ktx/src/test/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapterTest.kt index e44ef4284..b58560a48 100644 --- a/android/clientlib-ktx/src/test/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapterTest.kt +++ b/android/clientlib-ktx/src/test/java/com/solana/mobilewalletadapter/clientlib/MobileWalletAdapterTest.kt @@ -3,6 +3,7 @@ package com.solana.mobilewalletadapter.clientlib import android.net.Uri import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient.AuthorizationResult +import com.solana.mobilewalletadapter.common.protocol.SessionProperties import com.solana.mobilewalletadapter.common.util.NotifyingCompletableFuture import kotlinx.coroutines.CancellationException import kotlinx.coroutines.test.StandardTestDispatcher @@ -12,6 +13,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock @@ -19,8 +21,6 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.robolectric.RobolectricTestRunner import java.util.concurrent.TimeoutException -import kotlin.test.assertFailsWith -import kotlin.test.assertIs import kotlin.test.assertTrue @RunWith(RobolectricTestRunner::class) @@ -32,10 +32,44 @@ class MobileWalletAdapterTest { lateinit var mockClient: MobileWalletAdapterClient lateinit var sampleAuthResult: AuthorizationResult lateinit var sampleReauthResult: AuthorizationResult + lateinit var sample20AuthResult: AuthorizationResult lateinit var sender: ActivityResultSender lateinit var mobileWalletAdapter: MobileWalletAdapter + val creds = ConnectionIdentity( + identityUri = Uri.EMPTY, + iconUri = Uri.EMPTY, + identityName = "Test App", + ) + + private fun mockAssociationScenarioProvider(isMwa2: Boolean = false): AssociationScenarioProvider { + val protocol = if (isMwa2) SessionProperties.ProtocolVersion.V1 else SessionProperties.ProtocolVersion.LEGACY + + return mock { + on { provideAssociationScenario(any()) } doAnswer { + mock { + on { start() } doAnswer { + mock> { + on { get(any(), any()) } doReturn mockClient + } + } + on { session } doAnswer { + mock { + on { encodedAssociationPublicKey } doAnswer { byteArrayOf() } + on { sessionProperties } doAnswer { SessionProperties(protocol) } + } + } + on { close() } doAnswer { + val future = NotifyingCompletableFuture() + future.complete(null) + future + } + } + } + } + } + @Before fun before() { testDispatcher = StandardTestDispatcher() @@ -47,11 +81,16 @@ class MobileWalletAdapterTest { } } - sampleAuthResult = AuthorizationResult.create("AUTHRESULTTOKEN", byteArrayOf(), "Some Label", Uri.EMPTY) sampleReauthResult = AuthorizationResult.create("REAUTHRESULTTOKEN", byteArrayOf(), "Some Label", Uri.EMPTY) + sample20AuthResult = AuthorizationResult.create("20AUTHTOKENRESULT", byteArrayOf(), "Some Label", Uri.EMPTY) mockClient = mock { + on { authorize(any(), any(), any(), any(), anyOrNull(), anyOrNull(), anyOrNull()) } doAnswer { + mock { + on { get() } doReturn sample20AuthResult + } + } on { authorize(any(), any(), any(), any()) } doAnswer { mock { on { get() } doReturn sampleAuthResult @@ -64,57 +103,17 @@ class MobileWalletAdapterTest { } } - mockProvider = mock { - on { provideAssociationScenario(any()) } doAnswer { - mock { - on { start() } doAnswer { - mock> { - on { get(any(), any()) } doReturn mockClient - } - } - on { session } doAnswer { - mock { - on { encodedAssociationPublicKey } doAnswer { byteArrayOf() } - } - } - on { close() } doAnswer { - val future = NotifyingCompletableFuture() - future.complete(null) - future - } - } - } - } + mockProvider = mockAssociationScenarioProvider() mobileWalletAdapter = MobileWalletAdapter( + connectionIdentity = creds, scenarioProvider = mockProvider, ioDispatcher = testDispatcher ) } @Test - fun `validate calling connect results in a failure if credentials are not provided first`() = runTest(testDispatcher) { - val result = mobileWalletAdapter.connect(sender) - - assertIs>(result) - assertTrue { result.e.message == "App identity credentials must be provided via the constructor to use the connect method." } - assertTrue { result.successPayload == null } - } - - @Test - fun `validate calling connect is successful when credentials are provided`() = runTest(testDispatcher) { - val creds = ConnectionIdentity( - identityUri = Uri.EMPTY, - iconUri = Uri.EMPTY, - identityName = "Test App", - ) - - mobileWalletAdapter = MobileWalletAdapter( - connectionIdentity = creds, - scenarioProvider = mockProvider, - ioDispatcher = testDispatcher - ) - + fun `validate calling connect is successful using default constructor`() = runTest(testDispatcher) { val result = mobileWalletAdapter.connect(sender) assertTrue { result is TransactionResult.Success } @@ -123,60 +122,43 @@ class MobileWalletAdapterTest { } @Test - fun `validate accessing authresult property without providing creds during transact throws an exception`() = runTest(testDispatcher) { - val result = mobileWalletAdapter.transact(sender) { - authorize(Uri.EMPTY, Uri.EMPTY, "name") - - "No auth result returned" - } - - assertIs>(result) - assertTrue { result.successPayload is String } - assertTrue { result.successPayload == "No auth result returned" } - - assertFailsWith(IllegalStateException::class) { - val willNotUseMe = result.authResult - } - } - - @Test - fun `validate calling transact without provided credentials does not attempt any authorization`() = runTest(testDispatcher) { + fun `validate an MWA v1 session results in the v1 authorize call`() = runTest(testDispatcher) { val refString = "Returning a string for validation" + mockProvider = mockAssociationScenarioProvider() val result = mobileWalletAdapter.transact(sender) { refString } - verify(mockClient, times(0)).authorize(any(), any(), any(), any()) - verify(mockClient, times(0)).reauthorize(any(), any(), any(), any()) - + verify(mockClient, times(1)).authorize(any(), any(), any(), any()) + assertTrue { result is TransactionResult.Success } assertTrue { result.successPayload == refString } + assertTrue { (result as TransactionResult.Success).authResult == sampleAuthResult } } @Test - fun `validate setting auth token or rpc cluster does not activate automatic auth or reauth`() = runTest(testDispatcher) { + fun `validate an MWA v2 session results in the v2 authorize call`() = runTest(testDispatcher) { val refString = "Returning a string for validation" + mockProvider = mockAssociationScenarioProvider(true) - mobileWalletAdapter.transact(sender) { } - - verify(mockClient, times(0)).authorize(any(), any(), any(), any()) - - mobileWalletAdapter.authToken = "SOMETOKENTHATWONTAFFECTANTYHING" - mobileWalletAdapter.rpcCluster = RpcCluster.Devnet + mobileWalletAdapter = MobileWalletAdapter( + connectionIdentity = creds, + scenarioProvider = mockProvider, + ioDispatcher = testDispatcher + ) - mobileWalletAdapter.connect(sender) - val result2 = mobileWalletAdapter.transact(sender) { - refString + "hi" + val result = mobileWalletAdapter.transact(sender) { + refString } - verify(mockClient, times(0)).authorize(any(), any(), any(), any()) - verify(mockClient, times(0)).reauthorize(any(), any(), any(), any()) - - assertTrue { result2.successPayload == refString + "hi" } + verify(mockClient, times(1)).authorize(any(), any(), any(), any(), anyOrNull(), anyOrNull(),anyOrNull()) + assertTrue { result is TransactionResult.Success } + assertTrue { result.successPayload == refString } + assertTrue { (result as TransactionResult.Success).authResult == sample20AuthResult } } @Test - fun `validate providing credentials in the constructor results in an authorize call`() = runTest(testDispatcher) { + fun `validate for a v1 session providing an authToken before transact results in an reauthorize call`() = runTest(testDispatcher) { val refString = "Returning a string for validation" val creds = ConnectionIdentity( identityUri = Uri.EMPTY, @@ -189,25 +171,23 @@ class MobileWalletAdapterTest { scenarioProvider = mockProvider, ioDispatcher = testDispatcher ) + mobileWalletAdapter.authToken = "ASAMPLETOKEN" val result = mobileWalletAdapter.transact(sender) { refString } - verify(mockClient, times(1)).authorize(any(), any(), any(), any()) + verify(mockClient, times(1)).reauthorize(any(), any(), any(), any()) + assertTrue { result is TransactionResult.Success } assertTrue { result.successPayload == refString } - assertTrue { (result as TransactionResult.Success).authResult == sampleAuthResult } + assertTrue { (result as TransactionResult.Success).authResult == sampleReauthResult } } @Test - fun `validate providing credentials and an authToken before transact results in an reauthorize call`() = runTest(testDispatcher) { + fun `validate for a v2 session providing an authToken before transact results in the authorize call`() = runTest(testDispatcher) { val refString = "Returning a string for validation" - val creds = ConnectionIdentity( - identityUri = Uri.EMPTY, - iconUri = Uri.EMPTY, - identityName = "Test App", - ) + mockProvider = mockAssociationScenarioProvider(true) mobileWalletAdapter = MobileWalletAdapter( connectionIdentity = creds, @@ -220,15 +200,15 @@ class MobileWalletAdapterTest { refString } - verify(mockClient, times(1)).reauthorize(any(), any(), any(), any()) + verify(mockClient, times(1)).authorize(any(), any(), any(), any(), any(), anyOrNull(),anyOrNull()) assertTrue { result is TransactionResult.Success } assertTrue { result.successPayload == refString } - assertTrue { (result as TransactionResult.Success).authResult == sampleReauthResult } + assertTrue { (result as TransactionResult.Success).authResult == sample20AuthResult } } @Test - fun `validate providing credentials and an authToken then calling transact multiple times results in reauth each time`() = runTest(testDispatcher) { + fun `validate for a v1 session providing an authToken then calling transact multiple times results in reauth each time`() = runTest(testDispatcher) { val creds = ConnectionIdentity( identityUri = Uri.EMPTY, iconUri = Uri.EMPTY, @@ -300,7 +280,7 @@ class MobileWalletAdapterTest { } @Test - fun `validate the proper auth token is returned on when authenticating and re-authenticating`() = runTest(testDispatcher) { + fun `validate for a v1 session the proper auth token is returned on when authenticating and re-authenticating`() = runTest(testDispatcher) { val creds = ConnectionIdentity( identityUri = Uri.EMPTY, iconUri = Uri.EMPTY, @@ -325,18 +305,7 @@ class MobileWalletAdapterTest { } @Test - fun `validate changing the rpc cluster at runtime invalidates the auth token and calls authorize`() = runTest(testDispatcher) { - val creds = ConnectionIdentity( - identityUri = Uri.EMPTY, - iconUri = Uri.EMPTY, - identityName = "Test App", - ) - - mobileWalletAdapter = MobileWalletAdapter( - connectionIdentity = creds, - scenarioProvider = mockProvider, - ioDispatcher = testDispatcher - ) + fun `validate for a v1 session changing the rpc cluster at runtime invalidates the auth token and calls authorize`() = runTest(testDispatcher) { mobileWalletAdapter.authToken = "SOMEAUTHTOKEN" assertTrue { mobileWalletAdapter.rpcCluster is RpcCluster.Devnet } @@ -350,4 +319,34 @@ class MobileWalletAdapterTest { verify(mockClient, times(1)).authorize(any(), any(), any(), any()) } + + @Test + fun `validate for setting the rpcCluster property properly sets the blockchain property`() { + mobileWalletAdapter.rpcCluster = RpcCluster.MainnetBeta + + assertTrue { mobileWalletAdapter.blockchain is Solana.Mainnet } + + mobileWalletAdapter.rpcCluster = RpcCluster.Devnet + + assertTrue { mobileWalletAdapter.blockchain is Solana.Devnet } + + mobileWalletAdapter.rpcCluster = RpcCluster.Testnet + + assertTrue { mobileWalletAdapter.blockchain is Solana.Testnet } + } + + @Test + fun `validate setting both cluster and blockchain values at runtime reset the auth token`() { + mobileWalletAdapter.authToken = "SOMETOKENBYEBYE" + + mobileWalletAdapter.rpcCluster = RpcCluster.MainnetBeta + + assertTrue { mobileWalletAdapter.authToken == null } + + mobileWalletAdapter.authToken = "SOMETOKENBYEBYE" + + mobileWalletAdapter.blockchain = Solana.Testnet + + assertTrue { mobileWalletAdapter.authToken == null } + } } \ No newline at end of file diff --git a/android/clientlib/src/main/java/com/solana/mobilewalletadapter/clientlib/protocol/MobileWalletAdapterSession.java b/android/clientlib/src/main/java/com/solana/mobilewalletadapter/clientlib/protocol/MobileWalletAdapterSession.java index de857760a..aa4b6e637 100644 --- a/android/clientlib/src/main/java/com/solana/mobilewalletadapter/clientlib/protocol/MobileWalletAdapterSession.java +++ b/android/clientlib/src/main/java/com/solana/mobilewalletadapter/clientlib/protocol/MobileWalletAdapterSession.java @@ -130,6 +130,8 @@ protected void handleSessionEstablishmentMessage(@NonNull byte[] payload) Arrays.copyOfRange(payload, ECDSAKeys.ENCODED_PUBLIC_KEY_LENGTH_BYTES, payload.length); sessionProperties = parseSessionProps(encryptedSessionProperties); } + } catch (IndexOutOfBoundsException ignored) { + Log.w(TAG, "could not parse session properties, falling back on legacy session"); } finally { mSessionProperties = sessionProperties; }