Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-25 17:57:22 +04:00
parent 7d017162cb
commit 642ae56dc7
7 changed files with 98 additions and 20 deletions

View file

@ -4,8 +4,10 @@ import com.tangem.datasource.exchangeservice.swap.SwapServiceLoader
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
@ -45,6 +47,8 @@ internal object ActivityModule {
fun provideDefaultRampManager(
appStateHolder: AppStateHolder,
swapServiceLoader: SwapServiceLoader,
currenciesRepository: CurrenciesRepository,
getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
dispatchers: CoroutineDispatcherProvider,
): RampStateManager {
return DefaultRampManager(
@ -52,6 +56,8 @@ internal object ActivityModule {
buyService = Provider { requireNotNull(appStateHolder.buyService) },
sellService = Provider { requireNotNull(appStateHolder.sellService) },
swapServiceLoader = swapServiceLoader,
currenciesRepository = currenciesRepository,
getNetworkCoinStatusUseCase = getNetworkCoinStatusUseCase,
dispatchers = dispatchers,
)
}

View file

@ -4,23 +4,37 @@ import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRA
import com.tangem.datasource.exchangeservice.swap.SwapServiceLoader
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.isNullOrZero
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
@Suppress("LongParameterList")
internal class DefaultRampManager(
private val exchangeService: ExchangeService?,
private val buyService: Provider<ExchangeService>,
private val sellService: Provider<ExchangeService>,
private val swapServiceLoader: SwapServiceLoader,
private val currenciesRepository: CurrenciesRepository,
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
private val dispatchers: CoroutineDispatcherProvider,
) : RampStateManager {
private val cryptoCurrencyConverter = CryptoCurrencyConverter()
override fun isSellSupportedByService(cryptoCurrency: CryptoCurrency): Boolean {
return exchangeService?.availableForSell(
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
) ?: false
}
override fun availableForBuy(scanResponse: ScanResponse, cryptoCurrency: CryptoCurrency): Boolean {
return exchangeService?.availableForBuy(
scanResponse = scanResponse,
@ -28,10 +42,14 @@ internal class DefaultRampManager(
) ?: false
}
override fun availableForSell(cryptoCurrency: CryptoCurrency): Boolean {
return exchangeService?.availableForSell(
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
) ?: false
override suspend fun availableForSell(userWalletId: UserWalletId, status: CryptoCurrencyStatus): Boolean {
val sellSupportedByService = isSellSupportedByService(cryptoCurrency = status.currency)
if (!sellSupportedByService) return false
val reason = getSendUnavailabilityReason(userWalletId, status)
return reason == ScenarioUnavailabilityReason.None
}
override suspend fun availableForSwap(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean {
@ -75,4 +93,34 @@ internal class DefaultRampManager(
asset?.exchangeAvailable ?: false
}
}
private suspend fun getSendUnavailabilityReason(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): ScenarioUnavailabilityReason {
val coinStatus = getNetworkCoinStatusUseCase.invokeSync(
userWalletId = userWalletId,
networkId = cryptoCurrencyStatus.currency.network.id,
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
isSingleWalletWithTokens = false,
).getOrNull()
return when {
cryptoCurrencyStatus.value.amount.isNullOrZero() -> {
ScenarioUnavailabilityReason.EmptyBalance(ScenarioUnavailabilityReason.WithdrawalScenario.SEND)
}
currenciesRepository.isSendBlockedByPendingTransactions(
cryptoCurrencyStatus = cryptoCurrencyStatus,
coinStatus = coinStatus,
) -> {
ScenarioUnavailabilityReason.PendingTransaction(
withdrawalScenario = ScenarioUnavailabilityReason.WithdrawalScenario.SEND,
networkName = coinStatus?.currency?.network?.name.orEmpty(),
)
}
else -> {
ScenarioUnavailabilityReason.None
}
}
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.domain.exchange
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
@ -11,9 +12,18 @@ import kotlinx.coroutines.flow.Flow
*/
interface RampStateManager {
/** Check if sell service is supported for given [CryptoCurrency] */
fun isSellSupportedByService(cryptoCurrency: CryptoCurrency): Boolean
fun availableForBuy(scanResponse: ScanResponse, cryptoCurrency: CryptoCurrency): Boolean
fun availableForSell(cryptoCurrency: CryptoCurrency): Boolean
/**
* Check if [CryptoCurrency] is available for sell
*
* @param userWalletId id of multi-currency wallet
* @param status crypto currency status
*/
suspend fun availableForSell(userWalletId: UserWalletId, status: CryptoCurrencyStatus): Boolean
suspend fun availableForSwap(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean

View file

@ -189,7 +189,7 @@ class GetCryptoCurrencyActionsUseCase(
}
// sell
val sellSupportedByService = rampManager.availableForSell(cryptoCurrency)
val sellSupportedByService = rampManager.isSellSupportedByService(cryptoCurrency)
val sendAvailable = sendUnavailabilityReason is ScenarioUnavailabilityReason.None
when {

View file

@ -11,10 +11,10 @@ import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.selecttoken.ui.OnrampSelectToken
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -34,7 +34,7 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor(
params = OnrampTokenListComponent.Params(
filterOperation = when (params) {
is OnrampOperationComponent.Params.Buy -> OnrampOperation.BUY
is OnrampOperationComponent.Params.Sell -> OnrampOperation.SWAP
is OnrampOperationComponent.Params.Sell -> OnrampOperation.SELL
},
hasSearchBar = true,
userWalletId = params.userWalletId,

View file

@ -124,7 +124,6 @@ internal class UpdateTokenItemsTransformer(
-> null
}
},
onItemClick = onItemClick,
)
}

View file

@ -15,8 +15,8 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
import com.tangem.features.onramp.tokenlist.entity.TokenListUMController
import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer
@ -124,20 +124,35 @@ internal class OnrampTokenListModel @Inject constructor(
private suspend fun List<CryptoCurrencyStatus>.filterByAvailability(): Map<Boolean, List<CryptoCurrencyStatus>> {
return groupBy { status ->
val isAvailable = when (params.filterOperation) {
OnrampOperation.BUY -> {
rampStateManager.availableForBuy(scanResponse = scanResponse, cryptoCurrency = status.currency)
}
OnrampOperation.SELL -> rampStateManager.availableForSell(cryptoCurrency = status.currency)
OnrampOperation.SWAP -> rampStateManager.availableForSwap(
val isAvailable = checkAvailabilityByOperation(status = status)
val isNotMissedDerivation = status.value !is CryptoCurrencyStatus.MissedDerivation
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
}
isAvailable && isNotMissedDerivation && isNotUnreachable
}
}
private suspend fun checkAvailabilityByOperation(status: CryptoCurrencyStatus): Boolean {
return when (params.filterOperation) {
OnrampOperation.BUY -> {
rampStateManager.availableForBuy(scanResponse = scanResponse, cryptoCurrency = status.currency)
}
OnrampOperation.SELL -> {
rampStateManager.availableForSell(userWalletId = params.userWalletId, status = status)
}
OnrampOperation.SWAP -> {
val isAvailable = rampStateManager.availableForSwap(
userWalletId = params.userWalletId,
cryptoCurrency = status.currency,
)
}
isAvailable &&
status.value !is CryptoCurrencyStatus.MissedDerivation &&
status.value !is CryptoCurrencyStatus.Unreachable
isAvailable && status.value !is CryptoCurrencyStatus.NoQuote
}
}
}
}