From 8cee77705fa9ce2cad080047b399c5e13a1a49ca Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 12 Dec 2025 13:16:10 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/YieldSupplyDomainModule.kt | 6 ++++ .../currency/CryptoCurrencyExtensions.kt | 4 +-- .../YieldSupplyGetDustMinAmountUseCase.kt | 20 ++++++++++++ .../YieldSupplyGetDustMinAmountUseCaseTest.kt | 31 +++++++++++++++++++ .../active/model/YieldSupplyActiveModel.kt | 3 ++ .../YieldSupplyActiveMinAmountTransformer.kt | 6 ++-- .../impl/main/model/YieldSupplyModel.kt | 16 ++++++++-- 7 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCase.kt create mode 100644 domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCaseTest.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt index 28f00e7e98..b8c7e6dfc7 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt @@ -219,4 +219,10 @@ internal object YieldSupplyDomainModule { yieldSupplyRepository = yieldSupplyRepository, ) } + + @Provides + @Singleton + fun provideYieldSupplyGetDustMinAmountUseCase(): YieldSupplyGetDustMinAmountUseCase { + return YieldSupplyGetDustMinAmountUseCase() + } } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyExtensions.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyExtensions.kt index 4937c3c0af..09c43362de 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyExtensions.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyExtensions.kt @@ -11,9 +11,9 @@ fun CryptoCurrencyStatus.hasNotSuppliedAmount(): Boolean { return notSupplied > BigDecimal.ZERO } -fun CryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount: BigDecimal): Boolean { +fun CryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(dustAmount: BigDecimal): Boolean { val notSupplied = notSuppliedAmountOrNull() ?: return false - return notSupplied >= minAmount + return notSupplied >= dustAmount } fun CryptoCurrencyStatus.notSuppliedAmountOrNull(): BigDecimal? { diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCase.kt new file mode 100644 index 0000000000..7814d60add --- /dev/null +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCase.kt @@ -0,0 +1,20 @@ +package com.tangem.domain.yield.supply.usecase + +import com.tangem.domain.appcurrency.model.AppCurrency +import java.math.BigDecimal + +class YieldSupplyGetDustMinAmountUseCase { + + operator fun invoke(minAmount: BigDecimal, appCurrency: AppCurrency): BigDecimal { + return if (appCurrency.code in SUPPORTED_DUST_CURRENCIES) { + DUST_MIN_AMOUNT + } else { + minAmount.stripTrailingZeros() + } + } + + companion object { + private val DUST_MIN_AMOUNT = BigDecimal("0.1") + private val SUPPORTED_DUST_CURRENCIES = setOf("EUR", "USD", "AUD", "CAD", "GBP") + } +} \ No newline at end of file diff --git a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCaseTest.kt b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCaseTest.kt new file mode 100644 index 0000000000..50a00ac69c --- /dev/null +++ b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetDustMinAmountUseCaseTest.kt @@ -0,0 +1,31 @@ +package com.tangem.domain.yield.supply.usecase + +import com.google.common.truth.Truth.assertThat +import com.tangem.domain.appcurrency.model.AppCurrency +import org.junit.jupiter.api.Test +import java.math.BigDecimal + +class YieldSupplyGetDustMinAmountUseCaseTest { + + private val useCase = YieldSupplyGetDustMinAmountUseCase() + + @Test + fun `GIVEN supported currency WHEN invoke THEN return dust min amount`() { + val minAmount = BigDecimal("123.456") + val appCurrency = AppCurrency(code = "EUR", name = "Euro", symbol = "€") + + val result = useCase(minAmount, appCurrency) + + assertThat(result).isEqualTo(BigDecimal("0.1")) + } + + @Test + fun `GIVEN unsupported currency WHEN invoke THEN return min amount stripped`() { + val minAmount = BigDecimal("1.2300") + val appCurrency = AppCurrency(code = "JPY", name = "Japanese Yen", symbol = "¥") + + val result = useCase(minAmount, appCurrency) + + assertThat(result).isEqualTo(BigDecimal("1.23")) + } +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt index 8dbc13d8c2..d468e6d60d 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt @@ -58,6 +58,7 @@ internal class YieldSupplyActiveModel @Inject constructor( private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val urlOpener: UrlOpener, private val appRouter: AppRouter, + private val yieldSupplyGetDustMinAmountUseCase: YieldSupplyGetDustMinAmountUseCase, ) : Model(), YieldSupplyStopEarningComponent.ModelCallback, YieldSupplyApproveComponent.ModelCallback { @@ -241,11 +242,13 @@ internal class YieldSupplyActiveModel @Inject constructor( userWalletId, cryptoCurrencyStatusFlow.value, ).onRight { minAmount -> + val dustAmount = yieldSupplyGetDustMinAmountUseCase(minAmount = minAmount, appCurrency = appCurrency) uiState.update( YieldSupplyActiveMinAmountTransformer( cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value, appCurrency = appCurrency, minAmount = minAmount, + dustMinAmount = dustAmount, analyticsHandler = analyticsHandler, onApprove = ::onApprove, ), diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt index c3b1fd4763..54dd971067 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt @@ -28,11 +28,12 @@ import java.math.BigDecimal * - Builds the fee policy note text using the minimum amount. * - Adds contextual notifications: * - Approval required notification when spending is not yet allowed (emits analytics on CTA). - * - "Not all amount supplied" info when wallet balance exceeds the supplied balance by more than [minAmount]. + * - "Not all amount supplied" info when the not-supplied balance exceeds the dust threshold [dustMinAmount]. * * @property cryptoCurrencyStatus Current currency status used to calculate values and flags. * @property appCurrency Preferred fiat currency for formatting. * @property minAmount Protocol-required minimal amount to deposit/supply (in crypto units). + * @property dustMinAmount Threshold used to detect dust/not-supplied balance (in crypto units). * @property analyticsHandler Analytics reporter for user actions. * @property onApprove Action invoked when the "Approve" notification button is tapped. */ @@ -40,6 +41,7 @@ internal class YieldSupplyActiveMinAmountTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, private val appCurrency: AppCurrency, private val minAmount: BigDecimal, + private val dustMinAmount: BigDecimal, private val analyticsHandler: AnalyticsEventHandler, private val onApprove: () -> Unit, ) : Transformer { @@ -89,7 +91,7 @@ internal class YieldSupplyActiveMinAmountTransformer( } private fun getNotSuppliedNotification(cryptoCurrencyStatus: CryptoCurrencyStatus): NotificationUM? { - return if (cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount)) { + return if (cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(dustMinAmount)) { val cryptoCurrency = cryptoCurrencyStatus.currency val notDepositedAmount = cryptoCurrencyStatus.notSuppliedAmountOrNull() val formattedAmount = diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt index 2c16d93d51..266b8f2df4 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt @@ -1,5 +1,6 @@ package com.tangem.features.yield.supply.impl.main.model +import arrow.core.getOrElse import com.tangem.common.routing.AppRoute import android.os.SystemClock import com.tangem.common.routing.AppRoute.YieldSupplyPromo @@ -12,6 +13,8 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -28,6 +31,7 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyIsAvailableUseCase +import com.tangem.domain.yield.supply.usecase.YieldSupplyGetDustMinAmountUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase import com.tangem.features.yield.supply.api.YieldSupplyComponent import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics @@ -55,6 +59,7 @@ internal class YieldSupplyModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val analyticsEventsHandler: AnalyticsEventHandler, private val appRouter: AppRouter, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher, @@ -65,6 +70,7 @@ internal class YieldSupplyModel @Inject constructor( private val yieldSupplyDeactivateUseCase: YieldSupplyDeactivateUseCase, private val yieldSupplyRepository: YieldSupplyRepository, private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase, + private val yieldSupplyGetDustMinAmountUseCase: YieldSupplyGetDustMinAmountUseCase, ) : Model(), YieldSupplyClickIntents { private val params = paramsContainer.require() @@ -73,6 +79,7 @@ internal class YieldSupplyModel @Inject constructor( field = MutableStateFlow(YieldSupplyUM.Initial) private val cryptoCurrency = params.cryptoCurrency + private var appCurrency: AppCurrency = AppCurrency.Default var userWallet: UserWallet by Delegates.notNull() private val fetchCurrencyJobHolder = JobHolder() @@ -85,7 +92,8 @@ internal class YieldSupplyModel @Inject constructor( } private fun checkIfYieldSupplyIsAvailable() { - modelScope.launch(dispatchers.io) { + modelScope.launch(dispatchers.default) { + appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } val isAvailable = yieldSupplyIsAvailableUseCase(params.userWalletId, params.cryptoCurrency) if (isAvailable) { subscribeOnCurrencyStatusUpdates() @@ -345,7 +353,11 @@ internal class YieldSupplyModel @Inject constructor( val minAmount = yieldSupplyMinAmountUseCase(userWalletId = userWallet.walletId, cryptoCurrencyStatus) .getOrNull() if (minAmount != null) { - cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount) + val dustAmount = yieldSupplyGetDustMinAmountUseCase( + minAmount = minAmount, + appCurrency = appCurrency, + ) + cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(dustAmount) } else { false }