Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-07 09:05:00 +02:00
parent daf2a6b636
commit 81a7b760ae
104 changed files with 1197 additions and 941 deletions

View file

@ -12,7 +12,7 @@ import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import com.tangem.domain.networks.utils.NetworksCleaner
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.staking.utils.StakingCleaner
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -125,14 +125,14 @@ internal object AccountStatusUseCaseModule {
fun provideCryptoCurrencyBalanceFetcher(
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
): CryptoCurrencyBalanceFetcher {
return CryptoCurrencyBalanceFetcher(
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
)

View file

@ -6,7 +6,7 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.tokens.wallet.FetchingSource
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
@ -19,7 +19,7 @@ import timber.log.Timber
*
* @property multiNetworkStatusFetcher Fetcher for updating network statuses.
* @property multiQuoteStatusFetcher Fetcher for updating quote statuses.
* @property multiYieldBalanceFetcher Fetcher for updating yield balances.
* @property multiStakingBalanceFetcher Fetcher for updating staking balances.
* @property stakingIdFactory Factory for creating staking IDs.
* @property parallelUpdatingScope Coroutine scope for parallel balance updates.
*
@ -28,7 +28,7 @@ import timber.log.Timber
class CryptoCurrencyBalanceFetcher(
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
private val stakingIdFactory: StakingIdFactory,
private val parallelUpdatingScope: CoroutineScope,
) {
@ -52,7 +52,10 @@ class CryptoCurrencyBalanceFetcher(
FetchingSource.NETWORK to refreshNetworks(userWalletId = userWalletId, currencies = currencies)
},
async {
FetchingSource.STAKING to refreshYieldBalances(userWalletId = userWalletId, currencies = currencies)
FetchingSource.STAKING to refreshStakingBalances(
userWalletId = userWalletId,
currencies = currencies,
)
},
async { FetchingSource.QUOTE to refreshQuotes(currencies = currencies) },
)
@ -85,7 +88,7 @@ class CryptoCurrencyBalanceFetcher(
)
}
private suspend fun refreshYieldBalances(
private suspend fun refreshStakingBalances(
userWalletId: UserWalletId,
currencies: List<CryptoCurrency>,
): Either<Throwable, Unit> {
@ -93,8 +96,8 @@ class CryptoCurrencyBalanceFetcher(
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
}
return multiYieldBalanceFetcher(
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
return multiStakingBalanceFetcher(
params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
)
}

View file

@ -7,7 +7,7 @@ import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.network.getAddress
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
@ -16,8 +16,8 @@ import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
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.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import com.tangem.domain.staking.single.SingleStakingBalanceSupplier
import com.tangem.domain.tokens.operations.CryptoCurrencyStatusFactory
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
@ -28,7 +28,7 @@ import javax.inject.Inject
*
* @property singleNetworkStatusSupplier Supplier for obtaining network status.
* @property singleQuoteStatusSupplier Supplier for obtaining quote status.
* @property singleYieldBalanceSupplier Supplier for obtaining yield balance.
* @property singleStakingBalanceSupplier Supplier for obtaining staking balance.
* @property stakingIdFactory Factory for creating staking IDs.
*
[REDACTED_AUTHOR]
@ -36,7 +36,7 @@ import javax.inject.Inject
internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
private val singleStakingBalanceSupplier: SingleStakingBalanceSupplier,
private val stakingIdFactory: StakingIdFactory,
) {
@ -53,7 +53,7 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
currency = currency,
maybeNetworkStatus = statusSources.networkStatus.toOption(),
maybeQuoteStatus = statusSources.quoteStatus.toOption(),
maybeYieldBalance = statusSources.yieldBalance.toOption(),
maybeStakingBalance = statusSources.stakingBalance.toOption(),
)
}
.onEmpty {
@ -71,10 +71,10 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
): Flow<CryptoCurrencyStatusSources> {
val networkStatusFlow = getNetworkStatusFlow(userWalletId = userWallet.walletId, network = currency.network)
val yieldBalanceFlow = if (userWallet.isMultiCurrency) {
val stakingBalanceFlow = if (userWallet.isMultiCurrency) {
networkStatusFlow.flatMapLatest { networkStatus ->
if (networkStatus != null) {
getYieldBalanceFlow(
getStakingBalanceFlow(
userWalletId = userWallet.walletId,
currencyId = currency.id,
networkStatus = networkStatus,
@ -89,26 +89,26 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
val quoteStatusFlow = currency.id.rawCurrencyId?.let(::getQuoteStatusFlow)
return combine(networkStatusFlow, yieldBalanceFlow, quoteStatusFlow)
return combine(networkStatusFlow, stakingBalanceFlow, quoteStatusFlow)
.distinctUntilChanged()
}
private fun combine(
networkStatusFlow: Flow<NetworkStatus?>,
yieldBalanceFlow: Flow<YieldBalance?>?,
stakingBalanceFlow: Flow<StakingBalance?>?,
quoteStatusFlow: Flow<QuoteStatus>?,
): Flow<CryptoCurrencyStatusSources> {
return when {
yieldBalanceFlow != null && quoteStatusFlow != null -> {
stakingBalanceFlow != null && quoteStatusFlow != null -> {
combine(
flow = networkStatusFlow,
flow2 = yieldBalanceFlow,
flow2 = stakingBalanceFlow,
flow3 = quoteStatusFlow,
transform = ::CryptoCurrencyStatusSources,
)
}
yieldBalanceFlow != null -> {
combine(flow = networkStatusFlow, flow2 = yieldBalanceFlow, transform = ::CryptoCurrencyStatusSources)
stakingBalanceFlow != null -> {
combine(flow = networkStatusFlow, flow2 = stakingBalanceFlow, transform = ::CryptoCurrencyStatusSources)
}
quoteStatusFlow != null -> {
combine(flow = networkStatusFlow, flow2 = quoteStatusFlow) { networkStatus, quoteStatus ->
@ -136,11 +136,11 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
.distinctUntilChanged()
}
private fun getYieldBalanceFlow(
private fun getStakingBalanceFlow(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
networkStatus: NetworkStatus,
): Flow<YieldBalance?> {
): Flow<StakingBalance?> {
val stakingId = stakingIdFactory.create(
currencyId = currencyId,
defaultAddress = networkStatus.getAddress(),
@ -148,8 +148,8 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
.getOrNull()
return if (stakingId != null) {
singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
singleStakingBalanceSupplier(
params = SingleStakingBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
)
.distinctUntilChanged()
} else {
@ -159,7 +159,7 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
private data class CryptoCurrencyStatusSources(
val networkStatus: NetworkStatus? = null,
val yieldBalance: YieldBalance? = null,
val stakingBalance: StakingBalance? = null,
val quoteStatus: QuoteStatus? = null,
)
}

View file

@ -11,7 +11,7 @@ import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
@ -20,8 +20,8 @@ import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
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.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import com.tangem.domain.staking.single.SingleStakingBalanceSupplier
import com.tangem.test.core.getEmittedValues
import io.mockk.*
import kotlinx.coroutines.flow.emptyFlow
@ -41,13 +41,13 @@ class CryptoCurrencyStatusesFlowFactoryTest {
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier = mockk()
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier = mockk()
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier = mockk()
private val singleStakingBalanceSupplier: SingleStakingBalanceSupplier = mockk()
private val stakingIdFactory: StakingIdFactory = mockk()
private val factory = CryptoCurrencyStatusesFlowFactory(
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
singleStakingBalanceSupplier = singleStakingBalanceSupplier,
stakingIdFactory = stakingIdFactory,
)
@ -63,7 +63,7 @@ class CryptoCurrencyStatusesFlowFactoryTest {
clearMocks(
singleNetworkStatusSupplier,
singleQuoteStatusSupplier,
singleYieldBalanceSupplier,
singleStakingBalanceSupplier,
stakingIdFactory,
)
}
@ -96,13 +96,13 @@ class CryptoCurrencyStatusesFlowFactoryTest {
stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
} returns stakingId.right()
val yieldBalance = YieldBalance.Empty(stakingId = stakingId, source = StatusSource.ACTUAL)
val yieldBalanceFlow = flowOf(yieldBalance)
val stakingBalance = StakingBalance.Empty(stakingId = stakingId, source = StatusSource.ACTUAL)
val stakingBalanceFlow = flowOf(stakingBalance)
every {
singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
singleStakingBalanceSupplier(
params = SingleStakingBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
)
} returns yieldBalanceFlow
} returns stakingBalanceFlow
// Act
val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
@ -121,7 +121,7 @@ class CryptoCurrencyStatusesFlowFactoryTest {
coVerify(ordering = Ordering.SEQUENCE) {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
singleYieldBalanceSupplier(params = SingleYieldBalanceProducer.Params(userWalletId, stakingId))
singleStakingBalanceSupplier(params = SingleStakingBalanceProducer.Params(userWalletId, stakingId))
}
}

View file

@ -44,7 +44,7 @@ internal fun createStatus(currency: CryptoCurrency, fiatAmount: BigDecimal): Cry
fiatRate = BigDecimal.ONE,
fiatAmount = fiatAmount,
priceChange = BigDecimal.ZERO,
yieldBalance = null,
stakingBalance = null,
hasCurrentNetworkTransactions = false,
yieldSupplyStatus = null,
pendingTransactions = emptySet(),