Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MWA 2.0 clientlib-ktx support #576

Merged
merged 13 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<String>? = null,
addresses: Array<ByteArray>? = 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<ByteArray>, addresses: Array<ByteArray>): MobileWalletAdapterClient.SignPayloadsResult

suspend fun signMessagesDetached(messages: Array<ByteArray>, addresses: Array<ByteArray>): MobileWalletAdapterClient.SignMessagesResult

suspend fun signTransactions(transactions: Array<ByteArray>): MobileWalletAdapterClient.SignPayloadsResult

suspend fun signAndSendTransactions(transactions: Array<ByteArray>, params: TransactionParams = DefaultTransactionParams): MobileWalletAdapterClient.SignAndSendTransactionsResult
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -24,6 +29,22 @@ class LocalAdapterOperations(
}
}

override suspend fun authorize(
identityUri: Uri,
iconUri: Uri,
identityName: String,
chain: String,
authToken: String?,
Funkatronics marked this conversation as resolved.
Show resolved Hide resolved
features: Array<String>?,
addresses: Array<ByteArray>?
): 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")
Expand All @@ -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<ByteArray>, addresses: Array<ByteArray>): MobileWalletAdapterClient.SignPayloadsResult {
return withContext(ioDispatcher) {
@Suppress("BlockingMethodInNonBlockingContext")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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<Unit> {
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 <T> transact(
Expand Down Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The "credentials" (now "identity") are always going to be provided in 2.0

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Funkatronics marked this conversation as resolved.
Show resolved Hide resolved
}

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(
Expand Down
Loading