Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-24 14:06:24 +05:00
parent 5c5c31dfc0
commit 3e4e7d8da0
17 changed files with 120 additions and 34 deletions

View file

@ -9,6 +9,7 @@ import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcAddNetworkUseCase
import com.tangem.domain.walletconnect.usecase.method.WcNetworkDerivationState
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -25,8 +26,10 @@ internal class WcEthAddNetworkUseCase @AssistedInject constructor(
get() = context.rawSdkRequest
override val network: Network
get() = context.network
override val walletAddress: String
get() = context.accountAddress
override val derivationState: WcNetworkDerivationState = when {
context.networkDerivationsCount > 1 -> WcNetworkDerivationState.Multiple(walletAddress = context.accountAddress)
else -> WcNetworkDerivationState.Single
}
override suspend fun approve(): Either<WcRequestError, String> {
return respondService.respond(rawSdkRequest, "")

View file

@ -75,6 +75,7 @@ internal class WcEthNetwork(
rawSdkRequest = request,
network = walletNetwork,
accountAddress = accountAddress,
networkDerivationsCount = networksConverter.filterWalletNetworkForRequest(chainId, wallet).size,
)
return when (method) {
is WcEthMethod.MessageSign -> factories.messageSign.create(context, method)

View file

@ -72,6 +72,7 @@ internal class WcSolanaNetwork(
rawSdkRequest = request,
network = walletNetwork,
accountAddress = accountAddress,
networkDerivationsCount = networksConverter.filterWalletNetworkForRequest(chainId, wallet).size,
)
return when (method) {
is WcSolanaMethod.SignMessage -> factories.messageSign.create(context, method)

View file

@ -7,6 +7,7 @@ import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
import com.tangem.domain.walletconnect.usecase.method.WcNetworkDerivationState
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase
import kotlinx.coroutines.flow.FlowCollector
@ -23,8 +24,12 @@ internal abstract class BaseWcSignUseCase<MiddleAction, SignModel> :
abstract val context: WcMethodUseCaseContext
override val network: Network get() = context.network
override val session: WcSession get() = context.session
override val walletAddress: String get() = context.accountAddress
override val rawSdkRequest: WcSdkSessionRequest get() = context.rawSdkRequest
override val derivationState: WcNetworkDerivationState
get() = when {
context.networkDerivationsCount > 1 -> WcNetworkDerivationState.Multiple(context.accountAddress)
else -> WcNetworkDerivationState.Single
}
protected val delegate by lazy {
WcSignUseCaseDelegate(
@ -73,6 +78,7 @@ internal class WcMethodUseCaseContext(
val rawSdkRequest: WcSdkSessionRequest,
val network: Network,
val accountAddress: String,
val networkDerivationsCount: Int,
)
internal typealias SignCollector<SignModel> = FlowCollector<WcSignState<SignModel>>

View file

@ -47,6 +47,7 @@ internal class WcSignUseCaseDelegateTest {
network = MockCryptoCurrencyFactory().ethereum.network,
accountAddress = "",
rawSdkRequest = rawRequestMock,
networkDerivationsCount = 1,
session = WcSession(
wallet = MockUserWalletFactory.create(),
networks = setOf(),

View file

@ -2,11 +2,11 @@ package com.tangem.domain.walletconnect.usecase.method
import arrow.core.Either
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletconnect.model.WcMethod
import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.models.wallet.UserWallet
import kotlinx.coroutines.flow.Flow
interface WcMethodUseCase
@ -16,10 +16,15 @@ interface WcMethodContext {
val rawSdkRequest: WcSdkSessionRequest
val network: Network
val method: WcMethod
val walletAddress: String
val derivationState: WcNetworkDerivationState
val wallet: UserWallet get() = session.wallet
}
sealed class WcNetworkDerivationState {
data object Single : WcNetworkDerivationState()
data class Multiple(val walletAddress: String) : WcNetworkDerivationState()
}
/**
* Wc Methods that must be signed by Tangem card
* [WcSignState] represents signing state

View file

@ -0,0 +1,24 @@
package com.tangem.features.walletconnect.transaction.converter
import com.tangem.domain.walletconnect.usecase.method.WcNetworkDerivationState
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
import com.tangem.utils.converter.Converter
private const val ADDRESS_FIRST_PART_LENGTH = 7
private const val ADDRESS_SECOND_PART_LENGTH = 4
internal object WcAddressConverter : Converter<WcNetworkDerivationState, WcAddressUM?> {
override fun convert(value: WcNetworkDerivationState): WcAddressUM? {
return when (value) {
is WcNetworkDerivationState.Single -> null
is WcNetworkDerivationState.Multiple -> WcAddressUM(
fullAddress = value.walletAddress,
shortAddress = value.walletAddress.toShortAddressText(),
)
}
}
private fun String.toShortAddressText() =
"${take(ADDRESS_FIRST_PART_LENGTH)}...${takeLast(ADDRESS_SECOND_PART_LENGTH)}"
}

View file

@ -3,10 +3,7 @@ package com.tangem.features.walletconnect.transaction.converter
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.usecase.method.WcMutableFee
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase
import com.tangem.domain.walletconnect.usecase.method.*
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
@ -41,9 +38,9 @@ internal class WcSendTransactionUMConverter @Inject constructor(
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),
address = value.useCase.walletAddress.toShortAddressText(),
estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(),
isLoading = value.signState.domainStep == WcSignStep.Signing,
address = WcAddressConverter.convert(value.useCase.derivationState),
),
feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading,
transactionRequestInfo = WcTransactionRequestInfoUM(

View file

@ -30,6 +30,7 @@ internal class WcSignTransactionUMConverter @Inject constructor(
walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.useCase.network),
isLoading = value.signState.domainStep == WcSignStep.Signing,
address = WcAddressConverter.convert(value.useCase.derivationState),
),
transactionRequestInfo = WcTransactionRequestInfoUM(
persistentListOf(

View file

@ -17,9 +17,6 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
private const val ADDRESS_FIRST_PART_LENGTH = 7
private const val ADDRESS_SECOND_PART_LENGTH = 4
internal class WcSignTypedDataUMConverter @Inject constructor(
private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter,
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
@ -38,7 +35,7 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
),
walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo },
networkInfo = networkInfoUMConverter.convert(value.useCase.network),
addressText = value.useCase.walletAddress.toShortAddressText(),
address = WcAddressConverter.convert(value.useCase.derivationState),
isLoading = value.signState.domainStep == WcSignStep.Signing,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
@ -78,7 +75,4 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
val signModel: WcMessageSignUseCase.SignModel,
val actions: WcTransactionActionsUM,
)
}
internal fun String.toShortAddressText() =
"${take(ADDRESS_FIRST_PART_LENGTH)}...${takeLast(ADDRESS_SECOND_PART_LENGTH)}"
}

View file

@ -0,0 +1,3 @@
package com.tangem.features.walletconnect.transaction.entity.common
data class WcAddressUM(val fullAddress: String, val shortAddress: String)

View file

@ -4,6 +4,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM
@ -25,6 +26,6 @@ internal data class WcSendTransactionItemUM(
val estimatedWalletChanges: WcSendReceiveTransactionCheckResultsUM?,
val walletName: String?,
val networkInfo: WcNetworkInfoUM,
val address: String? = null,
val address: WcAddressUM?,
val isLoading: Boolean = false,
) : TangemBottomSheetConfigContent

View file

@ -1,6 +1,7 @@
package com.tangem.features.walletconnect.transaction.entity.sign
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM
@ -17,6 +18,6 @@ internal data class WcSignTransactionItemUM(
val appInfo: WcTransactionAppInfoContentUM,
val walletName: String?,
val networkInfo: WcNetworkInfoUM,
val addressText: String? = null,
val address: WcAddressUM?,
val isLoading: Boolean = false,
) : TangemBottomSheetConfigContent

View file

@ -10,12 +10,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import com.tangem.core.ui.components.tooltip.TangemTooltip
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
@Composable
internal fun WcAddressItem(addressText: String, modifier: Modifier = Modifier) {
internal fun WcAddressItem(address: WcAddressUM, modifier: Modifier = Modifier) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
@ -31,14 +33,20 @@ internal fun WcAddressItem(addressText: String, modifier: Modifier = Modifier) {
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
Text(
TangemTooltip(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing16)
.weight(1f),
text = addressText,
textAlign = TextAlign.End,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
text = address.fullAddress,
content = { contentModifier ->
Text(
modifier = contentModifier,
text = address.shortAddress,
textAlign = TextAlign.End,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
)
},
)
}
}

View file

@ -15,6 +15,7 @@ import com.tangem.core.ui.components.divider.DividerWithPadding
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState
@ -24,8 +25,8 @@ internal fun WcSendTransactionItems(
networkInfo: WcNetworkInfoUM,
feeState: WcTransactionFeeState,
feeSelectorBlockComponent: FeeSelectorBlockComponent?,
address: WcAddressUM?,
modifier: Modifier = Modifier,
address: String? = null,
) {
val onFeeBlockClicked = remember(feeState) {
when (feeState) {
@ -55,11 +56,11 @@ internal fun WcSendTransactionItems(
modifier = itemsModifier,
networkInfo = networkInfo,
)
if (!address.isNullOrEmpty()) {
if (address != null) {
DividerWithPadding(start = 40.dp, end = 12.dp)
WcAddressItem(
modifier = itemsModifier,
addressText = address,
address = address,
)
}
if (feeState != WcTransactionFeeState.None) {

View file

@ -32,6 +32,7 @@ import com.tangem.features.walletconnect.transaction.components.PreviewFeeSelect
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangeUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangesUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState
@ -191,11 +192,47 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide
),
),
),
isLoading = false,
),
walletName = "Tangem 2.0",
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
feeState = WcTransactionFeeState.Success(null, {}),
address = "0x345FF...34FA",
address = null,
),
WcSendTransactionItemUM(
onDismiss = {},
onSend = {},
appInfo = WcTransactionAppInfoContentUM(
appName = "React App",
appIcon = "",
verifiedState = VerifiedDAppState.Verified {},
appSubtitle = "react-app.walletconnect.com",
),
estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(
notificationText = TextReference.Str(
"The transaction approves erc20 tokens to a known malicious address",
),
estimatedWalletChanges = WcEstimatedWalletChangesUM(
items = persistentListOf(
WcEstimatedWalletChangeUM(
iconRes = R.drawable.ic_send_new_24,
title = resourceReference(R.string.common_send),
description = "- 42 USDT",
tokenIconUrl = "https://tangem.com",
),
WcEstimatedWalletChangeUM(
iconRes = R.drawable.ic_receive_new_24,
title = resourceReference(R.string.common_receive),
description = "+ 1,131.46 MATIC",
tokenIconUrl = "https://tangem.com",
),
),
),
),
walletName = "Tangem 2.0",
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
feeState = WcTransactionFeeState.Success(null, {}),
address = WcAddressUM("0xdac17f958d2ee523a2206206994597c13d831ec7", "0x345FF...34FA"),
),
WcSendTransactionItemUM(
onDismiss = {},
@ -230,7 +267,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide
walletName = null,
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
feeState = WcTransactionFeeState.None,
address = "0x345FF...34FA",
address = null,
),
),
)

View file

@ -28,6 +28,7 @@ import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState
import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
@ -124,11 +125,11 @@ private fun WcSignTransactionItems(state: WcSignTransactionItemUM, modifier: Mod
modifier = itemsModifier,
networkInfo = state.networkInfo,
)
if (!state.addressText.isNullOrEmpty()) {
if (state.address != null) {
DividerWithPadding(start = 40.dp, end = 12.dp)
WcAddressItem(
modifier = itemsModifier,
addressText = state.addressText,
address = state.address,
)
}
}
@ -182,6 +183,7 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide
),
walletName = "Tangem 2.0",
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
address = null,
),
WcSignTransactionItemUM(
onDismiss = {},
@ -193,8 +195,8 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide
appSubtitle = "react-app.walletconnect.com",
),
walletName = null,
addressText = "0x345FF...34FA",
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
address = WcAddressUM("0xdac17f958d2ee523a2206206994597c13d831ec7", "0x345FF...34FA"),
),
),
)