Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-14 14:22:01 +04:00
parent 589805424e
commit c448a1aebb
8 changed files with 64 additions and 57 deletions

View file

@ -7,6 +7,7 @@ import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.transaction.WalletAddressServiceRepository
import com.tangem.domain.transaction.usecase.ParseSharedAddressUseCase
import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase
import com.tangem.domain.transaction.usecase.IsMemoRequiredUseCase
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.delegate.DefaultUserWalletsSyncDelegate
@ -210,6 +211,14 @@ internal object WalletsDomainModule {
return ValidateWalletMemoUseCase(walletAddressServiceRepository = walletAddressServiceRepository)
}
@Provides
@Singleton
fun providesIsMemoRequiredUseCase(
walletAddressServiceRepository: WalletAddressServiceRepository,
): IsMemoRequiredUseCase {
return IsMemoRequiredUseCase(walletAddressServiceRepository = walletAddressServiceRepository)
}
@Provides
@Singleton
fun providesParseSharedAddressUseCase(

View file

@ -0,0 +1,20 @@
package com.tangem.domain.transaction.usecase
import com.tangem.domain.models.network.Network
import com.tangem.domain.transaction.WalletAddressServiceRepository
class IsMemoRequiredUseCase(
private val walletAddressServiceRepository: WalletAddressServiceRepository,
) {
suspend operator fun invoke(network: Network, destinationAddress: String): Boolean {
return try {
walletAddressServiceRepository.isMemoRequired(
network = network,
destinationAddress = destinationAddress,
)
} catch (_: Throwable) {
false
}
}
}

View file

@ -33,7 +33,7 @@ interface SendNotificationsComponent {
val callback: ModelCallback,
) {
data class NotificationData(
val destinationAddress: String,
val destinationAddress: String?,
val memo: String?,
val amountValue: BigDecimal,
val reduceAmountBy: BigDecimal,

View file

@ -34,7 +34,6 @@ import com.tangem.domain.tokens.GetCurrencyCheckUseCase
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
@ -69,7 +68,6 @@ internal class NotificationsModel @Inject constructor(
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase,
private val validateTransactionUseCase: ValidateTransactionUseCase,
private val validateWalletMemoUseCase: ValidateWalletMemoUseCase,
private val getTronFeeNotificationShowCountUseCase: GetTronFeeNotificationShowCountUseCase,
private val incrementNotificationsShowCountUseCase: IncrementNotificationsShowCountUseCase,
private val notificationsUpdateTrigger: SendNotificationsUpdateTrigger,
@ -302,7 +300,7 @@ internal class NotificationsModel @Inject constructor(
}
private suspend fun MutableList<NotificationUM>.addWarningNotifications(
destinationAddress: String,
destinationAddress: String?,
memo: String?,
enteredAmount: BigDecimal,
sendingAmount: BigDecimal,
@ -311,14 +309,18 @@ internal class NotificationsModel @Inject constructor(
isFeeCoverage: Boolean,
currencyCheck: CryptoCurrencyCheck,
) {
val validationError = validateTransactionUseCase(
userWalletId = userWalletId,
amount = enteredAmount.convertToSdkAmount(cryptoCurrencyStatus),
fee = fee,
memo = memo,
destination = destinationAddress,
network = cryptoCurrencyStatus.currency.network,
).leftOrNull()
val validationError = if (destinationAddress != null) {
validateTransactionUseCase(
userWalletId = userWalletId,
amount = enteredAmount.convertToSdkAmount(cryptoCurrencyStatus),
fee = fee,
memo = memo,
destination = destinationAddress,
network = cryptoCurrencyStatus.currency.network,
).leftOrNull()
} else {
null
}
addRentExemptionNotification(
rentWarning = currencyCheck.rentWarning,
@ -352,10 +354,6 @@ internal class NotificationsModel @Inject constructor(
params.callback.onAmountReduceTo(reduceTo)
},
)
addDestinationTagRequiredNotification(
isMemoRequired = currencyCheck.isMemoRequired,
memo = memo,
)
addHighFeeWarningNotification(
enteredAmountValue = enteredAmount,
cryptoCurrencyStatus = cryptoCurrencyStatus,
@ -376,21 +374,6 @@ internal class NotificationsModel @Inject constructor(
addTronNetworkFeesNotification()
}
private suspend fun MutableList<NotificationUM>.addDestinationTagRequiredNotification(
isMemoRequired: Boolean,
memo: String?,
) {
if (!isMemoRequired || contains(NotificationUM.Error.DestinationTagRequired)) return
val isMemoInvalid = memo.isNullOrEmpty() || validateWalletMemoUseCase(
userWalletId = userWalletId,
cryptoCurrency = currency,
memo = memo,
).isLeft()
if (isMemoInvalid) {
add(NotificationUM.Error.DestinationTagRequired)
}
}
private suspend fun MutableList<NotificationUM>.addTronNetworkFeesNotification() {
val cryptoCurrency = cryptoCurrencyStatus.currency
val isTronToken = cryptoCurrency is CryptoCurrency.Token &&

View file

@ -1,6 +1,5 @@
package com.tangem.features.swap.v2.impl.notifications.model
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
@ -9,8 +8,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.transaction.usecase.IsMemoRequiredUseCase
import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger
import com.tangem.features.swap.v2.impl.amount.entity.PriceImpact
import com.tangem.features.swap.v2.impl.notifications.DefaultSwapNotificationsUpdateTrigger
@ -28,7 +26,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.math.BigDecimal
import javax.inject.Inject
@Suppress("LongParameterList")
@ -38,7 +35,7 @@ internal class SwapNotificationsModel @Inject constructor(
private val swapNotificationsUpdateListener: SwapNotificationsUpdateListener,
private val swapNotificationsUpdateTrigger: DefaultSwapNotificationsUpdateTrigger,
private val swapAmountUpdateTrigger: SwapAmountUpdateTrigger,
private val validateTransactionUseCase: ValidateTransactionUseCase,
private val isMemoRequiredUseCase: IsMemoRequiredUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
paramsContainer: ParamsContainer,
) : Model() {
@ -116,20 +113,18 @@ internal class SwapNotificationsModel @Inject constructor(
private suspend fun MutableList<NotificationUM>.addDestinationTagRequiredNotification() {
val toCryptoCurrencyStatus = notificationData.toCryptoCurrencyStatus ?: return
val userWalletId = notificationData.userWalletId ?: return
val destinationAddress = notificationData.destinationAddress
if (destinationAddress.isEmpty()) return
val validationError = validateTransactionUseCase(
amount = BigDecimal.ZERO.convertToSdkAmount(toCryptoCurrencyStatus),
fee = null,
memo = notificationData.memo,
destination = destinationAddress,
userWalletId = userWalletId,
network = toCryptoCurrencyStatus.currency.network,
).leftOrNull()
if (validationError is BlockchainSdkError.DestinationTagRequired) {
val isMemoRequired = if (notificationData.memo.isNullOrEmpty()) {
isMemoRequiredUseCase(
network = toCryptoCurrencyStatus.currency.network,
destinationAddress = destinationAddress,
)
} else {
false
}
if (isMemoRequired) {
add(NotificationUM.Error.DestinationTagRequired)
}
}

View file

@ -11,7 +11,6 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDirection
@ -113,10 +112,11 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor(
appCurrency = params.appCurrency,
callback = model,
notificationData = SendNotificationsComponent.Params.NotificationData(
destinationAddress = when (val currency = model.primaryCurrencyStatus.currency) {
is CryptoCurrency.Token -> currency.contractAddress
is CryptoCurrency.Coin -> "0"
},
/**
* Null when destination is unknown at this point (e.g. CEX swap address is only known
* after receiving exchange-data). For DEX / DEX_BRIDGE, the address is known upfront.
*/
destinationAddress = null,
memo = null,
amountValue = model.confirmData.enteredFromAmount.orZero(),
reduceAmountBy = model.confirmData.reduceAmountBy.orZero(),

View file

@ -436,10 +436,11 @@ internal class SendWithSwapConfirmModel @Inject constructor(
feeUMV2?.feeExtraInfo?.feeCryptoCurrencyStatus ?: params.primaryFeePaidCurrencyStatusFlow.value
sendNotificationsUpdateTrigger.triggerUpdate(
data = NotificationData(
destinationAddress = when (val currency = primaryCurrencyStatus.currency) {
is CryptoCurrency.Token -> currency.contractAddress
is CryptoCurrency.Coin -> "0"
},
/**
* Null when destination is unknown at this point (e.g. CEX swap address is only known
* after receiving exchange-data). For DEX / DEX_BRIDGE, the address is known upfront.
*/
destinationAddress = null,
memo = null,
amountValue = confirmData.enteredFromAmount.orZero(),
reduceAmountBy = confirmData.reduceAmountBy,

View file

@ -12,7 +12,6 @@ import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.notifications.notifications
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.extensions.*
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationBlockComponent