Skip to content

Commit

Permalink
Expose MWA 2.0 stuff in Fakedapp (#583)
Browse files Browse the repository at this point in the history
* start update UI

* give fakewallet flavors same appId (for testing)

* try to give legacy version a more realistic setup

* use string resources for displayed protocol version
  • Loading branch information
Funkatronics authored Oct 24, 2023
1 parent 7f3126e commit 2037ffe
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.solana.mobilewalletadapter.common.protocol.SessionProperties
import com.solana.mobilewalletadapter.fakedapp.databinding.ActivityMainBinding
import com.solana.mobilewalletadapter.fakedapp.usecase.Base58EncodeUseCase
import com.solana.mobilewalletadapter.fakedapp.usecase.MemoTransactionVersion
import com.solana.mobilewalletadapter.fakedapp.usecase.MobileWalletAdapterUseCase.StartMobileWalletAdapterActivity
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -54,11 +56,22 @@ class MainActivity : AppCompatActivity() {
if (spinnerPos > 0) viewBinding.spinnerTxnVer.setSelection(spinnerPos)
}

viewBinding.tvAccountName.text =
uiState.accountLabel ?: getString(R.string.string_no_account_name)
viewBinding.spinnerAccounts.adapter =
ArrayAdapter(this@MainActivity, android.R.layout.simple_spinner_item,
uiState.accounts?.map { account ->
account.accountLabel ?: Base58EncodeUseCase.invoke(account.publicKey)
} ?: listOf()
)

viewBinding.tvWalletUriPrefix.text =
uiState.walletUriBase?.toString()
?: getString(R.string.string_no_wallet_uri_prefix)
uiState.walletUriBase?.toString() ?: getString(R.string.string_no_wallet_uri_prefix)
viewBinding.tvSessionVersion.text =
getString(uiState.sessionProtocolVersion?.let {
when (it) {
SessionProperties.ProtocolVersion.LEGACY -> R.string.string_session_version_legacy
SessionProperties.ProtocolVersion.V1 -> R.string.string_session_version_v1
}
} ?: R.string.string_no_session_version)

if (uiState.messages.isNotEmpty()) {
val message = uiState.messages.first()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import androidx.annotation.StringRes
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient
import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient.AuthorizationResult.AuthorizedAccount
import com.solana.mobilewalletadapter.clientlib.scenario.LocalAssociationIntentCreator
import com.solana.mobilewalletadapter.clientlib.transaction.TransactionVersion
import com.solana.mobilewalletadapter.common.ProtocolContract
import com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion
import com.solana.mobilewalletadapter.fakedapp.usecase.*
import com.solana.mobilewalletadapter.fakedapp.usecase.MobileWalletAdapterUseCase.StartMobileWalletAdapterActivity
import kotlinx.coroutines.*
Expand Down Expand Up @@ -111,6 +113,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
Log.d(TAG, "Capabilities: $it")
Log.d(TAG, "Supports legacy transactions: ${TransactionVersion.supportsLegacy(it.supportedTransactionVersions)}")
Log.d(TAG, "Supports v0 transactions: ${TransactionVersion.supportsVersion(it.supportedTransactionVersions, 0)}")
Log.d(TAG, "Supported features: ${it.supportedOptionalFeatures.contentToString()}")
showMessage(R.string.msg_request_succeeded)
}
} catch (e: MobileWalletAdapterUseCase.LocalAssociationFailedException) {
Expand All @@ -124,7 +127,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {

fun requestAirdrop() = viewModelScope.launch {
try {
RequestAirdropUseCase(CLUSTER_RPC_URI, _uiState.value.publicKey!!)
RequestAirdropUseCase(CLUSTER_RPC_URI, _uiState.value.primaryPublicKey!!)
Log.d(TAG, "Airdrop request sent")
showMessage(R.string.msg_airdrop_request_sent)
} catch (e: RequestAirdropUseCase.AirdropFailedException) {
Expand Down Expand Up @@ -152,7 +155,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
val (blockhash, _) = latestBlockhash.await()
val transactions = Array(numTransactions) {
transactionUseCase.create(uiState.value.publicKey!!, blockhash)
transactionUseCase.create(uiState.value.primaryPublicKey!!, blockhash)
}
client.signTransactions(transactions).also {
Log.d(TAG, "Signed transaction(s): $it")
Expand All @@ -174,7 +177,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {

val verified = signedTransactions.map { txn ->
try {
transactionUseCase.verify(uiState.value.publicKey!!, txn)
transactionUseCase.verify(uiState.value.primaryPublicKey!!, txn)
true
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Memo transaction signature verification failed", e)
Expand All @@ -198,7 +201,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
val (blockhash, _) = latestBlockhash.await()
val transactions = arrayOf(
transactionUseCase.create(uiState.value.publicKey!!, blockhash)
transactionUseCase.create(uiState.value.primaryPublicKey!!, blockhash)
)
client.signTransactions(transactions).also {
Log.d(TAG, "Signed transaction(s): $it")
Expand All @@ -220,7 +223,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {

val verified = signedTransactions.map { txn ->
try {
transactionUseCase.verify(uiState.value.publicKey!!, txn)
transactionUseCase.verify(uiState.value.primaryPublicKey!!, txn)
true
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Memo transaction signature verification failed", e)
Expand All @@ -247,10 +250,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
doAuthorize(client, IDENTITY, CLUSTER_NAME)

message =
"Sign this message to prove you own account ${Base58EncodeUseCase(uiState.value.publicKey!!)}".encodeToByteArray()
"Sign this message to prove you own account ${Base58EncodeUseCase(uiState.value.primaryPublicKey!!)}".encodeToByteArray()
val signMessagesResult = client.signMessagesDetached(
arrayOf(message),
arrayOf(uiState.value.publicKey!!)
arrayOf(uiState.value.primaryPublicKey!!)
)

Log.d(TAG, "Simulating a short delay while we do something with the message the user just signed...")
Expand All @@ -259,7 +262,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {

val (blockhash, slot) = latestBlockhash.await()
val transaction =
arrayOf(transactionUseCase.create(uiState.value.publicKey!!, blockhash))
arrayOf(transactionUseCase.create(uiState.value.primaryPublicKey!!, blockhash))
val signAndSendTransactionsResult = client.signAndSendTransactions(transaction, slot)

signMessagesResult[0] to signAndSendTransactionsResult[0]
Expand All @@ -282,7 +285,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
OffChainMessageSigningUseCase.verify(
signedMessage.message,
signedMessage.signatures[0],
uiState.value.publicKey!!,
uiState.value.primaryPublicKey!!,
message
)
true
Expand Down Expand Up @@ -315,7 +318,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
doReauthorize(client, IDENTITY, _uiState.value.authToken!!).also {
Log.d(TAG, "Reauthorized: $it")
}
client.signMessagesDetached(messages, arrayOf(_uiState.value.publicKey!!)).also {
client.signMessagesDetached(messages, arrayOf(_uiState.value.primaryPublicKey!!)).also {
Log.d(TAG, "Signed message(s): $it")
}
}
Expand All @@ -335,7 +338,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
OffChainMessageSigningUseCase.verify(
sm.first.message,
sm.first.signatures[0],
_uiState.value.publicKey!!,
_uiState.value.primaryPublicKey!!,
sm.second
)
}
Expand All @@ -361,7 +364,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
val (blockhash, slot) = latestBlockhash.await()
val transactions = Array(numTransactions) {
transactionUseCase.create(uiState.value.publicKey!!, blockhash)
transactionUseCase.create(uiState.value.primaryPublicKey!!, blockhash)
}
client.signAndSendTransactions(transactions, slot).also {
Log.d(TAG, "Transaction signature(s): $it")
Expand Down Expand Up @@ -406,8 +409,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
_uiState.update {
it.copy(
authToken = null,
publicKey = null,
accountLabel = null,
accounts = null,
walletUriBase = null
)
}
Expand All @@ -417,8 +419,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
_uiState.update {
it.copy(
authToken = result.authToken,
publicKey = result.publicKey,
accountLabel = result.accountLabel,
accounts = result.accounts.asList(),
walletUriBase = result.walletUriBase
)
}
Expand All @@ -437,8 +438,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
_uiState.update {
it.copy(
authToken = null,
publicKey = null,
accountLabel = null,
accounts = null,
walletUriBase = null
)
}
Expand All @@ -448,8 +448,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
_uiState.update {
it.copy(
authToken = result.authToken,
publicKey = result.publicKey,
accountLabel = result.accountLabel,
accounts = result.accounts.asList(),
walletUriBase = result.walletUriBase
)
}
Expand All @@ -467,8 +466,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
_uiState.update {
it.copy(
authToken = null,
publicKey = null,
accountLabel = null,
accounts = null,
walletUriBase = null
)
}
Expand All @@ -481,7 +479,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
action: suspend (MobileWalletAdapterUseCase.Client) -> T
): T {
return try {
MobileWalletAdapterUseCase.localAssociateAndExecute(intentLauncher, uriPrefix, action)
MobileWalletAdapterUseCase.localAssociateAndExecute(intentLauncher, uriPrefix) { client, sessionProperties ->
_uiState.update {
it.copy(sessionProtocolVersion = sessionProperties.protocolVersion)
}
action(client)
}
} catch (e: MobileWalletAdapterUseCase.NoWalletAvailableException) {
showMessage(R.string.msg_no_wallet_found)
throw e
Expand All @@ -490,13 +493,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {

data class UiState(
val authToken: String? = null,
val publicKey: ByteArray? = null, // TODO(#44): support multiple addresses
val accountLabel: String? = null,
val accounts: List<AuthorizedAccount>? = null,
val walletUriBase: Uri? = null,
val messages: List<String> = emptyList(),
val txnVersion: MemoTransactionVersion = MemoTransactionVersion.Legacy
val txnVersion: MemoTransactionVersion = MemoTransactionVersion.Legacy,
val sessionProtocolVersion: ProtocolVersion? = null
) {
val hasAuthToken: Boolean get() = (authToken != null)
val primaryPublicKey: ByteArray? get() = (accounts?.first()?.publicKey)

override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand All @@ -505,23 +509,24 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
other as UiState

if (authToken != other.authToken) return false
if (publicKey != null) {
if (other.publicKey == null) return false
if (!publicKey.contentEquals(other.publicKey)) return false
} else if (other.publicKey != null) return false
if (accounts != null && accounts.size == other.accounts?.size) {
accounts.zip(other.accounts).all { (a1, a2) -> a1.publicKey.contentEquals(a2.publicKey) }
} else if (other.accounts != null) return false
if (walletUriBase != other.walletUriBase) return false
if (messages != other.messages) return false
if (txnVersion != other.txnVersion) return false
if (sessionProtocolVersion != other.sessionProtocolVersion) return false

return true
}

override fun hashCode(): Int {
var result = authToken?.hashCode() ?: 0
result = 31 * result + (publicKey?.contentHashCode() ?: 0)
result = 31 * result + (accounts?.hashCode() ?: 0)
result = 31 * result + (walletUriBase?.hashCode() ?: 0)
result = 31 * result + messages.hashCode()
result = 31 * result + txnVersion.hashCode()
result = 31 * result + (sessionProtocolVersion?.hashCode() ?: 0)
return result
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.solana.mobilewalletadapter.clientlib.scenario.LocalAssociationIntentC
import com.solana.mobilewalletadapter.clientlib.scenario.LocalAssociationScenario
import com.solana.mobilewalletadapter.clientlib.scenario.Scenario
import com.solana.mobilewalletadapter.common.ProtocolContract
import com.solana.mobilewalletadapter.common.protocol.SessionProperties
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
Expand Down Expand Up @@ -317,13 +318,13 @@ object MobileWalletAdapterUseCase {
suspend fun <T> localAssociateAndExecute(
intentLauncher: ActivityResultLauncher<StartMobileWalletAdapterActivity.CreateParams>,
uriPrefix: Uri? = null,
action: suspend (Client) -> T
action: suspend (Client, SessionProperties) -> T
): T = localAssociateAndExecuteAsync(intentLauncher, uriPrefix, action).await()

suspend fun <T> localAssociateAndExecuteAsync(
intentLauncher: ActivityResultLauncher<StartMobileWalletAdapterActivity.CreateParams>,
uriPrefix: Uri? = null,
action: suspend (Client) -> T
action: suspend (Client, SessionProperties) -> T
): Deferred<T> = coroutineScope {
// Use async to launch in a new Job, for proper cancellation semantics
async {
Expand Down Expand Up @@ -373,7 +374,8 @@ object MobileWalletAdapterUseCase {

contract.onMobileWalletAdapterClientConnected(this)

action(Client(mobileWalletAdapterClient))
action(Client(mobileWalletAdapterClient),
localAssociation.session.sessionProperties)
} finally {
@Suppress("BlockingMethodInNonBlockingContext") // running in Dispatchers.IO; blocking is appropriate
localAssociation.close()
Expand Down
41 changes: 31 additions & 10 deletions android/fakedapp/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -187,26 +187,25 @@
android:enabled="false" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_account_name"
android:id="@+id/label_accounts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toTopOf="@id/tv_account_name"
app:layout_constraintBottom_toBottomOf="@id/tv_account_name"
app:layout_constraintTop_toTopOf="@id/spinner_accounts"
app:layout_constraintBottom_toBottomOf="@id/spinner_accounts"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/label_account_name"
android:text="@string/label_accounts"
android:textAppearance="?textAppearanceBody1"
android:textStyle="bold" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_account_name"
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spinner_accounts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/label_has_auth_token"
app:layout_constraintStart_toEndOf="@id/label_account_name"
app:layout_constraintEnd_toEndOf="parent"
android:textAppearance="?textAppearanceBody1" />
app:layout_constraintStart_toEndOf="@id/label_accounts"
app:layout_constraintEnd_toEndOf="parent" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_wallet_uri_prefix"
Expand All @@ -225,7 +224,29 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/label_account_name"
app:layout_constraintTop_toBottomOf="@id/spinner_accounts"
app:layout_constraintStart_toEndOf="@id/label_wallet_uri_prefix"
app:layout_constraintEnd_toEndOf="parent"
android:textAppearance="?textAppearanceBody1" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_session_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toTopOf="@id/tv_session_version"
app:layout_constraintBottom_toBottomOf="@+id/tv_session_version"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/label_session_version"
android:textAppearance="?textAppearanceBody1"
android:textStyle="bold" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_session_version"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/label_wallet_uri_prefix"
app:layout_constraintStart_toEndOf="@id/label_wallet_uri_prefix"
app:layout_constraintEnd_toEndOf="parent"
android:textAppearance="?textAppearanceBody1" />
Expand Down
Loading

0 comments on commit 2037ffe

Please sign in to comment.