From edb42c1ee435e97b8548b116782bf2ca41696879 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 10 Feb 2025 11:46:32 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../com/tangem/tap/common/DialogManager.kt | 4 ++ .../walletconnect/WalletConnectSdkHelper.kt | 64 +++++++++++++++++++ .../domain/WalletConnectInteractor.kt | 15 +++-- .../walletconnect2/domain/WcJrpcMethods.kt | 13 ++++ .../domain/WcPreparedRequest.kt | 32 ++++++++-- .../domain/WcSessionRequestConverter.kt | 17 ++++- .../walletconnect/WalletConnectMiddleware.kt | 5 +- .../redux/walletconnect/WalletConnectState.kt | 6 +- .../dialogs/SignTransactionDialog.kt | 2 +- .../dialogs/SignTransactionsDialog.kt | 30 +++++++++ 11 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 104b52520e..a4d66fbf83 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 104b52520e8721eb775e2733deb361eed5067a63 +Subproject commit a4d66fbf83c88593cc398cff239cf168860ced7f diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index da4a494936..c69530d3be 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -131,6 +131,10 @@ class DialogManager : StoreSubscriber { preparedData = state.dialog.data, context = context, ) + is WalletConnectDialog.SignTransactionsDialog -> SignTransactionsDialog.create( + preparedData = state.dialog.data, + context = context, + ) is WalletConnectDialog.PairConnectErrorDialog -> SimpleAlertDialog.create( titleRes = R.string.wallet_connect_title, message = state.dialog.error.message, diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 736b070b7a..aa615456cc 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -36,6 +36,8 @@ import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionReque import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.tap.tangemSdkManager +import org.json.JSONArray +import org.json.JSONObject import timber.log.Timber import java.math.BigDecimal import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam @@ -427,6 +429,9 @@ class WalletConnectSdkHelper { } } + /** + * Returns result of signing prepared to send in WC request + */ suspend fun signTransaction( hashToSign: ByteArray, networkId: String, @@ -465,8 +470,67 @@ class WalletConnectSdkHelper { } } + /** + * Returns result of signing prepared to send in WC request + */ + suspend fun signTransactions( + hashesToSign: List, + networkId: String, + type: TransactionType, + derivationPath: String?, + cardId: String?, + ): String? { + val blockchain = Blockchain.fromNetworkId(networkId) ?: return null + val wallet = getWalletManager(blockchain, derivationPath)?.wallet ?: return null + val sdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk + + val signer = store.inject(DaggerGraphState::transactionSignerFactory).createTransactionSigner( + cardId = cardId, + sdk = sdk, + twinKey = null, + ) + + return when (val signingResult = signer.sign(hashesToSign, wallet.publicKey)) { + is CompletionResult.Failure -> { + Timber.e(signingResult.error.customMessage) + null + } + is CompletionResult.Success -> { + when (type) { + TransactionType.SOLANA_TX -> { + val result = signingResult.data.mapIndexed { index, bytes -> + byteArrayOf(1) + bytes + hashesToSign[index] + } + getSolanaResultTxHashesString(result) + } + } + } + else -> { + null + } + } + } + private fun getSolanaResultString(signedHash: ByteArray) = "{ signature: \"${signedHash.encodeBase58()}\" }" + /** + * Build json object + * { + * "transactions": [ + * "signed_tx_hash" + * ] + * } + */ + private fun getSolanaResultTxHashesString(signedHashes: List): String { + val result = JSONObject() + val transactions = JSONArray() + signedHashes.forEach { + transactions.put(it.encodeBase64()) + } + result.put("transactions", transactions) + return result.toString() + } + private fun getBnbResultString(publicKey: String, signature: String) = "{\"signature\":\"$signature\",\"publicKey\":\"$publicKey\"}" diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt index 7b646bb85f..48f90f3554 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt @@ -315,7 +315,7 @@ class WalletConnectInteractor( if (currentRequest == null || request.topic != currentRequest.topic) return val networkId = blockchainHelper.chainIdToNetworkIdOrNull(currentRequest.chainId.orEmpty()) ?: return - val signedHash = when (request) { + val signingResultData = when (request) { is WcPreparedRequest.BnbTransaction -> sdkHelper.signBnbTransaction( data = request.preparedRequestData.data.data, networkId = networkId, @@ -333,13 +333,20 @@ class WalletConnectInteractor( derivationPath = request.derivationPath, cardId = cardId, ) - is WcPreparedRequest.SignTransaction -> sdkHelper.signTransaction( + is WcPreparedRequest.SolanaSignTransaction -> sdkHelper.signTransaction( hashToSign = request.preparedRequestData.hashToSign, networkId = networkId, type = request.preparedRequestData.type, derivationPath = request.derivationPath, cardId = cardId, ) + is WcPreparedRequest.SolanaSignMultipleTransactions -> sdkHelper.signTransactions( + hashesToSign = request.preparedRequestData.hashesToSign, + networkId = networkId, + type = request.preparedRequestData.type, + derivationPath = request.derivationPath, + cardId = cardId, + ) } val requestData = RequestData( @@ -349,12 +356,12 @@ class WalletConnectInteractor( method = currentRequest.method, ) - if (signedHash == null) { + if (signingResultData == null) { walletConnectRepository.rejectRequest(requestData, WalletConnectError.SigningError) } else { walletConnectRepository.sendRequest( requestData = requestData, - result = signedHash, + result = signingResultData, ) } } diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt index f405179561..3a79b205ed 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt @@ -28,6 +28,7 @@ internal enum class WcJrpcMethods(val code: String) { SOLANA_SIGN_TX("solana_signTransaction"), SOLANA_SIGN_MESSAGE("solana_signMessage"), + SOLANA_SIGN_ALL_TX("solana_signAllTransactions"), ; companion object { @@ -130,6 +131,12 @@ data class WcEthereumTransaction( val data: String, ) : WcRequestData +@JsonClass(generateAdapter = true) +data class SolanaTransactionsRequest( + @Json(name = "transactions") + val transactions: List, +) : WcRequestData + interface WcRequestData data class WcCustomRequestData(val data: String) : WcRequestData @@ -146,6 +153,7 @@ sealed class WcRequest(open val data: WcRequestData) { data class AddChain(override val data: WcAddChain) : WcRequest(data) data class CustomRequest(override val data: WcCustomRequestData) : WcRequest(data) data class SolanaSignRequest(override val data: SolanaTransactionRequest) : WcRequest(data) + data class SolanaSignTransactions(override val data: SolanaTransactionsRequest) : WcRequest(data) } @Singleton @@ -242,6 +250,11 @@ internal class WcJrpcRequestsDeserializer @Inject constructor(@SdkMoshi private WcRequest.EthSign(data = data) } + WcJrpcMethods.SOLANA_SIGN_ALL_TX -> { + val transactionsToSign = moshi.adapter(SolanaTransactionsRequest::class.java) + .fromJsonOrNull(params) ?: return customRequest + WcRequest.SolanaSignTransactions(transactionsToSign) + } } } diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt index 64798e2d9c..ba9f21b2d6 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt @@ -31,19 +31,37 @@ sealed class WcPreparedRequest( derivationPath: String?, ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) - class SignTransaction( - override val preparedRequestData: WcGenericTransactionData, + class SolanaSignTransaction( + override val preparedRequestData: GenericTransactionData.SingleHash, + topic: String, + requestId: Long, + derivationPath: String?, + ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) + + class SolanaSignMultipleTransactions( + override val preparedRequestData: GenericTransactionData.MultipleHashes, topic: String, requestId: Long, derivationPath: String?, ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) } -data class WcGenericTransactionData( - val hashToSign: ByteArray, - val dAppName: String, - val type: TransactionType, -) +sealed interface GenericTransactionData { + val dAppName: String + val type: TransactionType + + data class SingleHash( + val hashToSign: ByteArray, + override val dAppName: String, + override val type: TransactionType, + ) : GenericTransactionData + + data class MultipleHashes( + val hashesToSign: List, + override val dAppName: String, + override val type: TransactionType, + ) : GenericTransactionData +} enum class TransactionType { SOLANA_TX, diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt index 04bab1fa69..909120438b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt @@ -117,8 +117,8 @@ internal class WcSessionRequestConverter( is WcRequest.SolanaSignRequest -> { val transaction = request.data.transaction - WcPreparedRequest.SignTransaction( - preparedRequestData = WcGenericTransactionData( + WcPreparedRequest.SolanaSignTransaction( + preparedRequestData = GenericTransactionData.SingleHash( hashToSign = transaction.prepareSolanaTransaction(), dAppName = sessionRequest.metaName, type = TransactionType.SOLANA_TX, @@ -128,6 +128,19 @@ internal class WcSessionRequestConverter( derivationPath = derivationPath, ) } + is WcRequest.SolanaSignTransactions -> { + val transactions = request.data.transactions + WcPreparedRequest.SolanaSignMultipleTransactions( + preparedRequestData = GenericTransactionData.MultipleHashes( + hashesToSign = transactions.map { it.prepareSolanaTransaction() }, // + dAppName = sessionRequest.metaName, + type = TransactionType.SOLANA_TX, + ), + topic = sessionRequest.topic, + requestId = sessionRequest.id, + derivationPath = derivationPath, + ) + } else -> throw WalletConnectError.UnsupportedMethod } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index d4d46d362c..4e7c5c7be2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -171,7 +171,10 @@ class WalletConnectMiddleware { is WcPreparedRequest.BnbTransaction -> WalletConnectDialog.BnbTransactionDialog(request) is WcPreparedRequest.EthTransaction -> WalletConnectDialog.RequestTransaction(request) is WcPreparedRequest.EthSign -> WalletConnectDialog.PersonalSign(request) - is WcPreparedRequest.SignTransaction -> WalletConnectDialog.SignTransactionDialog(request) + is WcPreparedRequest.SolanaSignTransaction -> + WalletConnectDialog.SignTransactionDialog(request) + is WcPreparedRequest.SolanaSignMultipleTransactions -> + WalletConnectDialog.SignTransactionsDialog(request) } store.dispatch(GlobalAction.ShowDialog(dialog)) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index d557aa6910..9eb02ddd35 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -116,7 +116,11 @@ sealed class WalletConnectDialog : StateDialog { ) : WalletConnectDialog() data class SignTransactionDialog( - val data: WcPreparedRequest.SignTransaction, + val data: WcPreparedRequest.SolanaSignTransaction, + ) : WalletConnectDialog() + + data class SignTransactionsDialog( + val data: WcPreparedRequest.SolanaSignMultipleTransactions, ) : WalletConnectDialog() data class PairConnectErrorDialog(val error: Throwable) : WalletConnectDialog() diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt index d985adc26b..e459b6065c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt @@ -10,7 +10,7 @@ import com.tangem.wallet.R internal object SignTransactionDialog { - fun create(preparedData: WcPreparedRequest.SignTransaction, context: Context): AlertDialog { + fun create(preparedData: WcPreparedRequest.SolanaSignTransaction, context: Context): AlertDialog { val signMessage = context.getString(R.string.wallet_connect_alert_sign_message, "") val message = "${preparedData.preparedRequestData.dAppName}\n$signMessage" return AlertDialog.Builder(context).apply { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt new file mode 100644 index 0000000000..579940d0ce --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.store +import com.tangem.wallet.R + +internal object SignTransactionsDialog { + + fun create(preparedData: WcPreparedRequest.SolanaSignMultipleTransactions, context: Context): AlertDialog { + val signMessage = context.getString(R.string.wallet_connect_alert_sign_message, "") + val message = "${preparedData.preparedRequestData.dAppName}\n$signMessage" + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.wallet_connect_title)) + setMessage(message) + setPositiveButton(context.getText(R.string.common_sign)) { _, _ -> + store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData)) + } + setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> + store.dispatch(WalletConnectAction.RejectRequest(preparedData.topic, preparedData.requestId)) + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } +} \ No newline at end of file