Updated on 2026-08-14
This commit is contained in:
parent
af2b62dbe3
commit
edb42c1ee4
11 changed files with 173 additions and 17 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 104b52520e8721eb775e2733deb361eed5067a63
|
||||
Subproject commit a4d66fbf83c88593cc398cff239cf168860ced7f
|
||||
|
|
@ -131,6 +131,10 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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<ByteArray>,
|
||||
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<ByteArray>): 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\"}"
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
) : 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ByteArray>,
|
||||
override val dAppName: String,
|
||||
override val type: TransactionType,
|
||||
) : GenericTransactionData
|
||||
}
|
||||
|
||||
enum class TransactionType {
|
||||
SOLANA_TX,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue