diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 3b3adab9bf..0776b43400 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -273,6 +273,18 @@ internal object TokensDomainModule { ) } + @Provides + @Singleton + fun provideGetMinimumTransactionAmountSyncUseCase( + currencyChecksRepository: CurrencyChecksRepository, + dispatchers: CoroutineDispatcherProvider, + ): GetMinimumTransactionAmountSyncUseCase { + return GetMinimumTransactionAmountSyncUseCase( + currencyChecksRepository = currencyChecksRepository, + dispatchers = dispatchers, + ) + } + @Provides @Singleton fun provideIsCryptoCurrencyCoinCouldHideUseCase( diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt index 0b9f4c1a5d..4fbf0d991a 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt @@ -4,10 +4,15 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.utils.checkExceedBalance import com.tangem.common.ui.amountScreen.utils.getFiatValue import com.tangem.common.ui.amountScreen.utils.getKeyboardAction +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero @@ -22,6 +27,7 @@ import java.math.BigDecimal */ class AmountReduceByTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: ReduceByData, ) : Transformer { @@ -47,22 +53,37 @@ class AmountReduceByTransformer( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField) + val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false val isZero = if (amountTextField.isFiatValue) { decimalFiatValue.isNullOrZero() } else { decimalCryptoValue.isNullOrZero() } + val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided return prevState.copy( - isPrimaryButtonEnabled = !isExceedBalance && !isZero, + isPrimaryButtonEnabled = !isZero && !isCheckFailed, amountTextField = amountTextField.copy( value = cryptoValue, fiatValue = fiatValue, - isError = isExceedBalance, - error = resourceReference(R.string.send_validation_amount_exceeds_balance), + isError = isCheckFailed, + error = when { + isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) + isLessThanMinimumIfProvided -> { + val minimumAmount = minimumTransactionAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue), + imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt index 74f6e66852..7e5cf0ab8d 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt @@ -4,11 +4,17 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.utils.checkExceedBalance import com.tangem.common.ui.amountScreen.utils.getFiatValue import com.tangem.common.ui.amountScreen.utils.getKeyboardAction +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer @@ -22,6 +28,7 @@ import java.math.BigDecimal */ class AmountReduceToTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: BigDecimal, ) : Transformer { private val maxEnterAmountConverter = MaxEnterAmountConverter() @@ -34,6 +41,7 @@ class AmountReduceToTransformer( val fiatDecimals = amountTextField.fiatAmount.decimals val cryptoValue = value.parseBigDecimal(cryptoDecimals) + val decimalCryptoValue = cryptoValue.parseToBigDecimal(cryptoDecimals) val (fiatValue, decimalFiatValue) = cryptoValue.getFiatValue( fiatRate = cryptoCurrencyStatus.value.fiatRate, isFiatValue = false, @@ -44,18 +52,33 @@ class AmountReduceToTransformer( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField) + val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else value.isNullOrZero() + val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided return prevState.copy( - isPrimaryButtonEnabled = !isExceedBalance && !isZero, + isPrimaryButtonEnabled = !isZero && !isCheckFailed, amountTextField = amountTextField.copy( value = cryptoValue, fiatValue = fiatValue, - isError = isExceedBalance, - error = resourceReference(R.string.send_validation_amount_exceeds_balance), + isError = isCheckFailed, + error = when { + isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) + isLessThanMinimumIfProvided -> { + val minimumAmount = minimumTransactionAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = value), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = getKeyboardAction(isExceedBalance, value), + imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt index d463a635bc..f91822b7b7 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt @@ -6,7 +6,7 @@ import com.tangem.common.ui.amountScreen.converters.field.AmountFieldConverter import com.tangem.common.ui.amountScreen.models.AmountParameters import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -34,7 +34,7 @@ class AmountStateConverter( private val clickIntents: AmountScreenClickIntents, private val appCurrencyProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, - private val maxEnterAmount: MaxEnterAmount, + private val maxEnterAmount: EnterAmountBoundary, private val iconStateConverter: CryptoCurrencyToIconStateConverter, ) : Converter { diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt index 1b23b64181..dce1957361 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt @@ -1,16 +1,16 @@ package com.tangem.common.ui.amountScreen.converters -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.converter.Converter /** - * Converts [CryptoCurrencyStatus] to [MaxEnterAmount] + * Converts [CryptoCurrencyStatus] to [EnterAmountBoundary] */ -class MaxEnterAmountConverter : Converter { +class MaxEnterAmountConverter : Converter { - override fun convert(value: CryptoCurrencyStatus): MaxEnterAmount { - return MaxEnterAmount( + override fun convert(value: CryptoCurrencyStatus): EnterAmountBoundary { + return EnterAmountBoundary( amount = value.value.amount, fiatAmount = value.value.fiatAmount, fiatRate = value.value.fiatRate, diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt index b51fa49ad2..392fa817ba 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt @@ -5,14 +5,18 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.utils.checkExceedBalance import com.tangem.common.ui.amountScreen.utils.getCryptoValue import com.tangem.common.ui.amountScreen.utils.getFiatValue import com.tangem.common.ui.amountScreen.utils.getKeyboardAction import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseToBigDecimal +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer import java.math.BigDecimal @@ -24,7 +28,9 @@ import java.math.BigDecimal * @property value amount value */ class AmountFieldChangeTransformer( - private val maxEnterAmount: MaxEnterAmount, + private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val maxEnterAmount: EnterAmountBoundary, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: String, ) : Transformer { @@ -52,23 +58,37 @@ class AmountFieldChangeTransformer( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField) + val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false val isZero = if (amountTextField.isFiatValue) { decimalFiatValue.isNullOrZero() } else { decimalCryptoValue.isNullOrZero() } + val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided return prevState.copy( - isPrimaryButtonEnabled = !isExceedBalance && !isZero, + isPrimaryButtonEnabled = !isZero && !isCheckFailed, amountTextField = amountTextField.copy( value = cryptoValue, fiatValue = fiatValue, - isError = isExceedBalance, - error = resourceReference(R.string.send_validation_amount_exceeds_balance).takeIf { isExceedBalance } - ?: TextReference.EMPTY, + isError = isCheckFailed, + error = when { + isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) + isLessThanMinimumIfProvided -> { + val minimumAmount = minimumTransactionAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue), + imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt index 16c67d20f8..990e027cdf 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt @@ -1,12 +1,19 @@ package com.tangem.common.ui.amountScreen.converters.field import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary +import com.tangem.common.ui.amountScreen.utils.getKeyboardAction +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal -import com.tangem.utils.isNullOrZero +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.utils.extensions.isZero import com.tangem.utils.transformer.Transformer import java.math.RoundingMode @@ -16,7 +23,9 @@ import java.math.RoundingMode * @property maxAmount maximum enter amount */ class AmountFieldSetMaxAmountTransformer( - private val maxAmount: MaxEnterAmount, + private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val maxAmount: EnterAmountBoundary, + private val minAmount: EnterAmountBoundary?, ) : Transformer { override fun transform(prevState: AmountState): AmountState { @@ -29,22 +38,35 @@ class AmountFieldSetMaxAmountTransformer( val decimalCryptoValue = maxAmount.amount val decimalFiatValue = maxAmount.fiatAmount - if (decimalCryptoValue.isNullOrZero()) return prevState + if (decimalCryptoValue == null || decimalCryptoValue.isZero()) return prevState - val isDoneActionEnabled = !decimalCryptoValue.isNullOrZero() - val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty() + val cryptoValue = decimalCryptoValue.parseBigDecimal(cryptoDecimals) val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals, roundingMode = RoundingMode.HALF_UP).orEmpty() + val isLessThanMinimumIfProvided = minAmount?.amount?.let { decimalCryptoValue < it } ?: false return prevState.copy( - isPrimaryButtonEnabled = true, + isPrimaryButtonEnabled = !isLessThanMinimumIfProvided, amountTextField = amountTextField.copy( isValuePasted = true, value = cryptoValue, fiatValue = fiatValue, - isError = false, + isError = isLessThanMinimumIfProvided, + error = when { + isLessThanMinimumIfProvided -> { + val minimumAmount = minAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = if (isDoneActionEnabled) ImeAction.Done else ImeAction.None, + imeAction = getKeyboardAction(isLessThanMinimumIfProvided, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt new file mode 100644 index 0000000000..bc21ab5104 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt @@ -0,0 +1,22 @@ +package com.tangem.common.ui.amountScreen.models + +import java.math.BigDecimal + +data class EnterAmountBoundary( + val amount: BigDecimal? = null, + val fiatAmount: BigDecimal? = null, + val fiatRate: BigDecimal? = null, +) { + constructor( + amount: BigDecimal? = null, + fiatRate: BigDecimal? = null, + ) : this( + amount = amount, + fiatAmount = if (amount != null && fiatRate != null) { + amount * fiatRate + } else { + null + }, + fiatRate = fiatRate, + ) +} \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt deleted file mode 100644 index e9a5f88238..0000000000 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.common.ui.amountScreen.models - -import java.math.BigDecimal - -data class MaxEnterAmount( - val amount: BigDecimal? = null, - val fiatAmount: BigDecimal? = null, - val fiatRate: BigDecimal? = null, -) \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt index 3f20ba474c..fe59dbf317 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt @@ -2,10 +2,10 @@ package com.tangem.common.ui.amountScreen.utils import androidx.compose.ui.text.input.ImeAction import com.tangem.common.ui.amountScreen.models.AmountFieldModel -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.utils.isNullOrZero +import com.tangem.utils.extensions.isZero import java.math.BigDecimal import java.math.RoundingMode @@ -36,7 +36,10 @@ internal fun String.getFiatValue( } } -internal fun String.checkExceedBalance(maxEnterAmount: MaxEnterAmount, amountTextField: AmountFieldModel): Boolean { +internal fun String.checkExceedBalance( + maxEnterAmount: EnterAmountBoundary, + amountTextField: AmountFieldModel, +): Boolean { val currencyCryptoAmount = maxEnterAmount.amount ?: BigDecimal.ZERO val currencyFiatAmount = maxEnterAmount.fiatAmount ?: BigDecimal.ZERO val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals) @@ -48,8 +51,8 @@ internal fun String.checkExceedBalance(maxEnterAmount: MaxEnterAmount, amountTex } } -internal fun getKeyboardAction(isExceedBalance: Boolean, decimalCryptoValue: BigDecimal) = - if (!isExceedBalance && !decimalCryptoValue.isNullOrZero()) { +internal fun getKeyboardAction(isCheckFailed: Boolean, decimalCryptoValue: BigDecimal) = + if (!isCheckFailed && !decimalCryptoValue.isZero()) { ImeAction.Done } else { ImeAction.None diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt index 8ca24539b5..858fdccb46 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt @@ -47,6 +47,14 @@ sealed class NotificationUM(val config: NotificationConfig) { ), ) + data class MinimumSendAmountError(val amount: String) : Error( + title = resourceReference(R.string.send_notification_invalid_amount_title), + subtitle = resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(amount, amount), + ), + ) + data class TransactionLimitError( val cryptoCurrency: String, val utxoLimit: String, diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt index 5ba4b5f528..359d6dd9c0 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt @@ -75,6 +75,20 @@ object NotificationsFactory { } } + fun MutableList.addMinimumAmountErrorNotification( + minimumSendAmount: BigDecimal?, + sendingAmount: BigDecimal, + cryptoCurrency: CryptoCurrency, + ) { + if (minimumSendAmount != null && minimumSendAmount > sendingAmount) { + add( + NotificationUM.Error.MinimumSendAmountError( + amount = minimumSendAmount.format { crypto(cryptoCurrency) }, + ), + ) + } + } + fun MutableList.addTransactionLimitErrorNotification( utxoLimit: UtxoAmountLimit?, cryptoCurrency: CryptoCurrency, diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt index ea56a8adbf..b156d85caf 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt @@ -2,6 +2,7 @@ package com.tangem.data.tokens.repository import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.common.FeeResourceAmountProvider +import com.tangem.blockchain.common.MinimumSendAmountProvider import com.tangem.blockchain.common.ReserveAmountProvider import com.tangem.blockchain.common.UtxoAmountLimitProvider import com.tangem.data.tokens.converters.UtxoConverter @@ -43,6 +44,15 @@ internal class DefaultCurrencyChecksRepository( return if (manager is ReserveAmountProvider) manager.getReserveAmount() else null } + override suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal? { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + + return if (manager is MinimumSendAmountProvider) manager.getMinimumSendAmount() else null + } + override suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount? { val manager = walletManagersFacade.getOrCreateWalletManager( userWalletId = userWalletId, diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt index 780c6c48fe..dae8238b02 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt @@ -6,6 +6,7 @@ import java.math.BigDecimal data class CryptoCurrencyCheck( val dustValue: BigDecimal?, val reserveAmount: BigDecimal?, + val minimumSendAmount: BigDecimal?, val existentialDeposit: BigDecimal?, val utxoAmountLimit: UtxoAmountLimit?, val isAccountFunded: Boolean, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt index aa488b2671..ba9e837248 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt @@ -24,6 +24,7 @@ class GetCurrencyCheckUseCase( val network = currencyStatus.currency.network val dustValue = currencyChecksRepository.getDustValue(userWalletId, network) val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network) + val minimumSendAmount = currencyChecksRepository.getMinimumSendAmount(userWalletId, network) val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network) val isAccountFunded = recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( @@ -46,6 +47,7 @@ class GetCurrencyCheckUseCase( CryptoCurrencyCheck( dustValue = dustValue, reserveAmount = reserveAmount, + minimumSendAmount = minimumSendAmount, existentialDeposit = existentialDeposit, utxoAmountLimit = utxoAmountLimit, isAccountFunded = isAccountFunded, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt new file mode 100644 index 0000000000..73aaeb8c0d --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt @@ -0,0 +1,26 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.raise.either +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.repository.CurrencyChecksRepository +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import java.math.BigDecimal + +class GetMinimumTransactionAmountSyncUseCase( + private val currencyChecksRepository: CurrencyChecksRepository, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend operator fun invoke( + userWalletId: UserWalletId, + cryptoCurrencyStatus: CryptoCurrencyStatus, + ): Either = withContext(dispatchers.io) { + either { + val cryptoCurrency = cryptoCurrencyStatus.currency + currencyChecksRepository.getMinimumSendAmount(userWalletId, cryptoCurrency.network) + } + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt index 0b5745a983..4ff37c6420 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt @@ -20,6 +20,9 @@ interface CurrencyChecksRepository { /** Returns reserve amount which is required to create an account */ suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal? + /** Returns minimum send transaction amount */ + suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal? + /** Returns a fee resource amount available and max for paying fees in several blockchains */ suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount? diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt index 204918b99b..9b8d11fb41 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -16,6 +17,7 @@ internal class AmountStateFactory( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) { private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) { @@ -23,6 +25,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) { @@ -30,6 +33,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } @@ -51,6 +55,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } private val amountReduceToConverter by lazy { @@ -58,6 +63,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt index 0468cd8c82..c0e28102e7 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -11,6 +12,7 @@ internal class SendAmountReduceByConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { override fun convert(value: AmountReduceByTransformer.ReduceByData): SendUiState { @@ -23,7 +25,11 @@ internal class SendAmountReduceByConverter( sendState = state.sendState?.copy( reduceAmountBy = value.reduceAmountBy, ), - amountState = AmountReduceByTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState), + amountState = AmountReduceByTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), + minimumTransactionAmount = minimumTransactionAmountProvider(), + value = value, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt index 1bb5518063..ea1caaf180 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -12,6 +13,7 @@ internal class SendAmountReduceToConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { override fun convert(value: BigDecimal): SendUiState { @@ -21,7 +23,11 @@ internal class SendAmountReduceToConverter( return state.copyWrapped( isEditState = isEditState, - amountState = AmountReduceToTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState), + amountState = AmountReduceToTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), + minimumTransactionAmount = minimumTransactionAmountProvider(), + value = value, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index bfe558339a..9975712433 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -11,6 +11,7 @@ import com.tangem.common.ui.notifications.NotificationsFactory.addExceedsBalance import com.tangem.common.ui.notifications.NotificationsFactory.addExistentialWarningNotification import com.tangem.common.ui.notifications.NotificationsFactory.addFeeCoverageNotification import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachableNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addMinimumAmountErrorNotification import com.tangem.common.ui.notifications.NotificationsFactory.addReserveAmountErrorNotification import com.tangem.common.ui.notifications.NotificationsFactory.addTransactionLimitErrorNotification import com.tangem.common.ui.notifications.NotificationsFactory.addValidateTransactionNotifications @@ -192,6 +193,11 @@ internal class SendNotificationFactory( cryptoCurrency = currency, isAccountFunded = currencyCheck.isAccountFunded, ) + addMinimumAmountErrorNotification( + minimumSendAmount = currencyCheck.minimumSendAmount, + sendingAmount = sendingAmount, + cryptoCurrency = currency, + ) } private suspend fun MutableList.addWarningNotifications( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt index c4f28c469e..ea7e502aec 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -12,21 +13,29 @@ internal class SendAmountFieldChangeConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { private val maxEnterAmountConverter = MaxEnterAmountConverter() override fun convert(value: String): SendUiState { val state = currentStateProvider() + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val isEditState = stateRouterProvider().isEditState val amountState = state.getAmountState(isEditState) - val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider()) + val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus) + val minimumTransactionAmount = minimumTransactionAmountProvider() return state.copyWrapped( isEditState = isEditState, sendState = state.sendState?.copy(reduceAmountBy = null), - amountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(amountState), + amountState = AmountFieldChangeTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = maxEnterAmount, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt index 6e071d34d8..882365d726 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -13,6 +14,7 @@ internal class SendAmountFieldMaxAmountConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { private val maxEnterAmountConverter = MaxEnterAmountConverter() @@ -27,11 +29,16 @@ internal class SendAmountFieldMaxAmountConverter( if (decimalCryptoValue.isNullOrZero()) return state val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus) + val minimumTransactionAmount = minimumTransactionAmountProvider() return state.copyWrapped( isEditState = isEditState, sendState = state.sendState?.copy(reduceAmountBy = null), - amountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount).transform(amountState), + amountState = AmountFieldSetMaxAmountTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxAmount = maxEnterAmount, + minAmount = minimumTransactionAmount, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 468482bb95..14b94f1d08 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -12,6 +12,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.parseBigDecimal @@ -59,6 +60,7 @@ import com.tangem.features.send.impl.presentation.state.recipient.RecipientSendF import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.Provider import com.tangem.utils.coroutines.* +import com.tangem.utils.extensions.orZero import com.tangem.utils.extensions.stripZeroPlainString import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.* @@ -78,6 +80,7 @@ internal class SendViewModel @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getWalletsUseCase: GetWalletsUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, + private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase, private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase, private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, @@ -153,6 +156,7 @@ internal class SendViewModel @Inject constructor( stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState.value }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + minimumTransactionAmountProvider = Provider { minimumTransactionAmount }, ) private val feeStateFactory = FeeStateFactory( @@ -212,6 +216,7 @@ internal class SendViewModel @Inject constructor( private var isTapHelpPreviewEnabled: Boolean = false private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null + private var minimumTransactionAmount: EnterAmountBoundary? = null private var balanceJobHolder = JobHolder() private var balanceHidingJobHolder = JobHolder() @@ -295,6 +300,7 @@ internal class SendViewModel @Inject constructor( onDataLoaded( currencyStatus = cryptoCurrencyStatus, feeCurrencyStatus = getFeeCurrencyStatusSync(cryptoCurrencyStatus, isMultiCurrency), + minTransactionAmount = getMinimumTransactionAmount(cryptoCurrencyStatus), ) }, ifLeft = { showErrorAlert() }, @@ -336,6 +342,18 @@ internal class SendViewModel @Inject constructor( } } + private suspend fun getMinimumTransactionAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): EnterAmountBoundary? { + return getMinimumTransactionAmountSyncUseCase( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).getOrNull()?.let { + EnterAmountBoundary( + amount = it, + fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(), + ) + } + } + private fun createSelectedAppCurrencyFlow(): StateFlow { return getSelectedAppCurrencyUseCase() .map { maybeAppCurrency -> @@ -348,9 +366,14 @@ internal class SendViewModel @Inject constructor( ) } - private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus?) { + private fun onDataLoaded( + currencyStatus: CryptoCurrencyStatus, + feeCurrencyStatus: CryptoCurrencyStatus?, + minTransactionAmount: EnterAmountBoundary?, + ) { cryptoCurrencyStatus = currencyStatus feeCryptoCurrencyStatus = feeCurrencyStatus + minimumTransactionAmount = minTransactionAmount subscribeOnQRScannerResult() when { uiState.value.sendState?.isSuccess == true -> return diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt index b9226a4214..532054c768 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.common.ui.amountScreen.converters.AmountStateConverter import com.tangem.common.ui.amountScreen.models.AmountParameters -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -37,7 +37,7 @@ internal class SetAmountDataTransformer( } else { cryptoBalanceValue.amount to cryptoBalanceValue.fiatAmount } - val maxEnterAmount = MaxEnterAmount( + val maxEnterAmount = EnterAmountBoundary( amount = amount, fiatAmount = fiatAmount, fiatRate = cryptoBalanceValue.fiatRate, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index ef2d4058db..6825aeb4f6 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -4,7 +4,7 @@ import com.tangem.common.extensions.remove import com.tangem.common.ui.amountScreen.converters.AmountStateConverter import com.tangem.common.ui.amountScreen.models.AmountParameters import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.list.RoundedListWithDividersItemData import com.tangem.core.ui.extensions.* @@ -201,7 +201,7 @@ internal class SetInitialDataStateTransformer( private fun createInitialAmountState(): AmountState { val cryptoBalanceValue = cryptoCurrencyStatusProvider().value - val maxEnterAmount = MaxEnterAmount( + val maxEnterAmount = EnterAmountBoundary( amount = cryptoBalanceValue.amount, fiatAmount = cryptoBalanceValue.fiatAmount, fiatRate = cryptoBalanceValue.fiatRate, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt index cd74d47116..41fdc707cf 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -11,6 +11,7 @@ import com.tangem.utils.transformer.Transformer internal class AmountChangeStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: String, private val yield: Yield, ) : Transformer { @@ -20,7 +21,7 @@ internal class AmountChangeStateTransformer( override fun transform(prevState: StakingUiState): StakingUiState { val actionType = prevState.actionType val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) { - MaxEnterAmount( + EnterAmountBoundary( amount = prevState.balanceState?.cryptoAmount, fiatAmount = prevState.balanceState?.fiatAmount, fiatRate = cryptoCurrencyStatus.value.fiatRate, @@ -29,7 +30,12 @@ internal class AmountChangeStateTransformer( maxEnterAmountConverter.convert(cryptoCurrencyStatus) } - val updatedAmountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(prevState.amountState) + val updatedAmountState = AmountFieldChangeTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = maxEnterAmount, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(prevState.amountState) return prevState.copy( amountState = AmountRequirementStateTransformer( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt index 034398c337..db2865b4ec 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -11,6 +11,7 @@ import com.tangem.utils.transformer.Transformer internal class AmountMaxValueStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val actionType: StakingActionCommonType, private val yield: Yield, ) : Transformer { @@ -19,7 +20,7 @@ internal class AmountMaxValueStateTransformer( override fun transform(prevState: StakingUiState): StakingUiState { val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) { - MaxEnterAmount( + EnterAmountBoundary( amount = prevState.balanceState?.cryptoAmount, fiatAmount = prevState.balanceState?.fiatAmount, fiatRate = cryptoCurrencyStatus.value.fiatRate, @@ -28,8 +29,11 @@ internal class AmountMaxValueStateTransformer( maxEnterAmountConverter.convert(cryptoCurrencyStatus) } - val updatedAmountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount) - .transform(prevState.amountState) + val updatedAmountState = AmountFieldSetMaxAmountTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxAmount = maxEnterAmount, + minAmount = minimumTransactionAmount, + ).transform(prevState.amountState) return prevState.copy( amountState = AmountRequirementStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt index 91468a21bd..f5e92f66ec 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt @@ -2,18 +2,24 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer internal class AmountReduceByStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: ReduceByData, ) : Transformer { override fun transform(prevState: StakingUiState): StakingUiState { return prevState.copy( - amountState = AmountReduceByTransformer(cryptoCurrencyStatus, value).transform(prevState.amountState), + amountState = AmountReduceByTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(prevState.amountState), ) } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt index b826aae59e..5f7b5e5e57 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt @@ -1,6 +1,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer @@ -8,11 +9,16 @@ import java.math.BigDecimal internal class AmountReduceToStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: BigDecimal, ) : Transformer { override fun transform(prevState: StakingUiState): StakingUiState { return prevState.copy( - amountState = AmountReduceToTransformer(cryptoCurrencyStatus, value).transform(prevState.amountState), + amountState = AmountReduceToTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(prevState.amountState), ) } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index f8dff3cf4a..e958be8c31 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -11,6 +11,7 @@ import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.bottomsheet.permission.state.ApproveType import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig import com.tangem.common.ui.notifications.NotificationUM @@ -72,6 +73,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstake import com.tangem.utils.Provider import com.tangem.utils.coroutines.* import com.tangem.utils.extensions.isSingleItem +import com.tangem.utils.extensions.orZero import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -92,6 +94,7 @@ internal class StakingViewModel @Inject constructor( private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, + private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, private val sendTransactionUseCase: SendTransactionUseCase, @@ -139,6 +142,7 @@ internal class StakingViewModel @Inject constructor( private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() private var processingActions: List = emptyList() private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null + private var minimumTransactionAmount: EnterAmountBoundary? = null private var innerRouter: InnerStakingRouter by Delegates.notNull() private var userWallet: UserWallet by Delegates.notNull() @@ -366,7 +370,14 @@ internal class StakingViewModel @Inject constructor( } override fun onAmountValueChange(value: String) { - stateController.update(AmountChangeStateTransformer(cryptoCurrencyStatus, value, yield)) + stateController.update( + AmountChangeStateTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + yield = yield, + ), + ) } override fun onAmountPasteTriggerDismiss() { @@ -378,6 +389,7 @@ internal class StakingViewModel @Inject constructor( stateController.update( AmountMaxValueStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, actionType = uiState.value.actionType, yield = yield, ), @@ -621,6 +633,7 @@ internal class StakingViewModel @Inject constructor( ) { AmountReduceByStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, value = AmountReduceByTransformer.ReduceByData( reduceAmountBy = reduceAmountBy, reduceAmountByDiff = reduceAmountByDiff, @@ -633,6 +646,7 @@ internal class StakingViewModel @Inject constructor( stateController.update( AmountReduceToStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, value = reduceAmountTo, ), ) @@ -796,6 +810,13 @@ internal class StakingViewModel @Inject constructor( feeCryptoCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull() + minimumTransactionAmount = + getMinimumTransactionAmountSyncUseCase(userWalletId, status).getOrNull()?.let { + EnterAmountBoundary( + amount = it, + fiatRate = status.value.fiatRate.orZero(), + ) + } cryptoCurrencyStatus = status setupApprovalNeeded() @@ -929,6 +950,7 @@ internal class StakingViewModel @Inject constructor( AmountChangeStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, value = amountValue, + minimumTransactionAmount = minimumTransactionAmount, yield = yield, ), )