Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-05 14:57:44 +05:00
parent ede97572e3
commit 4c720113f9
11 changed files with 95 additions and 20 deletions

View file

@ -484,4 +484,10 @@ internal object TokensDomainModule {
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun provideGetAssetRequirementsUseCase(walletManagersFacade: WalletManagersFacade): GetAssetRequirementsUseCase {
return GetAssetRequirementsUseCase(walletManagersFacade)
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.transaction.models.AssetRequirementsCondition
import com.tangem.domain.walletmanager.WalletManagersFacade
class GetAssetRequirementsUseCase(
private val walletManagersFacade: WalletManagersFacade,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): Either<Throwable, AssetRequirementsCondition?> {
return Either.Companion.catch {
walletManagersFacade.getAssetRequirements(userWalletId, currency)
}
}
}

View file

@ -56,15 +56,24 @@ internal open class BaseActionsFactory(
*
* @param userWallet the user's cold wallet
* @param currency the cryptocurrency to check
* @param requirementsDeferred a deferred object containing the asset requirements condition
*/
protected suspend fun getOnrampUnavailabilityReason(
userWallet: UserWallet,
currency: CryptoCurrency,
requirementsDeferred: Deferred<AssetRequirementsCondition?>?,
): ScenarioUnavailabilityReason {
return rampStateManager.availableForBuy(
val onrampUnavailabilityReason = rampStateManager.availableForBuy(
userWallet = userWallet,
cryptoCurrency = currency,
)
val shouldCheckAssetRequirements =
onrampUnavailabilityReason == ScenarioUnavailabilityReason.None && requirementsDeferred != null
return if (shouldCheckAssetRequirements) {
getReceiveScenario(requirementsDeferred.await())
} else {
onrampUnavailabilityReason
}
}
/**

View file

@ -52,7 +52,11 @@ internal class CommonActionsFactory(
}
val onrampUnavailabilityReasonDeferred = async {
getOnrampUnavailabilityReason(userWallet = userWallet, currency = cryptoCurrencyStatus.currency)
getOnrampUnavailabilityReason(
userWallet = userWallet,
currency = cryptoCurrencyStatus.currency,
requirementsDeferred = requirementsDeferred,
)
}
val sendUnavailabilityReasonDeferred = async {

View file

@ -2,12 +2,12 @@ package com.tangem.domain.tokens.actions
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState.ActionState
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.models.wallet.UserWallet
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
@ -50,7 +50,11 @@ internal class OutdatedDataActionsFactory(
}
val onrampUnavailabilityReasonDeferred = async {
getOnrampUnavailabilityReason(userWallet = userWallet, currency = cryptoCurrencyStatus.currency)
getOnrampUnavailabilityReason(
userWallet = userWallet,
currency = cryptoCurrencyStatus.currency,
requirementsDeferred = requirementsDeferred,
)
}
val sendUnavailabilityReasonDeferred = if (sources.networkSource == StatusSource.ACTUAL) {

View file

@ -1,11 +1,11 @@
package com.tangem.domain.tokens.actions
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState.ActionState
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.models.wallet.UserWallet
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
@ -36,7 +36,11 @@ internal class UnreachableActionsFactory(
}
val onrampUnavailabilityReasonDeferred = async {
getOnrampUnavailabilityReason(userWallet = userWallet, currency = cryptoCurrencyStatus.currency)
getOnrampUnavailabilityReason(
userWallet = userWallet,
currency = cryptoCurrencyStatus.currency,
requirementsDeferred = requirementsDeferred,
)
}
// endregion

View file

@ -45,6 +45,7 @@ dependencies {
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.settings)
implementation(projects.domain.transaction.models)
/** DI */
implementation(deps.hilt.android)

View file

@ -16,11 +16,13 @@ import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.tokens.GetAssetRequirementsUseCase
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.transaction.models.AssetRequirementsCondition
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
@ -54,6 +56,7 @@ internal class OnrampTokenListModel @Inject constructor(
private val getWalletsUseCase: GetWalletsUseCase,
private val rampStateManager: RampStateManager,
private val getUserCountryUseCase: GetUserCountryUseCase,
private val getAssetRequirementsUseCase: GetAssetRequirementsUseCase,
) : Model() {
val state: StateFlow<TokenListUM> = tokenListUMController.state
@ -206,17 +209,28 @@ internal class OnrampTokenListModel @Inject constructor(
return coroutineScope {
map { status ->
async {
val isAvailable = checkAvailabilityByOperation(status = status)
val isOperationAvailable = checkAvailabilityByOperation(status = status)
val isNotMissedDerivation = status.value !is CryptoCurrencyStatus.MissedDerivation
val isNotLoading = status.value !is CryptoCurrencyStatus.Loading
val requirements = getAssetRequirementsUseCase(
userWalletId = userWallet.walletId,
currency = status.currency,
).getOrNull()
val isNotUnreachable = when (params.filterOperation) {
OnrampOperation.BUY -> true // unreachable state is available for Buy operation
OnrampOperation.SELL -> status.value !is CryptoCurrencyStatus.Unreachable
OnrampOperation.SWAP -> status.value !is CryptoCurrencyStatus.Unreachable
val isNotTrustlineRequired = requirements !is AssetRequirementsCondition.RequiredTrustline
val isNotUnreachable = status.value !is CryptoCurrencyStatus.Unreachable
val isAvailable = when (params.filterOperation) {
OnrampOperation.BUY -> {
isNotTrustlineRequired
} // unreachable state is available for Buy operation
OnrampOperation.SELL -> isNotUnreachable
OnrampOperation.SWAP -> {
isNotUnreachable && isNotTrustlineRequired
}
}
status to (isAvailable && isNotMissedDerivation && isNotLoading && isNotUnreachable)
status to (isOperationAvailable && isNotMissedDerivation && isNotLoading && isAvailable)
}
}
.awaitAll()

View file

@ -41,6 +41,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.transaction.models)
/** Data */
implementation(projects.data.common)

View file

@ -26,10 +26,11 @@ import com.tangem.datasource.crypto.DataSignatureVerifier
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.transaction.models.AssetRequirementsCondition
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.swap.converters.*
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.ExpressDataError
@ -134,7 +135,12 @@ internal class DefaultSwapRepository(
contractAddress = initialCurrency.contractAddress,
network = initialCurrency.network,
)
val currenciesList = currencyList.map { leastTokenInfoConverter.convert(it) }
val currenciesList = currencyList
.filter {
val requirements = walletManagersFacade.getAssetRequirements(userWallet.walletId, it)
requirements !is AssetRequirementsCondition.RequiredTrustline
}
.map { leastTokenInfoConverter.convert(it) }
val pairsDeferred = async {
getPairsInternal(

View file

@ -20,6 +20,8 @@ import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.tokens.*
@ -29,10 +31,9 @@ import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.models.AssetRequirementsCondition
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.ExpressDataError
@ -79,6 +80,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
private val getEthSpecificFeeUseCase: GetEthSpecificFeeUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
private val getAssetRequirementsUseCase: GetAssetRequirementsUseCase,
private val amountFormatter: AmountFormatter,
@Assisted private val userWalletId: UserWalletId,
) : SwapInteractor {
@ -139,7 +141,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
)
}
private fun getToCurrenciesGroup(
private suspend fun getToCurrenciesGroup(
currency: CryptoCurrency,
leastPairs: List<SwapPairLeast>,
cryptoCurrenciesList: List<CryptoCurrencyStatus>,
@ -171,15 +173,18 @@ internal class SwapInteractorImpl @AssistedInject constructor(
)
}
private fun findProvidersForPair(
private suspend fun findProvidersForPair(
cryptoCurrencyStatuses: CryptoCurrencyStatus,
swapPairsLeastList: List<SwapPairLeast>,
tokenInfoForAvailable: (SwapPairLeast) -> LeastTokenInfo,
): List<SwapProvider>? {
val requirements = getAssetRequirementsUseCase.invoke(userWalletId, cryptoCurrencyStatuses.currency).getOrNull()
return swapPairsLeastList.firstNotNullOfOrNull {
val listTokenInfo = tokenInfoForAvailable(it)
if (cryptoCurrencyStatuses.currency.network.backendId == listTokenInfo.network &&
cryptoCurrencyStatuses.currency.getContractAddress() == listTokenInfo.contractAddress
cryptoCurrencyStatuses.currency.getContractAddress() == listTokenInfo.contractAddress &&
requirements !is AssetRequirementsCondition.RequiredTrustline
) {
it.providers
} else {