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

Expose MWA 2.0 stuff in Fakedapp #583

Merged
merged 5 commits into from
Oct 24, 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
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