Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-12 12:33:04 +04:00
parent 07f94d3a95
commit a2ec212c2e
18 changed files with 156 additions and 307 deletions

View file

@ -3,18 +3,15 @@ package com.tangem.tap.di.domain
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import com.tangem.domain.networks.repository.NetworksRepository
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceSupplier
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceSupplier
@ -73,15 +70,6 @@ internal object TokensDomainModule {
)
}
@Provides
@Singleton
fun provideGetCryptoCurrencyUseCase(
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
): GetCryptoCurrencyUseCase {
return GetCryptoCurrencyUseCase(currenciesRepository, multiWalletCryptoCurrenciesSupplier)
}
@Provides
@Singleton
fun provideGetCryptoCurrencyActionsUseCase(
@ -199,23 +187,17 @@ internal object TokensDomainModule {
@Singleton
fun provideBaseCurrencyStatusOperations(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
singleStakingBalanceSupplier: SingleStakingBalanceSupplier,
multiStakingBalanceSupplier: MultiStakingBalanceSupplier,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): BaseCurrencyStatusOperations {
return BaseCurrencyStatusOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleStakingBalanceSupplier = singleStakingBalanceSupplier,
multiStakingBalanceSupplier = multiStakingBalanceSupplier,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
)

View file

@ -1,10 +1,10 @@
package com.tangem.tap.di.domain
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.blockaid.BlockAidGasEstimate
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.error.FeeErrorResolver
import com.tangem.domain.yield.supply.YieldSupplyErrorResolver
@ -144,12 +144,12 @@ internal object YieldSupplyDomainModule {
fun provideYieldSupplyMinAmountUseCase(
feeRepository: FeeRepository,
quotesRepository: QuotesRepository,
currenciesRepository: CurrenciesRepository,
singleAccountListSupplier: SingleAccountListSupplier,
): YieldSupplyMinAmountUseCase {
return YieldSupplyMinAmountUseCase(
feeRepository = feeRepository,
quotesRepository = quotesRepository,
currenciesRepository = currenciesRepository,
singleAccountListSupplier = singleAccountListSupplier,
)
}
@ -158,12 +158,12 @@ internal object YieldSupplyDomainModule {
fun provideYieldSupplyGetCurrentFeeUseCase(
feeRepository: FeeRepository,
quotesRepository: QuotesRepository,
currenciesRepository: CurrenciesRepository,
singleAccountListSupplier: SingleAccountListSupplier,
): YieldSupplyGetCurrentFeeUseCase {
return YieldSupplyGetCurrentFeeUseCase(
feeRepository = feeRepository,
quotesRepository = quotesRepository,
currenciesRepository = currenciesRepository,
singleAccountListSupplier = singleAccountListSupplier,
)
}
@ -172,12 +172,12 @@ internal object YieldSupplyDomainModule {
fun provideYieldSupplyGetMaxFeeUseCase(
yieldSupplyRepository: YieldSupplyRepository,
quotesRepository: QuotesRepository,
currenciesRepository: CurrenciesRepository,
singleAccountListSupplier: SingleAccountListSupplier,
): YieldSupplyGetMaxFeeUseCase {
return YieldSupplyGetMaxFeeUseCase(
yieldSupplyRepository = yieldSupplyRepository,
quotesRepository = quotesRepository,
currenciesRepository = currenciesRepository,
singleAccountListSupplier = singleAccountListSupplier,
)
}

View file

@ -12,7 +12,6 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.token.TokenReceiveWarningActionStore
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.express.ExpressServiceFetcher
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository
@ -40,7 +39,6 @@ internal object TokensDataModule {
expressServiceFetcher: ExpressServiceFetcher,
excludedBlockchains: ExcludedBlockchains,
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
): CurrenciesRepository {
return DefaultCurrenciesRepository(
tangemTechApi = tangemTechApi,
@ -51,7 +49,6 @@ internal object TokensDataModule {
dispatchers = dispatchers,
excludedBlockchains = excludedBlockchains,
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
)
}

View file

@ -25,8 +25,6 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.model.FeePaidCurrency
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
@ -46,7 +44,6 @@ internal class DefaultCurrenciesRepository(
private val expressServiceFetcher: ExpressServiceFetcher,
private val dispatchers: CoroutineDispatcherProvider,
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
excludedBlockchains: ExcludedBlockchains,
) : CurrenciesRepository {
@ -121,23 +118,6 @@ internal class DefaultCurrenciesRepository(
}
}
override suspend fun getNetworkCoin(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency.Coin {
return multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId),
)
.orEmpty()
.find { currency ->
currency is CryptoCurrency.Coin &&
currency.network.id.rawId == networkId.rawId &&
currency.network.derivationPath == derivationPath
} as? CryptoCurrency.Coin
?: error("Unable to find coin for network ID: $networkId")
}
override suspend fun isSendBlockedByPendingTransactions(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,

View file

@ -255,7 +255,7 @@ internal object AccountCryptoCurrencyStatusFinder {
// region AccountList helpers
private fun AccountList.getExpectedAccounts(network: Network?): List<Account> {
internal fun AccountList.getExpectedAccounts(network: Network?): List<Account> {
return getExpectedAccounts(rawNetworkId = network?.rawId, derivationPath = network?.derivationPath)
}

View file

@ -1,12 +1,17 @@
package com.tangem.domain.account.status.utils
import arrow.core.None
import arrow.core.Option
import arrow.core.raise.catch
import arrow.core.raise.option
import arrow.core.toOption
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyOperations.getAccountCryptoCurrency
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusFinder.getExpectedAccounts
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import timber.log.Timber
/**
* Extension functions for retrieving [CryptoCurrency] from an [AccountList] or [Account.CryptoPortfolio].
@ -48,6 +53,33 @@ object CryptoCurrencyOperations {
return getCryptoCurrency(currencyId = cryptoCurrency.id, network = cryptoCurrency.network)
}
fun AccountList?.getCryptoCurrency(currencyIdValue: String): Option<CryptoCurrency> = option {
val currencyId = catch(
block = { CryptoCurrency.ID.fromValue(currencyIdValue) },
catch = { throwable ->
Timber.e("Error on converting currencyId: $throwable")
raise(None)
},
)
return getCryptoCurrency(currencyId = currencyId, network = null)
}
fun AccountList.getCoin(currency: CryptoCurrency): Option<CryptoCurrency.Coin> {
return getCoin(network = currency.network)
}
fun AccountList.getCoin(network: Network): Option<CryptoCurrency.Coin> {
return getExpectedAccounts(network = network)
.flatMap { account ->
(account as? Account.CryptoPortfolio)
?.cryptoCurrencies.orEmpty()
.filterIsInstance<CryptoCurrency.Coin>()
}
.firstOrNull { currency -> currency.network.id == network.id }
.toOption()
}
/**
* Retrieves the [CryptoCurrency] for the specified [currencyId] and [network] from this [AccountList].
*

View file

@ -1,73 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.repository.CurrenciesRepository
class GetCryptoCurrencyUseCase(
private val currenciesRepository: CurrenciesRepository,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
) {
/**
* Returns specific cryptocurrency for a given user wallet.
*
* !!! Important Use only [CryptoCurrency.ID.value] as cryptoCurrencyId
*
* @param userWallet The user's wallet.
* @param cryptoCurrencyId The ID of the cryptocurrency.
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
*/
suspend operator fun invoke(
userWallet: UserWallet,
cryptoCurrencyId: String,
): Either<CurrencyStatusError, CryptoCurrency> {
return either {
if (userWallet.isMultiCurrency) {
getCurrency(userWallet.walletId, cryptoCurrencyId)
} else {
getPrimaryCurrency(userWallet.walletId)
}
}
}
/**
* Returns the primary cryptocurrency for a given user wallet.
*
* @param userWalletId The ID of the user's wallet.
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
*/
suspend operator fun invoke(userWalletId: UserWalletId): Either<CurrencyStatusError, CryptoCurrency> {
return either { getPrimaryCurrency(userWalletId) }
}
private suspend fun Raise<CurrencyStatusError>.getCurrency(
userWalletId: UserWalletId,
id: String,
): CryptoCurrency {
return catch(
block = {
multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId),
)
?.firstOrNull { it.id.value == id }
?: error("Unable to find currency with ID: $id")
},
catch = { raise(CurrencyStatusError.DataError(it)) },
)
}
private suspend fun Raise<CurrencyStatusError>.getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {
return catch(
block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) },
catch = { raise(CurrencyStatusError.DataError(it)) },
)
}
}

View file

@ -1,22 +1,19 @@
package com.tangem.domain.tokens.operations
import arrow.core.*
import arrow.core.Either
import arrow.core.left
import arrow.core.raise.*
import arrow.core.right
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.multi.MultiNetworkStatusProducer
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import com.tangem.domain.networks.single.SingleNetworkStatusProducer
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.domain.staking.multi.MultiStakingBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import com.tangem.domain.staking.single.SingleStakingBalanceSupplier
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
@ -36,12 +33,9 @@ import kotlinx.coroutines.flow.firstOrNull
@Suppress("LargeClass", "LongParameterList")
class BaseCurrencyStatusOperations(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
private val singleStakingBalanceSupplier: SingleStakingBalanceSupplier,
private val multiStakingBalanceSupplier: MultiStakingBalanceSupplier,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
) {
@ -105,42 +99,6 @@ class BaseCurrencyStatusOperations(
}
}
suspend fun getCurrenciesStatusesSync(userWalletId: UserWalletId): Either<Error, List<CryptoCurrencyStatus>> {
return either {
catch(
block = {
val nonEmptyCurrencies = multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId),
)
?.toNonEmptyListOrNull()
?: return emptyList<CryptoCurrencyStatus>().right()
val (_, currenciesIds) = getIds(nonEmptyCurrencies)
val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet()
val quotes = quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = rawIds)?.right()
val networkStatuses = multiNetworkStatusSupplier(
params = MultiNetworkStatusProducer.Params(userWalletId = userWalletId),
)
.firstOrNull()
.orEmpty()
.right()
val stakingBalances = getStakingBalancesSync(userWalletId, nonEmptyCurrencies)
return currencyStatusProxyCreator.createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
maybeQuotes = quotes,
maybeNetworkStatuses = networkStatuses,
maybeStakingBalances = stakingBalances,
)
},
catch = { raise(Error.DataError(it)) },
)
}
}
private suspend fun Raise<Error>.getMultiCurrencyWalletCurrency(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
@ -173,28 +131,6 @@ class BaseCurrencyStatusOperations(
.bind()
}
private suspend fun getStakingBalancesSync(
userWalletId: UserWalletId,
cryptoCurrencies: List<CryptoCurrency>,
): Either<Error.EmptyStakingBalances, List<StakingBalance>> = either {
val stakingIds = cryptoCurrencies.mapNotNull { cryptoCurrency ->
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrency)
.getOrNull()
}
ensure(stakingIds.isNotEmpty()) { Error.EmptyStakingBalances }
val balances = multiStakingBalanceSupplier.getSyncOrNull(
params = MultiStakingBalanceProducer.Params(userWalletId = userWalletId),
)
.orEmpty()
.filter { it.stakingId in stakingIds }
ensure(balances.isNotEmpty()) { Error.EmptyStakingBalances }
balances
}
private suspend fun getStakingBalanceSync(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
@ -215,17 +151,4 @@ class BaseCurrencyStatusOperations(
ensureNotNull(yieldBalance) { Error.EmptyStakingBalances }
}
private fun getIds(currencies: List<CryptoCurrency>): Pair<NonEmptySet<Network>, NonEmptySet<CryptoCurrency.ID>> {
val currencyIdToNetworkId = currencies.associate { currency ->
currency.id to currency.network
}
val currenciesIds = currencyIdToNetworkId.keys.toNonEmptySetOrNull()
val networks = currencyIdToNetworkId.values.toNonEmptySetOrNull()
requireNotNull(currenciesIds) { "Currencies IDs cannot be empty" }
requireNotNull(networks) { "Networks IDs cannot be empty" }
return networks to currenciesIds
}
}

View file

@ -57,19 +57,6 @@ interface CurrenciesRepository {
id: CryptoCurrency.ID,
): CryptoCurrency
/**
* Get the coin for a specific network.
*
* @param userWalletId The unique identifier of the user wallet.
* @param networkId The unique identifier of the network.
* @param derivationPath currency derivation path.
*/
suspend fun getNetworkCoin(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency.Coin
/**
* Determines whether the currency sending is blocked by network pending transaction
*

View file

@ -17,6 +17,7 @@ dependencies {
implementation(projects.core.ui)
/** Domain */
implementation(projects.domain.account.status)
implementation(projects.domain.models)
implementation(projects.domain.yieldSupply.models)
implementation(projects.domain.transaction.models)

View file

@ -2,13 +2,15 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import arrow.core.Either.Companion.catch
import arrow.core.getOrElse
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCoin
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.yield.supply.YieldSupplyConst.YIELD_SUPPLY_EVM_CONSTANT_GAS_LIMIT
import com.tangem.domain.yield.supply.fixFee
@ -22,7 +24,7 @@ import java.math.RoundingMode
class YieldSupplyGetCurrentFeeUseCase(
private val feeRepository: FeeRepository,
private val quotesRepository: QuotesRepository,
private val currenciesRepository: CurrenciesRepository,
private val singleAccountListSupplier: SingleAccountListSupplier,
) {
suspend operator fun invoke(
@ -34,11 +36,13 @@ class YieldSupplyGetCurrentFeeUseCase(
val fiatRate = cryptoCurrencyStatus.value.fiatRate ?: error("Fiat rate is missing")
require(fiatRate > BigDecimal.ZERO) { "Fiat rate for token must be > 0" }
val nativeCryptoCurrency = currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = cryptoCurrencyStatus.currency.network.id,
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
)
val accountStatusList = singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
?: error("Account status list is missing: $userWalletId")
val nativeCryptoCurrency = accountStatusList.getCoin(cryptoCurrencyStatus.currency)
.getOrElse {
error("Unable to find coin for network ID: ${cryptoCurrencyStatus.currency.network.id}")
}
val quotes =
quotesRepository.getMultiQuoteSyncOrNull(setOfNotNull(nativeCryptoCurrency.id.rawCurrencyId))

View file

@ -2,13 +2,15 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import arrow.core.Either.Companion.catch
import arrow.core.getOrElse
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCoin
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.currency.yieldSupplyKey
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldSupplyMaxFee
import java.math.BigDecimal
@ -28,7 +30,7 @@ import java.math.RoundingMode
class YieldSupplyGetMaxFeeUseCase(
private val yieldSupplyRepository: YieldSupplyRepository,
private val quotesRepository: QuotesRepository,
private val currenciesRepository: CurrenciesRepository,
private val singleAccountListSupplier: SingleAccountListSupplier,
) {
suspend operator fun invoke(
@ -41,11 +43,13 @@ class YieldSupplyGetMaxFeeUseCase(
val fiatRate = cryptoCurrencyStatus.value.fiatRate ?: error("Fiat rate is missing")
require(fiatRate > BigDecimal.ZERO) { "Fiat rate for token must be > 0" }
val nativeCryptoCurrency = currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = cryptoCurrencyStatus.currency.network.id,
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
)
val accountStatusList = singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
?: error("Account status list is missing: $userWalletId")
val nativeCryptoCurrency = accountStatusList.getCoin(cryptoCurrencyStatus.currency)
.getOrElse {
error("Unable to find coin for network ID: ${cryptoCurrencyStatus.currency.network.id}")
}
val quotes =
quotesRepository.getMultiQuoteSyncOrNull(setOfNotNull(nativeCryptoCurrency.id.rawCurrencyId))

View file

@ -2,11 +2,13 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import arrow.core.Either.Companion.catch
import arrow.core.getOrElse
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCoin
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.yield.supply.YieldSupplyConst.YIELD_SUPPLY_EVM_CONSTANT_GAS_LIMIT
import com.tangem.domain.yield.supply.fixFee
@ -24,7 +26,7 @@ import java.math.RoundingMode
class YieldSupplyMinAmountUseCase(
private val feeRepository: FeeRepository,
private val quotesRepository: QuotesRepository,
private val currenciesRepository: CurrenciesRepository,
private val singleAccountListSupplier: SingleAccountListSupplier,
) {
suspend operator fun invoke(
@ -36,11 +38,13 @@ class YieldSupplyMinAmountUseCase(
val fiatRate = cryptoCurrencyStatus.value.fiatRate ?: error("Fiat rate is missing")
require(fiatRate > BigDecimal.ZERO) { "Fiat rate for token must be > 0" }
val nativeCryptoCurrency = currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = cryptoCurrencyStatus.currency.network.id,
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
)
val accountStatusList = singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
?: error("Account status list is missing: $userWalletId")
val nativeCryptoCurrency = accountStatusList.getCoin(cryptoCurrencyStatus.currency)
.getOrElse {
error("Unable to find coin for network ID: ${cryptoCurrencyStatus.currency.network.id}")
}
val quotes =
quotesRepository.getMultiQuoteSyncOrNull(setOfNotNull(nativeCryptoCurrency.id.rawCurrencyId))

View file

@ -4,6 +4,8 @@ import com.google.common.truth.Truth
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@ -13,7 +15,6 @@ 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.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase
import io.mockk.coEvery
@ -29,12 +30,12 @@ class YieldSupplyMinAmountUseCaseTest {
private val feeRepository: FeeRepository = mockk(relaxed = true)
private val quotesRepository: QuotesRepository = mockk(relaxed = true)
private val currenciesRepository: CurrenciesRepository = mockk(relaxed = true)
private val singleAccountListSupplier: SingleAccountListSupplier = mockk(relaxed = true)
private val useCase = YieldSupplyMinAmountUseCase(
feeRepository = feeRepository,
quotesRepository = quotesRepository,
currenciesRepository = currenciesRepository,
singleAccountListSupplier = singleAccountListSupplier,
)
@Test
@ -67,12 +68,11 @@ class YieldSupplyMinAmountUseCaseTest {
coEvery { feeRepository.getEthereumFeeWithoutGas(userWallet.walletId, token) } returns fee
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWallet.walletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWallet.walletId)
} returns AccountList.empty(
userWalletId = userWallet.walletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
val nativeFiatRate = BigDecimal("0.20353756561552608")
coEvery {
@ -147,12 +147,11 @@ class YieldSupplyMinAmountUseCaseTest {
coEvery { feeRepository.getEthereumFeeWithoutGas(userWallet.walletId, token) } returns fee
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWallet.walletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWallet.walletId)
} returns AccountList.empty(
userWalletId = userWallet.walletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
coEvery {
quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))

View file

@ -4,6 +4,8 @@ import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@ -12,7 +14,6 @@ import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.utils.convertToSdkAmount
import io.mockk.coEvery
@ -30,7 +31,7 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
private val feeRepository: FeeRepository = mockk()
private val quotesRepository: QuotesRepository = mockk()
private val currenciesRepository: CurrenciesRepository = mockk()
private val singleAccountListSupplier: SingleAccountListSupplier = mockk()
private lateinit var useCase: YieldSupplyGetCurrentFeeUseCase
@ -41,7 +42,7 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
useCase = YieldSupplyGetCurrentFeeUseCase(
feeRepository = feeRepository,
quotesRepository = quotesRepository,
currenciesRepository = currenciesRepository,
singleAccountListSupplier = singleAccountListSupplier,
)
}
@ -65,12 +66,11 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns feeWithoutGas
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
} returns AccountList.empty(
userWalletId = userWalletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
coEvery {
quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))
} returns setOf(
@ -117,12 +117,11 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns feeWithoutGas
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
} returns AccountList.empty(
userWalletId = userWalletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
coEvery {
quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))
} returns setOf(
@ -186,12 +185,11 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
)
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
} returns AccountList.empty(
userWalletId = userWalletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
coEvery { quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!)) } returns null
val result = useCase(userWalletId, cryptoStatus)
@ -216,12 +214,11 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
)
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
} returns AccountList.empty(
userWalletId = userWalletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
coEvery { quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!)) } returns emptySet()
val result = useCase(userWalletId, cryptoStatus)
@ -246,12 +243,11 @@ class YieldSupplyGetCurrentFeeUseCaseTest {
amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
)
coEvery {
currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = token.network.id,
derivationPath = token.network.derivationPath,
)
} returns nativeCoin
singleAccountListSupplier.getSyncOrNull(userWalletId = userWalletId)
} returns AccountList.empty(
userWalletId = userWalletId,
cryptoCurrencies = listOf(nativeCoin, token),
)
coEvery {
quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))
} returns setOf(

View file

@ -1,6 +1,7 @@
package com.tangem.features.onramp.success.model
import arrow.core.getOrElse
import arrow.core.toOption
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -11,6 +12,8 @@ import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCryptoCurrency
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.GetOnrampStatusUseCase
@ -20,7 +23,6 @@ import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
import com.tangem.domain.onramp.model.OnrampStatus
import com.tangem.domain.onramp.model.cache.OnrampTransaction
import com.tangem.domain.onramp.model.error.OnrampError
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.onramp.component.OnrampSuccessComponent
@ -48,7 +50,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
private val urlOpener: UrlOpener,
private val getOnrampTransactionUseCase: GetOnrampTransactionUseCase,
private val getOnrampStatusUseCase: GetOnrampStatusUseCase,
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val singleAccountListSupplier: SingleAccountListSupplier,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -97,14 +99,15 @@ internal class OnrampSuccessComponentModel @Inject constructor(
showErrorAlert(OnrampError.DomainError("UserWallet not found"))
return@launch
}
cryptoCurrency = getCryptoCurrencyUseCase(
userWallet = userWallet,
cryptoCurrencyId = transaction.toCurrencyId,
).getOrElse {
Timber.e("Crypto currency not found")
showErrorAlert(OnrampError.DomainError(null))
return@launch
}
cryptoCurrency = singleAccountListSupplier.getSyncOrNull(transaction.userWalletId)
?.getCryptoCurrency(currencyIdValue = transaction.toCurrencyId)?.getOrNull()
.toOption()
.getOrElse {
Timber.e("Crypto currency not found")
showErrorAlert(OnrampError.DomainError(null))
return@launch
}
startStatusUpdateTask(transaction)
},

View file

@ -1,11 +1,16 @@
package com.tangem.features.send.v2.deeplink
import arrow.core.Option
import arrow.core.getOrElse
import arrow.core.raise.option
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.ui.utils.parseBigDecimalOrNull
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCryptoCurrency
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler
import dagger.assisted.Assisted
@ -21,7 +26,7 @@ internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
@Assisted queryParams: Map<String, String>,
appRouter: AppRouter,
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val singleAccountListSupplier: SingleAccountListSupplier,
) : SellRedirectDeepLinkHandler {
init {
@ -51,8 +56,8 @@ internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
}
scope.launch {
val cryptoCurrency = getCryptoCurrencyUseCase(userWallet, currencyId).getOrElse { error ->
Timber.e("Error on getting cryptoCurrency: $error")
val cryptoCurrency = getCryptoCurrency(userWallet.walletId, currencyId).getOrElse {
Timber.e("Error on getting cryptoCurrency: $currencyId")
return@launch
}
// Convert using universal parser to account for regional separators
@ -72,6 +77,15 @@ internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
)
}
private suspend fun getCryptoCurrency(userWalletId: UserWalletId, currencyId: String): Option<CryptoCurrency> =
option {
val accountStatusList = singleAccountListSupplier.getSyncOrNull(userWalletId)
ensureNotNull(accountStatusList)
return accountStatusList.getCryptoCurrency(currencyIdValue = currencyId)
}
@AssistedFactory
interface Factory : SellRedirectDeepLinkHandler.Factory {
override fun create(

View file

@ -9,6 +9,7 @@ import com.tangem.common.routing.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
@ -18,9 +19,6 @@ import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.notifications.models.NotificationType
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
@ -42,7 +40,6 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
@Assisted private val isFromOnNewIntent: Boolean,
private val appRouter: AppRouter,
private val selectWalletUseCase: SelectWalletUseCase,
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val tokenDetailsDeepLinkActionTrigger: TokenDetailsDeepLinkActionTrigger,
private val walletDeepLinkActionTrigger: WalletDeepLinkActionTrigger,
@ -50,7 +47,7 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
private val getUserWalletUseCase: GetUserWalletUseCase,
private val walletBalanceFetcher: WalletBalanceFetcher,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val singleAccountListSupplier: SingleAccountListSupplier,
) : TokenDetailsDeepLinkHandler {
init {
@ -152,13 +149,12 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
isNetwork && isCurrency && isCorrectDerivation
}
} else {
getCryptoCurrencyUseCase(userWalletId = userWallet.walletId).getOrNull()
singleAccountListSupplier.getSyncOrNull(userWalletId = userWallet.walletId)
?.mainAccount?.cryptoCurrencies?.first()
}
private suspend fun getCryptoCurrencies(userWalletId: UserWalletId): List<CryptoCurrency>? {
return multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId),
)?.toList()
return singleAccountListSupplier.getSyncOrNull(userWalletId)?.flattenCurrencies()
}
@AssistedFactory