diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 04f3106b33..e0b447a00d 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -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( diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsMemoRequiredUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsMemoRequiredUseCase.kt new file mode 100644 index 0000000000..410f8fe048 --- /dev/null +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsMemoRequiredUseCase.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt index 5ec0bf75e3..b6c92acf3a 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt @@ -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, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt index 5cf7dc551b..8d0fd0bb25 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt @@ -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.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.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.addTronNetworkFeesNotification() { val cryptoCurrency = cryptoCurrencyStatus.currency val isTronToken = cryptoCurrency is CryptoCurrency.Token && diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt index 0857fa9cca..f69bf413d1 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt @@ -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.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) } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt index d5728b155e..8e24591f1f 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt @@ -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(), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt index a82e827dc2..3c71e14050 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt @@ -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, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/ui/SendWithSwapConfirmContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/ui/SendWithSwapConfirmContent.kt index 1688c9b88a..7005c6acdf 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/ui/SendWithSwapConfirmContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/ui/SendWithSwapConfirmContent.kt @@ -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