Skip to content

Commit

Permalink
Show full details of the WalletConnect transaction to sign (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
Radiokot authored Mar 26, 2024
1 parent 0a58782 commit 60131a5
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added
- Setting up and updating validator pool commission rates
- Support of WalletConnect CCD transfer requests
- Ability to see full details of the WalletConnect transaction to sign
- Support for WalletConnect verifiable presentation requests (for identity proofs)

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ enum class TransactionType {
@SerializedName("initContract", alternate = ["InitContract"])
INIT_CONTRACT,

@SerializedName("deployModule")
DEPLOY_MODULE,

// This has been added to have a default value
@SerializedName("unknown")
UNKNOWN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,23 @@ class AccountTransactionParamsDeserializer : JsonDeserializer<AccountTransaction
}
val schemaValueBytes = schema.value.data.map { it.toByte() }.toByteArray()
val schemaValue = Base64.encodeToString(schemaValueBytes, Base64.NO_WRAP)
return Schema.ValueSchema(type = schemaType, value = schemaValue)
return Schema.ValueSchema(
type = schemaType,
value = schemaValue,
version = schema.version,
)
} catch (ex: JsonParseException) {
return null
}
}
}

return try {
Schema.ValueSchema(type = "module", value = schemaElement.asString)
Schema.ValueSchema(
type = "module",
value = schemaElement.asString,
version = null,
)
} catch (ex: ClassCastException) {
null
} catch (ex: IllegalStateException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package com.concordium.wallet.data.walletconnect

import java.io.Serializable

sealed class Schema: Serializable {
sealed interface Schema : Serializable {
/**
* The version is present for "module" type schema.
*/
val version: Int?

data class ValueSchema(
val type: String?,
val value: String?
) : Schema()
val value: String?,
override val version: Int?,
) : Schema

/**
* In some cases the buffer object in Java Script has been serialized directly, instead of first
Expand All @@ -15,8 +21,9 @@ sealed class Schema: Serializable {
*/
data class BrokenSchema(
val type: String?,
val value: BrokenValue
) : Schema() {
val value: BrokenValue,
override val version: Int?,
) : Schema {
data class BrokenValue(val type: String?, val data: List<Int>): Serializable
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.concordium.wallet.ui.walletconnect

import android.graphics.Typeface
import android.util.TypedValue
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isVisible
Expand Down Expand Up @@ -29,6 +33,7 @@ import com.concordium.wallet.ui.base.BaseActivity
import com.concordium.wallet.ui.common.delegates.AuthDelegate
import com.concordium.wallet.uicore.view.ThemedCircularProgressDrawable
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator
import java.math.BigInteger

Expand Down Expand Up @@ -113,6 +118,7 @@ class WalletConnectView(
receiver = state.receiver,
amount = state.amount,
estimatedFee = state.estimatedFee,
canShowDetails = state.canShowDetails,
isEnoughFunds = state.isEnoughFunds,
account = state.account,
appMetadata = state.appMetadata,
Expand Down Expand Up @@ -195,6 +201,24 @@ class WalletConnectView(

Toast.makeText(activity, errorRes, Toast.LENGTH_SHORT).show()
}

is WalletConnectViewModel.Event.ShowCallDetailsDialog -> {
MaterialAlertDialogBuilder(activity)
.setTitle(event.method)
.setMessage(event.prettyPrintDetails)
.setPositiveButton(R.string.dialog_ok, null)
.show()
.apply {
val messageTextView = findViewById<View>(android.R.id.message) as? TextView
?: return@apply

with(messageTextView) {
typeface = Typeface.MONOSPACE
setTextIsSelectable(true)
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
}
}
}
}
}

Expand Down Expand Up @@ -279,6 +303,7 @@ class WalletConnectView(
receiver: String,
amount: BigInteger,
estimatedFee: BigInteger,
canShowDetails: Boolean,
isEnoughFunds: Boolean,
account: Account,
appMetadata: WalletConnectViewModel.AppMetadata,
Expand All @@ -291,6 +316,7 @@ class WalletConnectView(
receiver = receiver,
amount = amount,
estimatedFee = estimatedFee,
canShowDetails = canShowDetails,
isEnoughFunds = isEnoughFunds,
account = account,
appMetadata = appMetadata,
Expand All @@ -305,6 +331,7 @@ class WalletConnectView(
receiver: String,
amount: BigInteger,
estimatedFee: BigInteger,
canShowDetails: Boolean,
isEnoughFunds: Boolean,
account: Account,
appMetadata: WalletConnectViewModel.AppMetadata,
Expand Down Expand Up @@ -347,6 +374,15 @@ class WalletConnectView(
null
}

with(showDetailsButton) {
isVisible = canShowDetails
if (canShowDetails) {
setOnClickListener {
viewModel.onShowTransactionRequestDetailsClicked()
}
}
}

declineButton.setOnClickListener {
viewModel.rejectSessionRequest()
}
Expand Down Expand Up @@ -517,6 +553,7 @@ class WalletConnectView(
view.nextButton.visibility = GONE
return@with
}

else -> {
view.unprovableStatement.visibility = GONE
}
Expand All @@ -529,7 +566,7 @@ class WalletConnectView(
}
this.proofView.adapter = adapter
this.proofView.setCurrentItem(currentStatement, false)
this.proofView.registerOnPageChangeCallback(object: OnPageChangeCallback() {
this.proofView.registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position == statements.size - 1) {
Expand All @@ -540,7 +577,7 @@ class WalletConnectView(
})

// Connect tab dots to the proofView pager
TabLayoutMediator(pagerDots,this.proofView) { _, _ -> }.attach()
TabLayoutMediator(pagerDots, this.proofView) { _, _ -> }.attach()
}

private fun showConnecting() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.concordium.wallet.data.backend.repository.ProxyRepository
import com.concordium.wallet.data.cryptolib.AttributeRandomness
import com.concordium.wallet.data.cryptolib.CreateAccountTransactionInput
import com.concordium.wallet.data.cryptolib.CreateTransferOutput
import com.concordium.wallet.data.cryptolib.ParameterToJsonInput
import com.concordium.wallet.data.cryptolib.SignMessageInput
import com.concordium.wallet.data.cryptolib.StorageAccountData
import com.concordium.wallet.data.model.AccountData
Expand All @@ -52,12 +53,14 @@ import com.concordium.wallet.ui.walletconnect.delegate.LoggingWalletConnectCoreD
import com.concordium.wallet.ui.walletconnect.delegate.LoggingWalletConnectWalletDelegate
import com.concordium.wallet.util.DateTimeUtil
import com.concordium.wallet.util.Log
import com.concordium.wallet.util.PrettyPrint.prettyPrint
import com.concordium.wallet.util.toBigInteger
import com.google.gson.Gson
import com.walletconnect.android.Core
import com.walletconnect.android.CoreClient
import com.walletconnect.sign.client.Sign
import com.walletconnect.sign.client.SignClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -1016,6 +1019,7 @@ private constructor(
amount = accountTransactionPayload.amount,
estimatedFee = sessionRequestTransactionCost,
account = sessionRequestAccount,
canShowDetails = false,
isEnoughFunds = accountTransactionPayload.amount + transactionCost <= accountAtDisposalBalance,
appMetadata = sessionRequestAppMetadata,
)
Expand All @@ -1027,6 +1031,7 @@ private constructor(
amount = accountTransactionPayload.amount,
estimatedFee = sessionRequestTransactionCost,
account = sessionRequestAccount,
canShowDetails = true,
isEnoughFunds = accountTransactionPayload.amount + transactionCost <= accountAtDisposalBalance,
appMetadata = sessionRequestAppMetadata,
)
Expand Down Expand Up @@ -1426,6 +1431,61 @@ private constructor(
)
}

fun onShowTransactionRequestDetailsClicked() {
val reviewState = state as? State.SessionRequestReview.TransactionRequestReview
check(reviewState != null && reviewState.canShowDetails) {
"Show details button can only be clicked in the transaction request review state " +
"allowing showing the details"
}

val context = getApplication<Application>()

viewModelScope.launch(Dispatchers.IO) {
val accountTransactionPayload = sessionRequestAccountTransactionPayload
val accountTransactionParamsSchema = sessionRequestAccountTransactionParams.schema

if (accountTransactionPayload is AccountTransactionPayload.Update) {
val prettyPrintParams =
if (accountTransactionParamsSchema != null)
App.appCore.cryptoLibrary
.parameterToJson(
ParameterToJsonInput(
parameter = accountTransactionPayload.message,
receiveName = accountTransactionPayload.receiveName,
schema = accountTransactionParamsSchema,
schemaVersion = accountTransactionParamsSchema.version,
)
)
?.prettyPrint()
?: context.getString(R.string.wallet_connect_error_transaction_request_stringify_params_failed)
else if (accountTransactionPayload.message == "")
context.getString(R.string.wallet_connect_transaction_request_no_parameters)
else
context.getString(R.string.wallet_connect_error_transaction_request_stringify_params_no_schema)

mutableEventsFlow.emit(
Event.ShowCallDetailsDialog(
method = reviewState.method,
prettyPrintDetails = context.getString(
R.string.wallet_connect_template_transaction_request_details,
accountTransactionPayload.maxEnergy.toString(),
prettyPrintParams,
),
)
)
} else {
Log.w("Nothing to show as details for ${accountTransactionPayload::class.simpleName}")

mutableEventsFlow.emit(
Event.ShowCallDetailsDialog(
method = reviewState.method,
prettyPrintDetails = context.getString(R.string.wallet_connect_transaction_request_no_details),
)
)
}
}
}

fun onDialogCancelled() {
when (state) {
State.Idle -> {
Expand Down Expand Up @@ -1642,6 +1702,7 @@ private constructor(
val receiver: String,
val amount: BigInteger,
val estimatedFee: BigInteger,
val canShowDetails: Boolean,
val isEnoughFunds: Boolean,
account: Account,
appMetadata: AppMetadata,
Expand Down Expand Up @@ -1764,6 +1825,15 @@ private constructor(
class ShowFloatingError(
val error: Error,
) : Event

/**
* A dialog showing pretty print contract call details must be shown.
* The details printout may be quite long.
*/
class ShowCallDetailsDialog(
val method: String,
val prettyPrintDetails: String,
) : Event
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
android:id="@+id/amounts_layout"
style="@style/CryptoX_Container_Stroked"
android:layout_marginTop="8dp"
android:paddingVertical="20dp"
android:paddingTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/transaction_data_layout">

Expand Down Expand Up @@ -135,6 +135,23 @@
app:layout_constraintTop_toTopOf="@id/fee_title_text_view"
tools:text="1,2455 CCD" />

<Button
android:id="@+id/show_details_button"
style="@style/CryptoX_Button_Text_Small"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="8dp"
android:text="@string/wallet_connect_transaction_request_show_details"
app:drawableEndCompat="@drawable/ccx_ico_arrow_up_right"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fee_title_text_view" />

<Space
android:layout_width="0dp"
android:layout_height="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/show_details_button"
app:layout_goneMarginTop="8dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

<TextView
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,12 @@
<string name="wallet_connect_session_request_decline">@string/decline</string>
<string name="wallet_connect_transaction_request_ccd_amount">CCD amount</string>
<string name="wallet_connect_transaction_request_approximate_fee">@string/approximate_fee</string>
<string name="wallet_connect_transaction_request_show_details">Show details</string>
<string name="wallet_connect_error_transaction_request_stringify_params_no_schema">Can\'t stringify the parameters as there is no schema</string>
<string name="wallet_connect_error_transaction_request_stringify_params_failed">Failed to stringify the parameters</string>
<string name="wallet_connect_template_transaction_request_details">Max energy allowed:\n%1$s\n\nParameters:\n%2$s</string>
<string name="wallet_connect_transaction_request_no_parameters">The call has no parameters</string>
<string name="wallet_connect_transaction_request_no_details">There are no extra details</string>
<string name="wallet_connect_transaction_request_insufficient_funds">Insufficient funds to sign the transaction</string>
<string name="wallet_connect_transaction_submitted_finish">@string/finish</string>
<string name="wallet_connect_transaction_submitted_total_amount">@string/total_amount</string>
Expand Down

0 comments on commit 60131a5

Please sign in to comment.