Updated on 2026-08-14
This commit is contained in:
parent
309c9aad6c
commit
0e1e5e3a67
20 changed files with 834 additions and 484 deletions
|
|
@ -5,7 +5,6 @@ import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|||
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
|
||||
|
|
@ -50,7 +49,6 @@ internal object ActivityModule {
|
|||
appStateHolder: AppStateHolder,
|
||||
expressServiceLoader: ExpressServiceLoader,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
onrampFeatureToggles: OnrampFeatureToggles,
|
||||
|
|
@ -61,7 +59,6 @@ internal object ActivityModule {
|
|||
sellService = Provider { requireNotNull(appStateHolder.sellService) },
|
||||
expressServiceLoader = expressServiceLoader,
|
||||
currenciesRepository = currenciesRepository,
|
||||
getNetworkCoinStatusUseCase = getNetworkCoinStatusUseCase,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
dispatchers = dispatchers,
|
||||
onrampFeatureToggles = onrampFeatureToggles,
|
||||
|
|
|
|||
|
|
@ -229,20 +229,16 @@ internal object TokensDomainModule {
|
|||
fun provideGetCryptoCurrencyActionsUseCase(
|
||||
rampStateManager: RampStateManager,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
promoRepository: PromoRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
currencyStatusOperations: BaseCurrencyStatusOperations,
|
||||
): GetCryptoCurrencyActionsUseCase {
|
||||
return GetCryptoCurrencyActionsUseCase(
|
||||
rampManager = rampStateManager,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
currenciesRepository = currenciesRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
promoRepository = promoRepository,
|
||||
dispatchers = dispatchers,
|
||||
currencyStatusOperations = currencyStatusOperations,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,9 @@ import com.tangem.domain.exchange.ExpressAvailabilityState
|
|||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
|
||||
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.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -35,7 +33,6 @@ internal class DefaultRampManager(
|
|||
private val sellService: Provider<ExchangeService>,
|
||||
private val expressServiceLoader: ExpressServiceLoader,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val onrampFeatureToggles: OnrampFeatureToggles,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
|
|
@ -56,20 +53,22 @@ internal class DefaultRampManager(
|
|||
}
|
||||
|
||||
override suspend fun availableForSell(
|
||||
userWallet: UserWallet,
|
||||
userWalletId: UserWalletId,
|
||||
status: CryptoCurrencyStatus,
|
||||
sendUnavailabilityReason: ScenarioUnavailabilityReason?,
|
||||
): Either<ScenarioUnavailabilityReason, Unit> {
|
||||
return either {
|
||||
val sellSupportedByService = catch(
|
||||
block = {
|
||||
val serviceCurrency = cryptoCurrencyConverter.convertBack(status.currency)
|
||||
|
||||
exchangeService?.availableForSell(currency = serviceCurrency) ?: false
|
||||
exchangeService?.availableForSell(currency = serviceCurrency) == true
|
||||
},
|
||||
catch = { raise(ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name)) },
|
||||
)
|
||||
|
||||
val reason = getSendUnavailabilityReason(userWallet = userWallet, cryptoCurrencyStatus = status)
|
||||
val reason = sendUnavailabilityReason
|
||||
?: getSendUnavailabilityReason(userWalletId = userWalletId, cryptoCurrencyStatus = status)
|
||||
|
||||
ensure(condition = reason is ScenarioUnavailabilityReason.None) {
|
||||
when (reason) {
|
||||
|
|
@ -125,6 +124,27 @@ internal class DefaultRampManager(
|
|||
return expressServiceLoader.getInitializationStatus(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun getSendUnavailabilityReason(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): ScenarioUnavailabilityReason {
|
||||
return when {
|
||||
cryptoCurrencyStatus.value.amount.isNullOrZero() -> {
|
||||
ScenarioUnavailabilityReason.EmptyBalance(ScenarioUnavailabilityReason.WithdrawalScenario.SEND)
|
||||
}
|
||||
currenciesRepository.isSendBlockedByPendingTransactions(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
) -> {
|
||||
ScenarioUnavailabilityReason.PendingTransaction(
|
||||
withdrawalScenario = ScenarioUnavailabilityReason.WithdrawalScenario.SEND,
|
||||
networkName = cryptoCurrencyStatus.currency.network.name,
|
||||
)
|
||||
}
|
||||
else -> ScenarioUnavailabilityReason.None
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getExchangeableState(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
@ -204,33 +224,4 @@ internal class DefaultRampManager(
|
|||
val contractAddress = (this as? CryptoCurrency.Token)?.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
return asset.network == network.backendId && asset.contractAddress.equals(contractAddress, ignoreCase = true)
|
||||
}
|
||||
|
||||
private suspend fun getSendUnavailabilityReason(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): ScenarioUnavailabilityReason {
|
||||
val coinStatus = getNetworkCoinStatusUseCase.invokeSync(
|
||||
userWallet = userWallet,
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
|
||||
).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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -235,7 +235,7 @@ internal class DefaultStakingRepository(
|
|||
)
|
||||
when {
|
||||
prefetchedYield != null && isSupportedInMobileApp -> {
|
||||
send(StakingAvailability.Available(prefetchedYield.id))
|
||||
send(StakingAvailability.Available(prefetchedYield))
|
||||
}
|
||||
prefetchedYield == null && isSupportedInMobileApp -> {
|
||||
send(StakingAvailability.TemporaryUnavailable)
|
||||
|
|
@ -279,7 +279,7 @@ internal class DefaultStakingRepository(
|
|||
|
||||
return when {
|
||||
prefetchedYield != null && isSupportedInMobileApp -> {
|
||||
StakingAvailability.Available(prefetchedYield.id)
|
||||
StakingAvailability.Available(prefetchedYield)
|
||||
}
|
||||
prefetchedYield == null && isSupportedInMobileApp -> {
|
||||
StakingAvailability.TemporaryUnavailable
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchainsdk.compatibility.getL2CompatibilityTokenComparison
|
||||
import com.tangem.blockchainsdk.utils.*
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
|
|
@ -432,9 +433,9 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun isSendBlockedByPendingTransactions(
|
||||
override suspend fun isSendBlockedByPendingTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
val blockchain = cryptoCurrencyStatus.currency.network.toBlockchain()
|
||||
val isBitcoinBlockchain = blockchain == Blockchain.Bitcoin || blockchain == Blockchain.BitcoinTestnet
|
||||
|
|
@ -445,7 +446,14 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
blockchain.isEvm() -> false
|
||||
blockchain == Blockchain.Tron || blockchain == Blockchain.TronTestnet -> false
|
||||
else -> coinStatus?.value?.hasCurrentNetworkTransactions == true
|
||||
else -> {
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
) ?: return false
|
||||
|
||||
walletManager.wallet.recentTransactions.any { it.status == TransactionStatus.Unconfirmed }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
|
@ -24,12 +23,14 @@ interface RampStateManager {
|
|||
/**
|
||||
* Check if [CryptoCurrency] is available for sell
|
||||
*
|
||||
* @param userWallet user wallet
|
||||
* @param status crypto currency status
|
||||
* @param userWalletId the ID of the user's wallet
|
||||
* @param status crypto currency status
|
||||
* @param sendUnavailabilityReason the reason why sending is unavailable or null
|
||||
*/
|
||||
suspend fun availableForSell(
|
||||
userWallet: UserWallet,
|
||||
userWalletId: UserWalletId,
|
||||
status: CryptoCurrencyStatus,
|
||||
sendUnavailabilityReason: ScenarioUnavailabilityReason?,
|
||||
): Either<ScenarioUnavailabilityReason, Unit>
|
||||
|
||||
suspend fun availableForSwap(
|
||||
|
|
@ -46,4 +47,15 @@ interface RampStateManager {
|
|||
fun getSellInitializationStatus(): Flow<Lce<Throwable, Any>>
|
||||
|
||||
fun getExpressInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, Any>>
|
||||
|
||||
/**
|
||||
* Returns the reason why sending is unavailable for the given user wallet and cryptocurrency status
|
||||
*
|
||||
* @param userWalletId the ID of the user's wallet
|
||||
* @param cryptoCurrencyStatus the status of the cryptocurrency
|
||||
*/
|
||||
suspend fun getSendUnavailabilityReason(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): ScenarioUnavailabilityReason
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
|
||||
sealed class StakingAvailability {
|
||||
|
||||
data class Available(val integrationId: String) : StakingAvailability()
|
||||
data class Available(val yield: Yield) : StakingAvailability()
|
||||
|
||||
data object Unavailable : StakingAvailability()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,463 +1,137 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.promo.models.StoryContent
|
||||
import com.tangem.domain.promo.models.StoryContentIds
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.actions.CommonActionsFactory
|
||||
import com.tangem.domain.tokens.actions.MissedDerivationsActionsFactory
|
||||
import com.tangem.domain.tokens.actions.OutdatedDataActionsFactory
|
||||
import com.tangem.domain.tokens.actions.UnreachableActionsFactory
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.requireColdWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
/**
|
||||
* Use case to determine which TokenActions are available for a [CryptoCurrency]
|
||||
* Use case for retrieving actions available for a specific cryptocurrency in a user's wallet.
|
||||
*
|
||||
* @property rampManager Ramp manager to check ramp availability
|
||||
* @param rampManager the manager for handling ramp state operations
|
||||
* @param walletManagersFacade the facade for managing wallet operations
|
||||
* @property stakingRepository the repository for staking-related data
|
||||
* @property promoRepository the repository for promotional content
|
||||
* @property dispatchers the coroutine dispatcher provider for managing concurrency
|
||||
*/
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
class GetCryptoCurrencyActionsUseCase(
|
||||
private val rampManager: RampStateManager,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
rampManager: RampStateManager,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val promoRepository: PromoRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val currencyStatusOperations: BaseCurrencyStatusOperations,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): Flow<TokenActionsState> {
|
||||
private val unreachableActionsFactory = UnreachableActionsFactory(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
rampStateManager = rampManager,
|
||||
)
|
||||
|
||||
private val outdatedDataActionsFactory = OutdatedDataActionsFactory(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
rampStateManager = rampManager,
|
||||
)
|
||||
|
||||
private val commonActionsFactory = CommonActionsFactory(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
rampStateManager = rampManager,
|
||||
)
|
||||
|
||||
operator fun invoke(userWallet: UserWallet, cryptoCurrencyStatus: CryptoCurrencyStatus): Flow<TokenActionsState> {
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
coldFlow(userWallet, cryptoCurrencyStatus)
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
TODO("[REDACTED_TASK_KEY]")
|
||||
}
|
||||
is UserWallet.Cold -> coldFlow(userWallet, cryptoCurrencyStatus)
|
||||
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun coldFlow(
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun coldFlow(
|
||||
userWallet: UserWallet.Cold,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): Flow<TokenActionsState> {
|
||||
val networkId = cryptoCurrencyStatus.currency.network.id
|
||||
val requirements = withTimeoutOrNull(REQUEST_EXCHANGE_DATA_TIMEOUT) {
|
||||
walletManagersFacade.getAssetRequirements(userWallet.walletId, cryptoCurrencyStatus.currency)
|
||||
}
|
||||
return flow {
|
||||
val networkFlow = if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
||||
currencyStatusOperations.getNetworkCoinForSingleWalletWithTokenFlow(userWallet.walletId, networkId)
|
||||
} else if (!userWallet.isMultiCurrency) {
|
||||
currencyStatusOperations.getPrimaryCurrencyStatusFlow(userWallet.walletId, includeQuotes = false)
|
||||
} else {
|
||||
currencyStatusOperations.getNetworkCoinFlow(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = networkId,
|
||||
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
|
||||
includeQuotes = false,
|
||||
)
|
||||
}
|
||||
val flow = combine(
|
||||
flow = networkFlow,
|
||||
flow2 = promoRepository.getStoryById(StoryContentIds.STORY_FIRST_TIME_SWAP.id).conflate(),
|
||||
flow3 = stakingRepository.getStakingAvailability(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
).onStart { emit(StakingAvailability.Unavailable) },
|
||||
) { maybeCoinStatus, maybeSwapStories, stakingAvailability ->
|
||||
createTokenActionsState(
|
||||
userWallet = userWallet,
|
||||
coinStatus = maybeCoinStatus.getOrNull(),
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
requirements = requirements,
|
||||
shouldShowSwapStories = maybeSwapStories != null,
|
||||
isStakingAvailable = stakingAvailability is StakingAvailability.Available,
|
||||
)
|
||||
}
|
||||
|
||||
emitAll(flow)
|
||||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private suspend fun createTokenActionsState(
|
||||
userWallet: UserWallet,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
requirements: AssetRequirementsCondition?,
|
||||
shouldShowSwapStories: Boolean,
|
||||
isStakingAvailable: Boolean,
|
||||
): TokenActionsState {
|
||||
return TokenActionsState(
|
||||
walletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
states = createListOfActions(
|
||||
userWallet = userWallet,
|
||||
coinStatus = coinStatus,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
requirements = requirements,
|
||||
shouldShowSwapStories = shouldShowSwapStories,
|
||||
isStakingAvailable = isStakingAvailable,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates list of action for expected order
|
||||
* Actions priority: [Receive Send Swap Buy Sell]
|
||||
*/
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
private suspend fun createListOfActions(
|
||||
userWallet: UserWallet,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
requirements: AssetRequirementsCondition?,
|
||||
shouldShowSwapStories: Boolean,
|
||||
isStakingAvailable: Boolean,
|
||||
): List<TokenActionsState.ActionState> {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.MissedDerivation) {
|
||||
return listOf(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Unreachable) {
|
||||
return getActionsForUnreachableCurrency(userWallet, cryptoCurrencyStatus, requirements)
|
||||
}
|
||||
|
||||
if (cryptoCurrencyStatus.value.sources.total != StatusSource.ACTUAL) {
|
||||
return getActionsForOutdatedData(userWallet, cryptoCurrencyStatus, requirements, isStakingAvailable)
|
||||
}
|
||||
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val disabledList = mutableListOf<TokenActionsState.ActionState>()
|
||||
|
||||
// markets
|
||||
// not a custom token
|
||||
if (cryptoCurrencyStatus.currency.id.rawCurrencyId != null) {
|
||||
activeList.add(TokenActionsState.ActionState.Analytics(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
|
||||
// copy address
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
|
||||
// receive
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
val scenario = getReceiveScenario(requirements)
|
||||
activeList.add(TokenActionsState.ActionState.Receive(scenario))
|
||||
}
|
||||
|
||||
// staking
|
||||
addStakingActions(cryptoCurrency, isStakingAvailable, activeList, disabledList)
|
||||
|
||||
// send
|
||||
val sendUnavailabilityReason = getSendUnavailabilityReason(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
if (sendUnavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(TokenActionsState.ActionState.Send(sendUnavailabilityReason))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Send(sendUnavailabilityReason))
|
||||
}
|
||||
|
||||
// swap
|
||||
val swapActionState = getSwapUnavailabilityReason(userWallet, cryptoCurrencyStatus, shouldShowSwapStories)
|
||||
if (swapActionState.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(swapActionState)
|
||||
} else {
|
||||
disabledList.add(swapActionState)
|
||||
}
|
||||
|
||||
// buy
|
||||
val onrampActionState = getOnrampUnavailabilityReason(userWallet, cryptoCurrencyStatus)
|
||||
if (onrampActionState.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(onrampActionState)
|
||||
} else {
|
||||
disabledList.add(onrampActionState)
|
||||
}
|
||||
|
||||
// region sell
|
||||
rampManager.availableForSell(userWallet = userWallet, status = cryptoCurrencyStatus)
|
||||
.onRight {
|
||||
activeList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
.onLeft { reason ->
|
||||
disabledList.add(TokenActionsState.ActionState.Sell(reason))
|
||||
}
|
||||
// endregion
|
||||
|
||||
// hide
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
|
||||
return activeList + disabledList
|
||||
}
|
||||
|
||||
private suspend fun addStakingActions(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
isStakingAvailable: Boolean,
|
||||
activeList: MutableList<TokenActionsState.ActionState>,
|
||||
disabledList: MutableList<TokenActionsState.ActionState>,
|
||||
) {
|
||||
if (isStakingAvailable) {
|
||||
val yield = kotlin.runCatching {
|
||||
stakingRepository.getYield(
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
}.getOrNull()
|
||||
activeList.add(
|
||||
TokenActionsState.ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.None,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.StakingUnavailable(cryptoCurrency.name),
|
||||
yield = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getActionsForUnreachableCurrency(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
requirements: AssetRequirementsCondition?,
|
||||
): List<TokenActionsState.ActionState> {
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val disabledList = mutableListOf<TokenActionsState.ActionState>()
|
||||
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
|
||||
// buy (is not depend on cache)
|
||||
val onrampActionState = getOnrampUnavailabilityReason(userWallet, cryptoCurrencyStatus)
|
||||
if (onrampActionState.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(onrampActionState)
|
||||
} else {
|
||||
disabledList.add(onrampActionState)
|
||||
}
|
||||
|
||||
disabledList.add(TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.Unreachable))
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.Unreachable,
|
||||
showBadge = false,
|
||||
),
|
||||
)
|
||||
disabledList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.Unreachable))
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
val scenario = getReceiveScenario(requirements)
|
||||
activeList.add(TokenActionsState.ActionState.Receive(scenario))
|
||||
}
|
||||
disabledList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.Unreachable, null))
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
|
||||
return activeList + disabledList
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private suspend fun getActionsForOutdatedData(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
requirements: AssetRequirementsCondition?,
|
||||
isStakingAvailable: Boolean,
|
||||
): List<TokenActionsState.ActionState> {
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val disabledList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
// copy address
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
|
||||
// receive
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
val scenario = getReceiveScenario(requirements)
|
||||
val action = TokenActionsState.ActionState.Receive(scenario)
|
||||
if (scenario == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(action)
|
||||
} else {
|
||||
disabledList.add(action)
|
||||
}
|
||||
}
|
||||
|
||||
// swap
|
||||
val sources = cryptoCurrencyStatus.value.sources
|
||||
val isSwapAvailable = with(sources) {
|
||||
quoteSource.isActual() && networkSource.isActual()
|
||||
}
|
||||
|
||||
val swapAction = TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = if (isSwapAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else if (sources.networkSource == StatusSource.ONLY_CACHE) {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
} else {
|
||||
// CACHE source always when loading
|
||||
ScenarioUnavailabilityReason.DataLoading
|
||||
},
|
||||
showBadge = false,
|
||||
)
|
||||
if (swapAction.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(swapAction)
|
||||
} else {
|
||||
disabledList.add(swapAction)
|
||||
}
|
||||
|
||||
// buy (is not depend on cache)
|
||||
val onrampActionState = getOnrampUnavailabilityReason(userWallet, cryptoCurrencyStatus)
|
||||
if (onrampActionState.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(onrampActionState)
|
||||
} else {
|
||||
disabledList.add(onrampActionState)
|
||||
}
|
||||
|
||||
// staking
|
||||
if (cryptoCurrencyStatus.value.sources.networkSource.isActual()) {
|
||||
addStakingActions(cryptoCurrency, isStakingAvailable, activeList, disabledList)
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.UsedOutdatedData, null))
|
||||
}
|
||||
|
||||
// send
|
||||
val isSendAvailable = cryptoCurrencyStatus.value.sources.networkSource.isActual()
|
||||
|
||||
val sendAction = TokenActionsState.ActionState.Send(
|
||||
unavailabilityReason = if (isSendAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
},
|
||||
)
|
||||
if (sendAction.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(sendAction)
|
||||
} else {
|
||||
disabledList.add(sendAction)
|
||||
}
|
||||
|
||||
// region sell
|
||||
if (isSendAvailable) {
|
||||
activeList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.UsedOutdatedData))
|
||||
}
|
||||
// endregion
|
||||
|
||||
// hide
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
|
||||
return activeList + disabledList
|
||||
}
|
||||
|
||||
private fun getReceiveScenario(requirements: AssetRequirementsCondition?): ScenarioUnavailabilityReason {
|
||||
return when (requirements) {
|
||||
AssetRequirementsCondition.PaidTransaction,
|
||||
is AssetRequirementsCondition.PaidTransactionWithFee,
|
||||
-> ScenarioUnavailabilityReason.UnassociatedAsset
|
||||
is AssetRequirementsCondition.IncompleteTransaction,
|
||||
null,
|
||||
-> ScenarioUnavailabilityReason.None
|
||||
is AssetRequirementsCondition.RequiredTrustline -> ScenarioUnavailabilityReason.TrustlineRequired
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSendUnavailabilityReason(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): ScenarioUnavailabilityReason {
|
||||
return when {
|
||||
cryptoCurrencyStatus.value.amount.isNullOrZero() -> {
|
||||
ScenarioUnavailabilityReason.EmptyBalance(ScenarioUnavailabilityReason.WithdrawalScenario.SEND)
|
||||
cryptoCurrencyStatus.value is CryptoCurrencyStatus.MissedDerivation -> {
|
||||
flowOf(value = MissedDerivationsActionsFactory.create())
|
||||
}
|
||||
currenciesRepository.isSendBlockedByPendingTransactions(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
) -> {
|
||||
ScenarioUnavailabilityReason.PendingTransaction(
|
||||
withdrawalScenario = ScenarioUnavailabilityReason.WithdrawalScenario.SEND,
|
||||
networkName = coinStatus?.currency?.network?.name.orEmpty(),
|
||||
cryptoCurrencyStatus.value is CryptoCurrencyStatus.Unreachable -> {
|
||||
flow {
|
||||
val actions = unreachableActionsFactory.create(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
|
||||
emit(actions)
|
||||
}
|
||||
}
|
||||
cryptoCurrencyStatus.value.sources.total != StatusSource.ACTUAL -> {
|
||||
getStakingAvailabilityFlow(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
)
|
||||
.mapLatest {
|
||||
outdatedDataActionsFactory.create(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
stakingAvailability = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
ScenarioUnavailabilityReason.None
|
||||
combine(
|
||||
flow = getStakingAvailabilityFlow(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
),
|
||||
flow2 = getSwapStoryContent(),
|
||||
) { stakingAvailability, swapStoryContent ->
|
||||
commonActionsFactory.create(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
stakingAvailability = stakingAvailability,
|
||||
shouldShowSwapStories = swapStoryContent != null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSwapUnavailabilityReason(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
shouldShowSwapStories: Boolean,
|
||||
): TokenActionsState.ActionState {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val isMultiCurrency =
|
||||
userWallet is UserWallet.Hot || userWallet is UserWallet.Cold && userWallet.isMultiCurrency
|
||||
|
||||
return if (isMultiCurrency) {
|
||||
if (cryptoCurrency.isCustom) {
|
||||
return TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.CustomToken(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
.map {
|
||||
TokenActionsState(
|
||||
walletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
states = it.toList(),
|
||||
)
|
||||
}
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.NoQuote) {
|
||||
return TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.TokenNoQuotes(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
val reason = rampManager.availableForSwap(userWallet.walletId, cryptoCurrency)
|
||||
val isShowBadge = reason == ScenarioUnavailabilityReason.None && shouldShowSwapStories
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = reason,
|
||||
showBadge = isShowBadge,
|
||||
)
|
||||
} else {
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.SingleWallet,
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
private suspend fun getOnrampUnavailabilityReason(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): TokenActionsState.ActionState {
|
||||
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
|
||||
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val reason = rampManager.availableForBuy(userWallet.scanResponse, userWallet.walletId, cryptoCurrency)
|
||||
return TokenActionsState.ActionState.Buy(unavailabilityReason = reason)
|
||||
private fun getStakingAvailabilityFlow(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): Flow<StakingAvailability> {
|
||||
return stakingRepository.getStakingAvailability(userWalletId = userWalletId, cryptoCurrency = currency)
|
||||
.onStart { emit(StakingAvailability.Unavailable) }
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun isAddressAvailable(networkAddress: NetworkAddress?): Boolean {
|
||||
return networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val REQUEST_EXCHANGE_DATA_TIMEOUT = 1000L
|
||||
private fun getSwapStoryContent(): Flow<StoryContent?> {
|
||||
return promoRepository.getStoryById(StoryContentIds.STORY_FIRST_TIME_SWAP.id)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.domain.tokens.actions
|
||||
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
|
||||
/**
|
||||
* Builder for creating a set of [TokenActionsState.ActionState] based on their availability
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class ActionAvailabilityBuilder {
|
||||
|
||||
private val activeList = mutableSetOf<TokenActionsState.ActionState>()
|
||||
private val disabledList = mutableSetOf<TokenActionsState.ActionState>()
|
||||
|
||||
/** Marks the current [TokenActionsState.ActionState] as active */
|
||||
fun TokenActionsState.ActionState.active() {
|
||||
activeList.add(this)
|
||||
}
|
||||
|
||||
/** Marks the current [TokenActionsState.ActionState] as disabled */
|
||||
fun TokenActionsState.ActionState.disabled() {
|
||||
disabledList.add(this)
|
||||
}
|
||||
|
||||
/** Marks a list of [TokenActionsState.ActionState] as disabled */
|
||||
fun List<TokenActionsState.ActionState>.disabled() {
|
||||
disabledList.addAll(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current [TokenActionsState.ActionState] to the appropriate list based on its [ScenarioUnavailabilityReason].
|
||||
*
|
||||
* If the [ScenarioUnavailabilityReason] is [ScenarioUnavailabilityReason.None], the action is added to the active list.
|
||||
* Otherwise, it is added to the disabled list.
|
||||
*/
|
||||
fun TokenActionsState.ActionState.addByReason() {
|
||||
if (unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(this)
|
||||
} else {
|
||||
disabledList.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun build(): Set<TokenActionsState.ActionState> {
|
||||
return activeList + disabledList
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function initializes an [ActionAvailabilityBuilder], applies the given
|
||||
* [block] to it, and returns the resulting set of [TokenActionsState.ActionState]
|
||||
*/
|
||||
internal suspend fun actionAvailabilityBuilder(
|
||||
block: suspend ActionAvailabilityBuilder.() -> Unit,
|
||||
): Set<TokenActionsState.ActionState> {
|
||||
val builder = ActionAvailabilityBuilder()
|
||||
|
||||
builder.block()
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
package com.tangem.domain.tokens.actions
|
||||
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
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.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
/**
|
||||
* Base factory class for creating token actions.
|
||||
*
|
||||
* This class provides utility methods to determine the availability of actions and to create specific token actions
|
||||
* based on the provided conditions.
|
||||
*
|
||||
* @param walletManagersFacade the facade for managing wallet operations
|
||||
* @param rampStateManager the manager for handling ramp state operations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal open class BaseActionsFactory(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val rampStateManager: RampStateManager,
|
||||
) {
|
||||
|
||||
/** Checks if the provided network address [networkAddress] is available */
|
||||
protected fun isAddressAvailable(networkAddress: NetworkAddress?): Boolean {
|
||||
return networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the asset requirements for a specific user wallet and cryptocurrency.
|
||||
*
|
||||
* @param userWalletId The ID of the user wallet.
|
||||
* @param currency The cryptocurrency to check.
|
||||
* @return The asset requirements condition, or `null` if the operation times out.
|
||||
*/
|
||||
protected suspend fun getAssetRequirements(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): AssetRequirementsCondition? {
|
||||
return withTimeoutOrNull(timeMillis = 1000L) {
|
||||
walletManagersFacade.getAssetRequirements(userWalletId = userWalletId, currency = currency)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the unavailability reason for the BUY action
|
||||
*
|
||||
* @param userWallet the user's cold wallet
|
||||
* @param currency the cryptocurrency to check
|
||||
*/
|
||||
protected suspend fun getOnrampUnavailabilityReason(
|
||||
userWallet: UserWallet.Cold,
|
||||
currency: CryptoCurrency,
|
||||
): ScenarioUnavailabilityReason {
|
||||
return rampStateManager.availableForBuy(
|
||||
userWalletId = userWallet.walletId,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
cryptoCurrency = currency,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the unavailability reason for the SEND action
|
||||
*
|
||||
* @param userWalletId the ID of the user's wallet
|
||||
* @param cryptoCurrencyStatus the status of the cryptocurrency
|
||||
*/
|
||||
protected suspend fun getSendUnavailabilityReason(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): ScenarioUnavailabilityReason {
|
||||
return rampStateManager.getSendUnavailabilityReason(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the unavailability reason for the SELL action
|
||||
*
|
||||
* @param userWalletId the ID of the user's wallet
|
||||
* @param status the status of the cryptocurrency
|
||||
* @param sendUnavailabilityReason the reason for unavailability of the send action
|
||||
*/
|
||||
protected suspend fun getSellUnavailabilityReason(
|
||||
userWalletId: UserWalletId,
|
||||
status: CryptoCurrencyStatus,
|
||||
sendUnavailabilityReason: ScenarioUnavailabilityReason,
|
||||
): ScenarioUnavailabilityReason {
|
||||
return rampStateManager.availableForSell(
|
||||
userWalletId = userWalletId,
|
||||
status = status,
|
||||
sendUnavailabilityReason = sendUnavailabilityReason,
|
||||
).fold(
|
||||
ifLeft = { it },
|
||||
ifRight = { ScenarioUnavailabilityReason.None },
|
||||
)
|
||||
}
|
||||
|
||||
/** Adds a "Copy Address" action to the builder if the address is available [isAddressAvailable] */
|
||||
protected fun ActionAvailabilityBuilder.addCopyAction(isAddressAvailable: Boolean) {
|
||||
if (isAddressAvailable) {
|
||||
ActionState.CopyAddress(unavailabilityReason = ScenarioUnavailabilityReason.None).active()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "Receive" action to the builder based on the address availability and asset requirements
|
||||
*
|
||||
* @param isAddressAvailable indicates whether the address is available
|
||||
* @param requirementsDeferred a deferred object containing the asset requirements condition
|
||||
*/
|
||||
protected suspend fun ActionAvailabilityBuilder.addReceiveAction(
|
||||
isAddressAvailable: Boolean,
|
||||
requirementsDeferred: Deferred<AssetRequirementsCondition?>?,
|
||||
) {
|
||||
if (isAddressAvailable && requirementsDeferred != null) {
|
||||
val scenario = getReceiveScenario(requirements = requirementsDeferred.await())
|
||||
val action = ActionState.Receive(scenario)
|
||||
|
||||
if (scenario == ScenarioUnavailabilityReason.None) {
|
||||
action.active()
|
||||
} else {
|
||||
action.disabled()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds a "Buy" action to the builder based on the unavailability [reason] */
|
||||
protected fun ActionAvailabilityBuilder.addBuyAction(reason: ScenarioUnavailabilityReason) {
|
||||
val action = ActionState.Buy(unavailabilityReason = reason)
|
||||
|
||||
if (reason == ScenarioUnavailabilityReason.None) {
|
||||
action.active()
|
||||
} else {
|
||||
action.disabled()
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds a "Hide Token" action to the builder */
|
||||
protected fun ActionAvailabilityBuilder.addHideTokenAction() {
|
||||
ActionState.HideToken(unavailabilityReason = ScenarioUnavailabilityReason.None).active()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a staking action based on the staking availability
|
||||
*
|
||||
* @param currency the cryptocurrency for staking
|
||||
* @param stakingAvailability the staking availability status
|
||||
*/
|
||||
protected fun createStakingAction(
|
||||
currency: CryptoCurrency,
|
||||
stakingAvailability: StakingAvailability,
|
||||
): ActionState.Stake {
|
||||
return if (stakingAvailability is StakingAvailability.Available) {
|
||||
ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.None,
|
||||
yield = stakingAvailability.yield,
|
||||
)
|
||||
} else {
|
||||
ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.StakingUnavailable(currency.name),
|
||||
yield = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getReceiveScenario(requirements: AssetRequirementsCondition?): ScenarioUnavailabilityReason {
|
||||
return when (requirements) {
|
||||
AssetRequirementsCondition.PaidTransaction,
|
||||
is AssetRequirementsCondition.PaidTransactionWithFee,
|
||||
-> ScenarioUnavailabilityReason.UnassociatedAsset
|
||||
is AssetRequirementsCondition.IncompleteTransaction,
|
||||
null,
|
||||
-> ScenarioUnavailabilityReason.None
|
||||
is AssetRequirementsCondition.RequiredTrustline -> ScenarioUnavailabilityReason.TrustlineRequired
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
package com.tangem.domain.tokens.actions
|
||||
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
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.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
/**
|
||||
* Factory class for creating common token actions
|
||||
*
|
||||
* @param walletManagersFacade the facade for managing wallet operations
|
||||
* @param rampStateManager the manager for handling ramp state operations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class CommonActionsFactory(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
private val rampStateManager: RampStateManager,
|
||||
) : BaseActionsFactory(walletManagersFacade, rampStateManager) {
|
||||
|
||||
/**
|
||||
* Creates a set of token actions based on the provided parameters
|
||||
*
|
||||
* @param userWallet the user's cold wallet
|
||||
* @param cryptoCurrencyStatus the status of the cryptocurrency
|
||||
* @param stakingAvailability the staking availability for the cryptocurrency
|
||||
* @param shouldShowSwapStories a flag indicating whether to show swap stories
|
||||
*/
|
||||
suspend fun create(
|
||||
userWallet: UserWallet.Cold,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
stakingAvailability: StakingAvailability,
|
||||
shouldShowSwapStories: Boolean,
|
||||
): Set<ActionState> = coroutineScope {
|
||||
val isAddressAvailable = isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)
|
||||
|
||||
val requirementsDeferred = if (isAddressAvailable) {
|
||||
async {
|
||||
getAssetRequirements(userWalletId = userWallet.walletId, currency = cryptoCurrencyStatus.currency)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val onrampUnavailabilityReasonDeferred = async {
|
||||
getOnrampUnavailabilityReason(userWallet = userWallet, currency = cryptoCurrencyStatus.currency)
|
||||
}
|
||||
|
||||
val sendUnavailabilityReasonDeferred = async {
|
||||
getSendUnavailabilityReason(userWalletId = userWallet.walletId, cryptoCurrencyStatus = cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
val swapUnavailabilityReason = if (!cryptoCurrencyStatus.currency.isCustom &&
|
||||
cryptoCurrencyStatus.value !is CryptoCurrencyStatus.NoQuote
|
||||
) {
|
||||
async {
|
||||
getSwapUnavailabilityReason(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
actionAvailabilityBuilder {
|
||||
// region Analytics
|
||||
if (cryptoCurrencyStatus.currency.id.rawCurrencyId != null) {
|
||||
ActionState.Analytics(unavailabilityReason = ScenarioUnavailabilityReason.None).active()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Copy
|
||||
addCopyAction(isAddressAvailable = isAddressAvailable)
|
||||
// endregion
|
||||
|
||||
// region Receive
|
||||
addReceiveAction(isAddressAvailable = isAddressAvailable, requirementsDeferred = requirementsDeferred)
|
||||
// endregion
|
||||
|
||||
// region Stake
|
||||
createStakingAction(currency = cryptoCurrencyStatus.currency, stakingAvailability = stakingAvailability)
|
||||
.addByReason()
|
||||
// endregion
|
||||
|
||||
val sendUnavailabilityReason = sendUnavailabilityReasonDeferred.await()
|
||||
|
||||
// region Send
|
||||
ActionState.Send(unavailabilityReason = sendUnavailabilityReason).addByReason()
|
||||
// endregion
|
||||
|
||||
// region Swap
|
||||
createSwapAction(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
swapUnavailableReasonDeferred = swapUnavailabilityReason,
|
||||
shouldShowSwapStories = shouldShowSwapStories,
|
||||
).addByReason()
|
||||
// endregion
|
||||
|
||||
// region Buy
|
||||
addBuyAction(reason = onrampUnavailabilityReasonDeferred.await())
|
||||
// endregion
|
||||
|
||||
// region Sell
|
||||
val sellUnavailabilityReason = getSellUnavailabilityReason(
|
||||
userWalletId = userWallet.walletId,
|
||||
status = cryptoCurrencyStatus,
|
||||
sendUnavailabilityReason = sendUnavailabilityReason,
|
||||
)
|
||||
|
||||
ActionState.Sell(unavailabilityReason = sellUnavailabilityReason).addByReason()
|
||||
// endregion
|
||||
|
||||
// region HideToken
|
||||
addHideTokenAction()
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createSwapAction(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
swapUnavailableReasonDeferred: Deferred<ScenarioUnavailabilityReason>?,
|
||||
shouldShowSwapStories: Boolean,
|
||||
): ActionState {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val isMultiCurrency = userWallet is UserWallet.Cold && userWallet.isMultiCurrency ||
|
||||
userWallet is UserWallet.Hot
|
||||
|
||||
if (!isMultiCurrency) {
|
||||
return ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.SingleWallet,
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
|
||||
return when {
|
||||
cryptoCurrency.isCustom -> {
|
||||
ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.CustomToken(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
cryptoCurrencyStatus.value is CryptoCurrencyStatus.NoQuote -> {
|
||||
ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.TokenNoQuotes(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val reason = swapUnavailableReasonDeferred!!.await()
|
||||
|
||||
return ActionState.Swap(
|
||||
unavailabilityReason = reason,
|
||||
showBadge = reason == ScenarioUnavailabilityReason.None && shouldShowSwapStories,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSwapUnavailabilityReason(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): ScenarioUnavailabilityReason {
|
||||
return rampStateManager.availableForSwap(userWalletId = userWalletId, cryptoCurrency = currency)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.domain.tokens.actions
|
||||
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState.ActionState
|
||||
|
||||
/**
|
||||
* Factory for creating a set of token action states for missed derivations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object MissedDerivationsActionsFactory {
|
||||
|
||||
/** Creates a set of token actions */
|
||||
fun create(): Set<ActionState> {
|
||||
val action = ActionState.HideToken(unavailabilityReason = ScenarioUnavailabilityReason.None)
|
||||
|
||||
return setOf(action)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package com.tangem.domain.tokens.actions
|
||||
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.StatusSource
|
||||
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.wallets.models.UserWallet
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
/**
|
||||
* Factory for creating a set of token action states when data is outdated
|
||||
*
|
||||
* @param walletManagersFacade the facade for managing wallet operations
|
||||
* @param rampStateManager the manager for handling ramp state operations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class OutdatedDataActionsFactory(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
rampStateManager: RampStateManager,
|
||||
) : BaseActionsFactory(walletManagersFacade, rampStateManager) {
|
||||
|
||||
/**
|
||||
* Creates a set of token actions based on the provided parameters
|
||||
*
|
||||
* @param userWallet the user's cold wallet
|
||||
* @param cryptoCurrencyStatus the status of the cryptocurrency
|
||||
* @param stakingAvailability the staking availability for the cryptocurrency
|
||||
*/
|
||||
suspend fun create(
|
||||
userWallet: UserWallet.Cold,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
stakingAvailability: StakingAvailability,
|
||||
): Set<ActionState> = coroutineScope {
|
||||
val sources = cryptoCurrencyStatus.value.sources
|
||||
|
||||
val isAddressAvailable = isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)
|
||||
|
||||
val requirementsDeferred = if (isAddressAvailable) {
|
||||
async {
|
||||
getAssetRequirements(userWalletId = userWallet.walletId, currency = cryptoCurrencyStatus.currency)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val onrampUnavailabilityReasonDeferred = async {
|
||||
getOnrampUnavailabilityReason(userWallet = userWallet, currency = cryptoCurrencyStatus.currency)
|
||||
}
|
||||
|
||||
val sendUnavailabilityReasonDeferred = if (sources.networkSource == StatusSource.ACTUAL) {
|
||||
async {
|
||||
getSendUnavailabilityReason(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
actionAvailabilityBuilder {
|
||||
// region Copy
|
||||
addCopyAction(isAddressAvailable = isAddressAvailable)
|
||||
// endregion
|
||||
|
||||
// region Receive
|
||||
addReceiveAction(isAddressAvailable = isAddressAvailable, requirementsDeferred = requirementsDeferred)
|
||||
// endregion
|
||||
|
||||
// region Swap
|
||||
createSwapAction(sources = sources).addByReason()
|
||||
// endregion
|
||||
|
||||
// region Buy
|
||||
addBuyAction(reason = onrampUnavailabilityReasonDeferred.await())
|
||||
// endregion
|
||||
|
||||
// region Stake
|
||||
if (sources.networkSource.isActual()) {
|
||||
val stakingAction = createStakingAction(
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
stakingAvailability = stakingAvailability,
|
||||
)
|
||||
|
||||
stakingAction.addByReason()
|
||||
} else {
|
||||
val stakingAction = ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.UsedOutdatedData,
|
||||
yield = null,
|
||||
)
|
||||
|
||||
stakingAction.disabled()
|
||||
}
|
||||
// endregion
|
||||
|
||||
val sendUnavailabilityReason = getSendUnavailabilityReason(
|
||||
sources = sources,
|
||||
reasonDeferred = sendUnavailabilityReasonDeferred,
|
||||
)
|
||||
|
||||
// region Send
|
||||
ActionState.Send(sendUnavailabilityReason).addByReason()
|
||||
// endregion
|
||||
|
||||
// region Sell
|
||||
if (sendUnavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
val sellUnavailabilityReason = getSellUnavailabilityReason(
|
||||
userWalletId = userWallet.walletId,
|
||||
status = cryptoCurrencyStatus,
|
||||
sendUnavailabilityReason = sendUnavailabilityReason,
|
||||
)
|
||||
|
||||
ActionState.Sell(sellUnavailabilityReason).addByReason()
|
||||
} else {
|
||||
ActionState.Sell(sendUnavailabilityReason).disabled()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region HideToken
|
||||
addHideTokenAction()
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSwapAction(sources: CryptoCurrencyStatus.Sources): ActionState {
|
||||
val isSwapAvailable = with(sources) { quoteSource.isActual() && networkSource.isActual() }
|
||||
|
||||
return ActionState.Swap(
|
||||
unavailabilityReason = when {
|
||||
isSwapAvailable -> ScenarioUnavailabilityReason.None
|
||||
sources.networkSource == StatusSource.ONLY_CACHE -> ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
else -> ScenarioUnavailabilityReason.DataLoading // CACHE source always when loading
|
||||
},
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getSendUnavailabilityReason(
|
||||
sources: CryptoCurrencyStatus.Sources,
|
||||
reasonDeferred: Deferred<ScenarioUnavailabilityReason>?,
|
||||
): ScenarioUnavailabilityReason {
|
||||
if (sources.networkSource != StatusSource.ACTUAL || reasonDeferred == null) {
|
||||
return ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
}
|
||||
|
||||
return reasonDeferred.await()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tangem.domain.tokens.actions
|
||||
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
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.wallets.models.UserWallet
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
/**
|
||||
* Factory for creating a set of unreachable token action states
|
||||
*
|
||||
* @param walletManagersFacade the facade for managing wallet operations
|
||||
* @param rampStateManager the manager for handling ramp state operations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class UnreachableActionsFactory(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
rampStateManager: RampStateManager,
|
||||
) : BaseActionsFactory(walletManagersFacade, rampStateManager) {
|
||||
|
||||
suspend fun create(userWallet: UserWallet.Cold, cryptoCurrencyStatus: CryptoCurrencyStatus): Set<ActionState> =
|
||||
coroutineScope {
|
||||
val isAddressAvailable = isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)
|
||||
|
||||
// region Deferred
|
||||
val requirementsDeferred = if (isAddressAvailable) {
|
||||
async {
|
||||
getAssetRequirements(userWalletId = userWallet.walletId, currency = cryptoCurrencyStatus.currency)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val onrampUnavailabilityReasonDeferred = async {
|
||||
getOnrampUnavailabilityReason(userWallet = userWallet, currency = cryptoCurrencyStatus.currency)
|
||||
}
|
||||
// endregion
|
||||
|
||||
actionAvailabilityBuilder {
|
||||
// region Copy
|
||||
addCopyAction(isAddressAvailable = isAddressAvailable)
|
||||
// endregion
|
||||
|
||||
// region Buy
|
||||
addBuyAction(reason = onrampUnavailabilityReasonDeferred.await())
|
||||
// endregion
|
||||
|
||||
// region Receive
|
||||
addReceiveAction(isAddressAvailable = isAddressAvailable, requirementsDeferred = requirementsDeferred)
|
||||
// endregion
|
||||
|
||||
// region Send, Swap, Sell, Stake
|
||||
listOf(
|
||||
ActionState.Send(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable),
|
||||
ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.Unreachable,
|
||||
showBadge = false,
|
||||
),
|
||||
ActionState.Sell(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable),
|
||||
ActionState.Stake(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable, yield = null),
|
||||
).disabled()
|
||||
// endregion
|
||||
|
||||
// region HideToken
|
||||
addHideTokenAction()
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,12 +223,12 @@ interface CurrenciesRepository {
|
|||
/**
|
||||
* Determines whether the currency sending is blocked by network pending transaction
|
||||
*
|
||||
* @param userWalletId the unique identifier of the user wallet
|
||||
* @param cryptoCurrencyStatus currency status
|
||||
* @param coinStatus main currency status in [cryptoCurrencyStatus] network
|
||||
*/
|
||||
fun isSendBlockedByPendingTransactions(
|
||||
suspend fun isSendBlockedByPendingTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -134,9 +134,9 @@ internal class MockCurrenciesRepository(
|
|||
return isSortedByBalance.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override fun isSendBlockedByPendingTransactions(
|
||||
override suspend fun isSendBlockedByPendingTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
private fun Lce<TokenListError, TokenList>.isInsufficientBalanceForSell(): Boolean {
|
||||
return if (params.filterOperation == OnrampOperation.SELL) {
|
||||
isContent {
|
||||
(it.totalFiatBalance as? TotalFiatBalance.Loaded)?.amount?.isZero() ?: false
|
||||
(it.totalFiatBalance as? TotalFiatBalance.Loaded)?.amount?.isZero() == true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
|
@ -235,7 +235,11 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
).isAvailable()
|
||||
}
|
||||
OnrampOperation.SELL -> {
|
||||
rampStateManager.availableForSell(userWallet = userWallet, status = status).isRight()
|
||||
rampStateManager.availableForSell(
|
||||
userWalletId = userWallet.walletId,
|
||||
status = status,
|
||||
sendUnavailabilityReason = null,
|
||||
).isRight()
|
||||
}
|
||||
OnrampOperation.SWAP -> {
|
||||
val isAvailable = rampStateManager.availableForSwap(
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private suspend fun updateButtons(currencyStatus: CryptoCurrencyStatus) {
|
||||
private fun updateButtons(currencyStatus: CryptoCurrencyStatus) {
|
||||
getCryptoCurrencyActionsUseCase(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = currencyStatus,
|
||||
|
|
|
|||
|
|
@ -11,17 +11,11 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.PrimaryCurrencySubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletButtonsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletNotificationsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.TxHistorySubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletDropDownItemsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletContentLoader(
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue