Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-29 13:43:28 +07:00
parent 9c68c9ff00
commit 4f499f41c7
17 changed files with 159 additions and 179 deletions

View file

@ -7,7 +7,6 @@ import arrow.core.right
import com.squareup.moshi.Moshi
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.data.walletconnect.model.CAIP2
import com.tangem.data.walletconnect.model.NamespaceKey
import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter
@ -15,7 +14,6 @@ import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter.Compani
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
import com.tangem.data.walletconnect.utils.WcNetworksConverter
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletconnect.model.*
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
@ -156,16 +154,6 @@ internal class WcEthNetwork(
val ethChainId = chainId.reference.toIntOrNull() ?: return null
return Blockchain.fromChainId(ethChainId)
}
override fun toCAIP2(network: Network): CAIP2? {
val blockchain = network.toBlockchain()
if (!blockchain.isEvm()) return null
val chainId = blockchain.getChainId() ?: return null
return CAIP2(
namespace = namespaceKey.key,
reference = chainId.toString(),
)
}
}
internal class Factories @Inject constructor(

View file

@ -41,6 +41,9 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
WcMutableFee {
private var approvalAmount: WcApprovedAmount? = null
// in case of change TransactionExtras
// change approvalAmount for example
private var isIgnoreDAppFee: Boolean = false
private var dAppFee: Fee? = null
override val securityStatus: LceFlow<Throwable, BlockAidTransactionCheck.Result> =
@ -102,7 +105,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
},
)
approvalAmount = action.amount
dAppFee = null
isIgnoreDAppFee = true
uncompiled.copy(extras = extras.copy(callData = callData))
}
is WcEthTxAction.UpdateFee -> uncompiled.copy(fee = action.fee)
@ -111,8 +114,11 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
emit(newState)
}
override fun dAppFee(): Fee? {
return dAppFee
override suspend fun dAppFee(): Fee? {
if (isIgnoreDAppFee) return null
if (dAppFee != null) return dAppFee
return ethTxHelper.getDAppFee(method.transaction, wallet, network)
.also { dAppFee = it }
}
override fun updateFee(fee: Fee) {
@ -120,9 +126,8 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
}
override fun invoke(): Flow<WcSignState<TransactionData>> = flow {
dAppFee = ethTxHelper.getDAppFee(method.transaction, wallet, network)
val transactionData = ethTxHelper.createTransactionData(
dAppFee = dAppFee,
dAppFee = dAppFee(),
network = context.network,
txParams = method.transaction,
) ?: return@flow

View file

@ -41,6 +41,9 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
WcMutableFee {
private var approvalAmount: WcApprovedAmount? = null
// in case of change TransactionExtras
// change approvalAmount for example
private var isIgnoreDAppFee: Boolean = false
private var dAppFee: Fee? = null
override val securityStatus = blockAidDelegate.getSecurityStatus(
@ -101,7 +104,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
},
)
approvalAmount = action.amount
dAppFee = null
isIgnoreDAppFee = true
uncompiled.copy(extras = extras.copy(callData = callData))
}
is WcEthTxAction.UpdateFee -> uncompiled.copy(fee = action.fee)
@ -115,17 +118,19 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
}
override fun invoke(): Flow<WcSignState<TransactionData>> = flow {
dAppFee = ethTxHelper.getDAppFee(method.transaction, wallet, network)
val transactionData = ethTxHelper.createTransactionData(
dAppFee = dAppFee,
dAppFee = dAppFee(),
network = context.network,
txParams = method.transaction,
) ?: return@flow
emitAll(delegate.invoke(transactionData))
}
override fun dAppFee(): Fee? {
return dAppFee
override suspend fun dAppFee(): Fee? {
if (isIgnoreDAppFee) return null
if (dAppFee != null) return dAppFee
return ethTxHelper.getDAppFee(method.transaction, wallet, network)
.also { dAppFee = it }
}
override fun getAmount(): WcApprovedAmount? {

View file

@ -8,7 +8,6 @@ import com.squareup.moshi.Moshi
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.extensions.decodeBase58
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.common.extensions.toHexString
import com.tangem.data.walletconnect.model.CAIP2
import com.tangem.data.walletconnect.model.NamespaceKey
@ -17,7 +16,6 @@ import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter.Compani
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
import com.tangem.data.walletconnect.utils.WcNetworksConverter
import com.tangem.domain.models.network.Network
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethodName
@ -88,27 +86,14 @@ internal class WcSolanaNetwork(
override val namespaceKey: NamespaceKey = NamespaceKey("solana")
override fun toBlockchain(chainId: CAIP2): Blockchain? {
val isMainNet = MAINNET_CHAIN_ID.any { it.lowercase() == chainId.reference.lowercase() }
if (chainId.namespace != namespaceKey.key) return null
return when (chainId.reference) {
MAINNET_CHAIN_ID -> Blockchain.Solana
TESTNET_CHAIN_ID -> Blockchain.SolanaTestnet
return when {
isMainNet -> Blockchain.Solana
chainId.reference.lowercase() == TESTNET_CHAIN_ID -> Blockchain.SolanaTestnet
else -> null
}
}
override fun toCAIP2(network: Network): CAIP2? {
val blockchain = network.toBlockchain()
val chainId = when (blockchain) {
Blockchain.Solana -> MAINNET_CHAIN_ID
Blockchain.SolanaTestnet -> TESTNET_CHAIN_ID
else -> null
}
chainId ?: return null
return CAIP2(
namespace = namespaceKey.key,
reference = chainId,
)
}
}
private fun WcSolanaMethodName.toMethod(request: WcSdkSessionRequest): Either<Throwable, WcSolanaMethod?> {
@ -140,7 +125,7 @@ internal class WcSolanaNetwork(
)
companion object {
private const val MAINNET_CHAIN_ID = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
private val MAINNET_CHAIN_ID = listOf("5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ")
private const val TESTNET_CHAIN_ID = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z"
}
}

View file

@ -110,8 +110,10 @@ internal class AssociateNetworksDelegate(
.distinctBy { it.rawId }
}
private fun Map<String, Namespace.Proposal>.setOfChainId(): Set<String> =
this.values.flatMap { proposal -> proposal.chains ?: listOf() }.toSet()
private fun missingNetworkName(chainId: String): String = chainId.replaceFirstChar(Char::titlecase)
companion object {
internal fun Map<String, Namespace.Proposal>.setOfChainId(): Set<String> =
this.values.flatMap { proposal -> proposal.chains ?: listOf() }.toSet()
}
}

View file

@ -2,6 +2,8 @@ package com.tangem.data.walletconnect.pair
import com.reown.walletkit.client.Wallet
import com.tangem.data.walletconnect.model.CAIP10
import com.tangem.data.walletconnect.model.CAIP2
import com.tangem.data.walletconnect.pair.AssociateNetworksDelegate.Companion.setOfChainId
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
import com.tangem.data.walletconnect.utils.WcNetworksConverter
import com.tangem.domain.models.network.Network
@ -22,6 +24,26 @@ internal class CaipNamespaceDelegate(
val userWallet = sessionForApprove.wallet
val result = mutableMapOf<String, Session>()
val requiredNamespaces = sessionProposal.requiredNamespaces.setOfChainId()
val optionalNamespaces = sessionProposal.optionalNamespaces.setOfChainId()
val allWcNetworks = (requiredNamespaces + optionalNamespaces)
.mapNotNull { chainId ->
val network = namespaceConverters
.firstNotNullOfOrNull { it.toNetwork(chainId, userWallet) }
?: return@mapNotNull null
val caip2 = CAIP2.fromRaw(chainId) ?: return@mapNotNull null
network to caip2
}
suspend fun createCAIP10(userWalletId: UserWalletId, network: Network): CAIP10? {
val address = walletManagersFacade.getDefaultAddress(userWalletId, network)
val chainId = allWcNetworks
.find { (wcNetwork, _) -> network.rawId == wcNetwork.rawId }
?.second
if (chainId == null || address == null) return null
return CAIP10(chainId = chainId, accountAddress = address)
}
wcNetworksConverter.convertNetworksForApprove(sessionForApprove)
.mapNotNull { createCAIP10(userWallet.walletId, it) }
.forEach { account ->
@ -52,13 +74,6 @@ internal class CaipNamespaceDelegate(
}
}
private suspend fun createCAIP10(userWalletId: UserWalletId, network: Network): CAIP10? {
val address = walletManagersFacade.getDefaultAddress(userWalletId, network)
val chainId = namespaceConverters.firstNotNullOfOrNull { it.toCAIP2(network) }
if (chainId == null || address == null) return null
return CAIP10(chainId = chainId, accountAddress = address)
}
private data class Session(
val chains: MutableSet<String> = mutableSetOf(),
val accounts: MutableSet<String> = mutableSetOf(),

View file

@ -115,6 +115,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
}.onCompletion {
if (it != null) {
Timber.tag(WC_TAG).e(it, "Completed with error $pairRequest")
emit(WcPairState.Error(WcPairError.Unknown(it.message.orEmpty())))
} else {
Timber.tag(WC_TAG).i("Completed successfully $pairRequest")
}
@ -134,7 +135,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
private suspend fun walletKitApproveSession(
sessionForApprove: WcSessionApprove,
sdkSessionProposal: Wallet.Model.SessionProposal,
): Either<WcPairError, Wallet.Model.SettledSessionResponse.Result> {
): Either<WcPairError, Wallet.Model.SettledSessionResponse.Result> = try {
val namespaces = caipNamespaceDelegate.associate(
sdkSessionProposal,
sessionForApprove,
@ -143,7 +144,10 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
proposerPublicKey = sdkSessionProposal.proposerPublicKey,
namespaces = namespaces,
)
return sdkDelegate.approve(sessionApprove)
sdkDelegate.approve(sessionApprove)
} catch (e: Throwable) {
Timber.tag(WC_TAG).e(e, "Failed to sdk approve session $pairRequest")
WcPairError.ApprovalFailed(e.message.orEmpty()).left()
}
private suspend fun buildProposalState(

View file

@ -150,7 +150,7 @@ internal class WcPairSdkDelegate : WcSdkObserver {
private fun Throwable.toApproveError() = WcPairError.ApprovalFailed(this.localizedMessage.orEmpty()).left()
companion object {
private const val CALLBACK_TIMEOUT = 30
private const val CALLBACK_TIMEOUT = 60
// com.reown.android.pairing.engine.domain.PairingEngine.pair
private val pairingExpiredMessages = listOf(
"Pairing URI expired",

View file

@ -52,7 +52,7 @@ internal class DefaultWcSessionsManager(
if (someMigrated) return@transform // ignore emit, wait next one
}
val associatedSessions: List<WcSession> = associate(inSdk, inStore, wallets)
val someRemove = removeUnknownSessions(inStore, associatedSessions)
val someRemove = removeUnknownSessions(inStore, inSdk, associatedSessions)
if (someRemove) return@transform // ignore emit, wait next one
emit(associatedSessions.groupBy { it.wallet })
}
@ -79,7 +79,7 @@ internal class DefaultWcSessionsManager(
override suspend fun removeSession(session: WcSession): Either<Throwable, Unit> {
val topic = session.sdkModel.topic
val sdkCall = sdkDisconnectSession(topic)
.onRight { onSessionDelete.trySend(Wallet.Model.SessionDelete.Success(topic = topic, reason = "")) }
onSessionDelete.trySend(Wallet.Model.SessionDelete.Success(topic = topic, reason = ""))
analytics.send(WcAnalyticEvents.SessionDisconnected(session.sdkModel.appMetaData))
return sdkCall
}
@ -142,10 +142,17 @@ internal class DefaultWcSessionsManager(
return wcSessions
}
private suspend fun removeUnknownSessions(storeSessions: Set<WcSessionDTO>, wcSessions: List<WcSession>): Boolean {
private suspend fun removeUnknownSessions(
storeSessions: Set<WcSessionDTO>,
inSdkSessions: List<Wallet.Model.Session>,
wcSessions: List<WcSession>,
): Boolean {
val unknownStoredSessions = storeSessions
.filterNot { dto -> wcSessions.any { it.sdkModel.topic == dto.topic } }
val haveSomeUnknown = unknownStoredSessions.isNotEmpty()
val unknownSdkSessions = inSdkSessions
.filterNot { sdkSession -> wcSessions.any { it.sdkModel.topic == sdkSession.topic } }
val haveSomeUnknownSdkSessions = unknownSdkSessions.isNotEmpty()
if (haveSomeUnknown) {
Timber.tag(WC_TAG).i("removeUnknownSessions $unknownStoredSessions")
@ -164,7 +171,10 @@ internal class DefaultWcSessionsManager(
store.removeSessions(emptyNetworksDto)
}
if (haveEmptySessions) {
emptyNetworkSessions.map { scope.launch { sdkDisconnectSession(it.sdkModel.topic) } }
emptyNetworkSessions.forEach { scope.launch { sdkDisconnectSession(it.sdkModel.topic) } }
}
if (haveSomeUnknownSdkSessions) {
unknownSdkSessions.forEach { scope.launch { sdkDisconnectSession(it.topic) } }
}
return haveSomeUnknown || haveEmptyDto
}

View file

@ -16,7 +16,6 @@ internal interface WcNamespaceConverter {
fun toBlockchain(chainId: CAIP2): Blockchain?
fun toBlockchain(chainId: String): Blockchain? = toCAIP2(chainId)?.let { caip2 -> toBlockchain(caip2) }
fun toCAIP2(network: Network): CAIP2?
fun toCAIP2(chainId: String): CAIP2? = CAIP2.fromRaw(chainId)
fun toNetwork(chainId: String, wallet: UserWallet): Network? {

View file

@ -34,7 +34,7 @@ interface WcListTransactionUseCase :
* [updateFee] triggered a new [TransactionData] emit
*/
interface WcMutableFee {
fun dAppFee(): Fee?
suspend fun dAppFee(): Fee?
fun updateFee(fee: Fee)
}

View file

@ -1,62 +0,0 @@
package com.tangem.features.walletconnect.transaction.converter
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.utils.converter.Converter
import javax.inject.Inject
internal class WcCommonTransactionUMConverter @Inject constructor(
private val signTypedDataUMConverter: WcSignTypedDataUMConverter,
private val signTransactionUMConverter: WcSignTransactionUMConverter,
private val sendTransactionUMConverter: WcSendTransactionUMConverter,
) : Converter<WcCommonTransactionUMConverter.Input, WcCommonTransactionUM?> {
override fun convert(value: Input): WcCommonTransactionUM? {
return when (value.useCase) {
is WcMessageSignUseCase -> {
when (value.useCase.method) {
is WcEthMethod.SignTypedData -> signTypedDataUMConverter.convert(
WcSignTypedDataUMConverter.Input(
useCase = value.useCase,
signState = value.signState,
signModel = value.signState.signModel as WcMessageSignUseCase.SignModel,
actions = value.actions,
),
)
is WcEthMethod.MessageSign, is WcSolanaMethod.SignMessage -> signTransactionUMConverter.convert(
WcSignTransactionUMConverter.Input(
useCase = value.useCase,
signState = value.signState,
signModel = value.signState.signModel as WcMessageSignUseCase.SignModel,
actions = value.actions,
),
)
else -> null
}
}
is WcTransactionUseCase -> sendTransactionUMConverter.convert(
WcSendTransactionUMConverter.Input(
useCase = value.useCase,
signState = value.signState,
actions = value.actions,
feeSelectorUM = value.feeSelectorUM,
),
)
else -> null
}
}
data class Input(
val useCase: WcSignUseCase<*>,
val signState: WcSignState<*>,
val actions: WcTransactionActionsUM,
val feeSelectorUM: FeeSelectorUM? = null,
)
}

View file

@ -2,7 +2,9 @@ package com.tangem.features.walletconnect.transaction.converter
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.usecase.method.*
import com.tangem.domain.walletconnect.usecase.method.WcMethodContext
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
@ -20,7 +22,7 @@ internal class WcSendTransactionUMConverter @Inject constructor(
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
) : Converter<WcSendTransactionUMConverter.Input, WcSendTransactionUM?> {
override fun convert(value: Input): WcSendTransactionUM? = when (value.useCase.method) {
override fun convert(value: Input): WcSendTransactionUM? = when (value.context.method) {
is WcEthMethod.SendTransaction,
is WcEthMethod.SignTransaction,
is WcSolanaMethod.SignAllTransaction,
@ -31,23 +33,23 @@ internal class WcSendTransactionUMConverter @Inject constructor(
onSend = value.actions.onSign,
appInfo = appInfoContentUMConverter.convert(
WcTransactionAppInfoContentUMConverter.Input(
session = value.useCase.session,
session = value.context.session,
onShowVerifiedAlert = value.actions.onShowVerifiedAlert,
),
),
feeState = constructFeeState(useCase = value.useCase, actions = value.actions),
walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.useCase.network),
feeState = value.feeState,
walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.context.network),
estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(),
isLoading = value.signState.domainStep == WcSignStep.Signing,
address = WcAddressConverter.convert(value.useCase.derivationState),
address = WcAddressConverter.convert(value.context.derivationState),
),
feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading,
transactionRequestInfo = WcTransactionRequestInfoUM(
blocks = buildList {
addAll(
requestBlockUMConverter.convert(
WcTransactionRequestBlockUMConverter.Input(value.useCase.rawSdkRequest),
WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest),
),
)
}.toImmutableList(),
@ -57,17 +59,9 @@ internal class WcSendTransactionUMConverter @Inject constructor(
else -> null
}
private fun constructFeeState(
useCase: WcTransactionUseCase,
actions: WcTransactionActionsUM,
): WcTransactionFeeState {
val mutableFee = useCase as? WcMutableFee ?: return WcTransactionFeeState.None
val dAppFee = mutableFee.dAppFee()
return WcTransactionFeeState.Success(dAppFee = dAppFee, onClick = actions.onShowFeeBottomSheet)
}
data class Input(
val useCase: WcTransactionUseCase,
val context: WcMethodContext,
val feeState: WcTransactionFeeState,
val signState: WcSignState<*>,
val actions: WcTransactionActionsUM,
val feeSelectorUM: FeeSelectorUM?,

View file

@ -1,6 +1,7 @@
package com.tangem.features.walletconnect.transaction.converter
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcMethodContext
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
@ -23,25 +24,25 @@ internal class WcSignTransactionUMConverter @Inject constructor(
onSign = value.actions.onSign,
appInfo = appInfoContentUMConverter.convert(
WcTransactionAppInfoContentUMConverter.Input(
session = value.useCase.session,
session = value.context.session,
onShowVerifiedAlert = value.actions.onShowVerifiedAlert,
),
),
walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.useCase.network),
walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.context.network),
isLoading = value.signState.domainStep == WcSignStep.Signing,
address = WcAddressConverter.convert(value.useCase.derivationState),
address = WcAddressConverter.convert(value.context.derivationState),
),
transactionRequestInfo = WcTransactionRequestInfoUM(
requestBlockUMConverter.convert(
WcTransactionRequestBlockUMConverter.Input(value.useCase.rawSdkRequest, value.signModel),
WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest, value.signModel),
).toImmutableList(),
onCopy = value.actions.onCopy,
),
)
data class Input(
val useCase: WcMessageSignUseCase,
val context: WcMethodContext,
val signState: WcSignState<*>,
val signModel: WcMessageSignUseCase.SignModel,
val actions: WcTransactionActionsUM,

View file

@ -1,6 +1,7 @@
package com.tangem.features.walletconnect.transaction.converter
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcMethodContext
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
@ -23,20 +24,20 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
onSign = value.actions.onSign,
appInfo = appInfoContentUMConverter.convert(
WcTransactionAppInfoContentUMConverter.Input(
session = value.useCase.session,
session = value.context.session,
onShowVerifiedAlert = value.actions.onShowVerifiedAlert,
),
),
walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.useCase.network),
address = WcAddressConverter.convert(value.useCase.derivationState),
walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.context.network),
address = WcAddressConverter.convert(value.context.derivationState),
isLoading = value.signState.domainStep == WcSignStep.Signing,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
blocks = buildList {
addAll(
requestBlockUMConverter.convert(
WcTransactionRequestBlockUMConverter.Input(value.useCase.rawSdkRequest, value.signModel),
WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest, value.signModel),
),
)
}.toImmutableList(),
@ -45,7 +46,7 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
)
data class Input(
val useCase: WcMessageSignUseCase,
val context: WcMethodContext,
val signState: WcSignState<*>,
val signModel: WcMessageSignUseCase.SignModel,
val actions: WcTransactionActionsUM,

View file

@ -36,11 +36,12 @@ import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorRelo
import com.tangem.features.send.v2.api.subcomponents.feeSelector.entity.FeeSelectorData
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
import com.tangem.features.walletconnect.transaction.converter.WcSendTransactionUMConverter
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiveBlockAidUiConverter
@ -62,7 +63,7 @@ internal class WcSendTransactionModel @Inject constructor(
private val router: Router,
private val clipboardManager: ClipboardManager,
private val useCaseFactory: WcRequestUseCaseFactory,
private val converter: WcCommonTransactionUMConverter,
private val converter: WcSendTransactionUMConverter,
private val blockAidUiConverter: WcSendAndReceiveBlockAidUiConverter,
private val getFeeUseCase: GetFeeUseCase,
private val getNetworkCoinUseCase: GetNetworkCoinStatusUseCase,
@ -198,20 +199,28 @@ internal class WcSendTransactionModel @Inject constructor(
is Lce.Error -> WcSendReceiveTransactionCheckResultsUM(isLoading = false)
is Lce.Loading -> WcSendReceiveTransactionCheckResultsUM(isLoading = true)
}
val feeState = when {
useCase is WcMutableFee ->
WcTransactionFeeState
.Success(dAppFee = useCase.dAppFee(), onClick = { onShowFeeBottomSheet() })
else -> WcTransactionFeeState.None
}
val actions = WcTransactionActionsUM(
onShowVerifiedAlert = ::showVerifiedAlert,
onDismiss = { cancel(useCase) },
onSign = { onSign(securityCheck.getOrNull()) },
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
)
var transactionUM = converter.convert(
WcCommonTransactionUMConverter.Input(
useCase = useCase,
WcSendTransactionUMConverter.Input(
context = useCase,
feeState = feeState,
signState = signState,
actions = WcTransactionActionsUM(
onShowVerifiedAlert = ::showVerifiedAlert,
onDismiss = { cancel(useCase) },
onSign = { onSign(securityCheck.getOrNull()) },
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
onShowFeeBottomSheet = ::onShowFeeBottomSheet,
),
actions = actions,
feeSelectorUM = uiState.value?.feeSelectorUM,
),
) as? WcSendTransactionUM
)
transactionUM = transactionUM?.copy(
transaction = transactionUM.transaction.copy(estimatedWalletChanges = blockAidState),
spendAllowance = blockAidState.spendAllowance,

View file

@ -11,13 +11,16 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
import com.tangem.features.walletconnect.transaction.converter.WcSignTransactionUMConverter
import com.tangem.features.walletconnect.transaction.converter.WcSignTypedDataUMConverter
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
@ -40,7 +43,8 @@ internal class WcSignTransactionModel @Inject constructor(
private val router: Router,
private val clipboardManager: ClipboardManager,
private val useCaseFactory: WcRequestUseCaseFactory,
private val converter: WcCommonTransactionUMConverter,
private val signTypedDataUMConverter: WcSignTypedDataUMConverter,
private val signTransactionUMConverter: WcSignTransactionUMConverter,
) : Model(), WcCommonTransactionModel {
private val params = paramsContainer.require<WcTransactionModelParams>()
@ -58,24 +62,44 @@ internal class WcSignTransactionModel @Inject constructor(
useCase.invoke()
.onEach { signState ->
if (signingIsDone(signState)) return@onEach
val signTransactionUM = converter.convert(
WcCommonTransactionUMConverter.Input(
useCase = useCase,
signState = signState,
actions = WcTransactionActionsUM(
onShowVerifiedAlert = ::showVerifiedAlert,
onDismiss = { cancel(useCase) },
onSign = useCase::sign,
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
),
),
) as? WcSignTransactionUM
val signTransactionUM = convertToUI(useCase, signState)
_uiState.emit(signTransactionUM)
}
.launchIn(this)
}
}
private fun convertToUI(
useCase: WcMessageSignUseCase,
signState: WcSignState<WcMessageSignUseCase.SignModel>,
): WcSignTransactionUM? {
val actions = WcTransactionActionsUM(
onShowVerifiedAlert = ::showVerifiedAlert,
onDismiss = { cancel(useCase) },
onSign = useCase::sign,
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
)
return when (useCase.method) {
is WcEthMethod.SignTypedData -> signTypedDataUMConverter.convert(
WcSignTypedDataUMConverter.Input(
context = useCase,
signState = signState,
signModel = signState.signModel,
actions = actions,
),
)
is WcEthMethod.MessageSign, is WcSolanaMethod.SignMessage -> signTransactionUMConverter.convert(
WcSignTransactionUMConverter.Input(
context = useCase,
signState = signState,
signModel = signState.signModel,
actions = actions,
),
)
else -> null
}
}
override fun dismiss() {
_uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop()
}