Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-16 21:31:23 +04:00
parent a3295ea181
commit aa52b659ce
7 changed files with 79 additions and 29 deletions

View file

@ -48,19 +48,30 @@ class WalletConnectSdkHelper {
}
@Suppress("MagicNumber")
suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData? {
suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData {
val transaction = data.transaction
val blockchain = Blockchain.fromNetworkId(data.networkId) ?: return null
val walletManager = getWalletManager(blockchain, data.rawDerivationPath) ?: return null
val blockchain = requireNotNull(Blockchain.fromNetworkId(data.networkId)) {
"Blockchain not found"
}
val walletManager = requireNotNull(getWalletManager(blockchain, data.rawDerivationPath)) {
"WalletManager not found"
}
walletManager.safeUpdate(isDemoCard())
val wallet = walletManager.wallet
val balance = wallet.amounts[AmountType.Coin]?.value ?: return null
val balance = requireNotNull(wallet.amounts[AmountType.Coin]?.value) {
"Coin balance not found"
}
val decimals = wallet.blockchain.decimals()
val value = (transaction.value ?: "0").hexToBigDecimal()
.movePointLeft(decimals) ?: return null
val value = (transaction.value ?: "0")
.hexToBigDecimal()
.movePointLeft(decimals)
requireNotNull(value) {
"Transaction amount is null"
}
val gasLimit = getGasLimitFromTx(value, walletManager, transaction)
@ -69,20 +80,23 @@ class WalletConnectSdkHelper {
is Result.Success -> result.data.toBigDecimal()
is Result.Failure -> {
(result.error as? Throwable)?.let { Timber.e(it, "getGasPrice failed") }
return null
error("Unable to get gas price: ${result.error}")
}
null -> return null
null -> error("Gas price is null")
}
val fee = (gasLimit * gasPrice).movePointLeft(decimals)
val total = value + fee
val destinationAddress = requireNotNull(transaction.to) { "Destination address is null" }
val transactionData = TransactionData(
amount = Amount(value, wallet.blockchain),
// TODO refactoring
fee = Fee.Common(Amount(fee, wallet.blockchain)),
sourceAddress = transaction.from,
destinationAddress = transaction.to!!,
destinationAddress = destinationAddress,
extras = EthereumTransactionExtras(
data = transaction.data.removePrefix(HEX_PREFIX).hexToBytes(),
gasLimit = gasLimit.toBigInteger(),
@ -102,6 +116,7 @@ class WalletConnectSdkHelper {
id = data.id,
type = data.type,
)
return WcTransactionData(
type = data.type,
transaction = transactionData,

View file

@ -374,6 +374,7 @@ internal class DefaultWalletConnectRepository(
override fun rejectRequest(requestData: RequestData, error: WalletConnectError) {
val session = currentSessions.find { it.topic == requestData.topic }
analyticsHandler.send(
WalletConnect.RequestHandled(
WalletConnect.RequestHandledParams(
@ -385,17 +386,18 @@ internal class DefaultWalletConnectRepository(
),
),
)
cancelRequest(requestData.topic, requestData.requestId)
cancelRequest(requestData.topic, requestData.requestId, error.error)
}
override fun cancelRequest(topic: String, id: Long) {
override fun cancelRequest(topic: String, id: Long, message: String) {
Web3Wallet.respondSessionRequest(
params = Wallet.Params.SessionRequestResponse(
sessionTopic = topic,
jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcError(
id = id,
code = 0,
message = "",
message = message,
),
),
onSuccess = {},
@ -406,8 +408,8 @@ internal class DefaultWalletConnectRepository(
override fun reject() {
Web3Wallet.rejectSession(
params = Wallet.Params.SessionReject(
sessionProposal?.proposerPublicKey ?: "",
"",
proposerPublicKey = sessionProposal?.proposerPublicKey ?: "",
reason = "",
),
onSuccess = {
Timber.d("Rejected successfully: $it")

View file

@ -260,10 +260,18 @@ class WalletConnectInteractor(
)
else -> {
currentRequest = sessionRequest
val data = prepareRequestData(sessionRequest)
if (data != null) {
handler.onSessionRequest(data)
val data = prepareRequestData(sessionRequest).getOrElse { e ->
val wrappedError = e as? WalletConnectError ?: WalletConnectError.UnknownError(
message = e.localizedMessage ?: "Unknown error",
)
walletConnectRepository.rejectRequest(requestData, wrappedError)
handler.onSessionRejected(wrappedError)
return
}
handler.onSessionRequest(data)
}
}
}
@ -353,7 +361,9 @@ class WalletConnectInteractor(
}
}
private suspend fun prepareRequestData(sessionRequest: WalletConnectEvents.SessionRequest): WcPreparedRequest? {
private suspend fun prepareRequestData(
sessionRequest: WalletConnectEvents.SessionRequest,
): Result<WcPreparedRequest> {
return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId)
}

View file

@ -27,5 +27,5 @@ interface WalletConnectRepository {
fun rejectRequest(requestData: RequestData, error: WalletConnectError)
fun cancelRequest(topic: String, id: Long)
fun cancelRequest(topic: String, id: Long, message: String = "")
}

View file

@ -4,6 +4,7 @@ import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
import com.tangem.tap.domain.walletconnect2.domain.mapper.mapToTransaction
import com.tangem.tap.domain.walletconnect2.domain.models.BnbData
import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents
import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType
@ -17,15 +18,18 @@ internal class WcSessionRequestConverter(
suspend fun prepareRequest(
sessionRequest: WalletConnectEvents.SessionRequest,
userWalletId: String,
): WcPreparedRequest? {
val networkId = blockchainHelper.chainIdToNetworkIdOrNull(sessionRequest.chainId ?: "") ?: return null
): Result<WcPreparedRequest> = runCatching {
val networkId = requireNotNull(blockchainHelper.chainIdToNetworkIdOrNull(sessionRequest.chainId.orEmpty())) {
"Failed to get network ID for chain ID: ${sessionRequest.chainId}"
}
val derivationPath = getDerivationPath(
sessionsRepository = sessionsRepository,
sessionRequest = sessionRequest,
userWalletId = userWalletId,
walletAddress = getWalletAddress(sessionRequest.request),
)
return when (val request = sessionRequest.request) {
when (val request = sessionRequest.request) {
is WcRequest.EthSendTransaction -> {
val data = sdkHelper.prepareTransactionData(
EthTransactionData(
@ -38,7 +42,8 @@ internal class WcSessionRequestConverter(
metaName = sessionRequest.metaName,
metaUrl = sessionRequest.metaUrl,
),
) ?: return null
)
WcPreparedRequest.EthTransaction(
preparedRequestData = data,
topic = sessionRequest.topic,
@ -58,7 +63,8 @@ internal class WcSessionRequestConverter(
metaName = sessionRequest.metaName,
metaUrl = sessionRequest.metaUrl,
),
) ?: return null
)
WcPreparedRequest.EthTransaction(
preparedRequestData = data,
topic = sessionRequest.topic,
@ -122,7 +128,7 @@ internal class WcSessionRequestConverter(
derivationPath = derivationPath,
)
}
else -> null
else -> throw WalletConnectError.UnsupportedMethod
}
}

View file

@ -20,8 +20,12 @@ sealed class WalletConnectError(val error: String) : Exception() {
override val message: String?,
) : WalletConnectError("ExternalApprovalError")
object WrongUserWallet : WalletConnectError("WrongUserWallet")
object UnsupportedMethod : WalletConnectError("UnsupportedMethod")
object SigningError : WalletConnectError("SigningError")
object ValidationError : WalletConnectError("ValidationError")
data class UnknownError(
override val message: String,
) : WalletConnectError(message)
data object WrongUserWallet : WalletConnectError("WrongUserWallet")
data object UnsupportedMethod : WalletConnectError("UnsupportedMethod")
data object SigningError : WalletConnectError("SigningError")
data object ValidationError : WalletConnectError("ValidationError")
}

View file

@ -9,6 +9,7 @@ import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
@ -20,6 +21,7 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
@ -135,6 +137,17 @@ class WalletConnectMiddleware {
),
)
}
is WalletConnectError.UnknownError -> {
store.dispatchOnMain(
GlobalAction.ShowDialog(
AppDialog.SimpleOkDialogRes(
headerId = R.string.wallet_connect_title,
messageId = R.string.wallet_connect_error_with_framework_message,
args = listOf(action.error.message),
),
),
)
}
is WalletConnectError.ExternalApprovalError -> {
Timber.e(action.error, "ExternalApprovalError ${action.error.message}")
// do not show dialog on this event