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

@ -6,7 +6,7 @@ import com.tangem.domain.managetokens.repository.ManageTokensRepository
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.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository
@ -74,7 +74,7 @@ internal object ManageTokensDomainModule {
derivationsRepository: DerivationsRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
): SaveManagedTokensUseCase {
@ -85,7 +85,7 @@ internal object ManageTokensDomainModule {
derivationsRepository = derivationsRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
)

View file

@ -10,7 +10,7 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.settings.repositories.SettingsRepository
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.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository
@ -70,7 +70,7 @@ object MarketsDomainModule {
currenciesRepository: CurrenciesRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
): SaveMarketTokensUseCase {
@ -81,7 +81,7 @@ object MarketsDomainModule {
currenciesRepository = currenciesRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
)

View file

@ -7,8 +7,8 @@ import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakeKitRepository
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitTransactionHashRepository
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.staking.toggles.StakingFeatureToggles
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import dagger.Module
@ -113,11 +113,11 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideFetchStakingYieldBalanceUseCase(
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
singleStakingBalanceFetcher: SingleStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
): FetchStakingYieldBalanceUseCase {
return FetchStakingYieldBalanceUseCase(
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
singleStakingBalanceFetcher = singleStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
)
}

View file

@ -12,11 +12,11 @@ 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.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
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.SingleYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceSupplier
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
@ -46,7 +46,7 @@ internal object TokensDomainModule {
walletManagersFacade: WalletManagersFacade,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
singleStakingBalanceFetcher: SingleStakingBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): AddCryptoCurrenciesUseCase {
@ -55,7 +55,7 @@ internal object TokensDomainModule {
walletManagersFacade = walletManagersFacade,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
singleStakingBalanceFetcher = singleStakingBalanceFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
)
@ -153,7 +153,7 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
singleStakingBalanceFetcher: SingleStakingBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): FetchCurrencyStatusUseCase {
@ -161,7 +161,7 @@ internal object TokensDomainModule {
currenciesRepository = currenciesRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
singleStakingBalanceFetcher = singleStakingBalanceFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
)
@ -344,8 +344,8 @@ internal object TokensDomainModule {
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
singleStakingBalanceSupplier: SingleStakingBalanceSupplier,
multiStakingBalanceSupplier: MultiStakingBalanceSupplier,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): BaseCurrencyStatusOperations {
@ -355,8 +355,8 @@ internal object TokensDomainModule {
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
multiYieldBalanceSupplier = multiYieldBalanceSupplier,
singleStakingBalanceSupplier = singleStakingBalanceSupplier,
multiStakingBalanceSupplier = multiStakingBalanceSupplier,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
)
@ -376,7 +376,7 @@ internal object TokensDomainModule {
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
): WalletBalanceFetcher {
@ -386,7 +386,7 @@ internal object TokensDomainModule {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
dispatchers = dispatchers,
)

View file

@ -21,7 +21,7 @@ import com.tangem.domain.models.StatusSource
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.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.model.isStakingSupported
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
@ -159,7 +159,7 @@ class TokenItemStateConverter(
return totalAmount.format { crypto(currency) }
}
private fun CryptoCurrencyStatus.getStakedBalance() = (value.yieldBalance as? YieldBalance.Data)
private fun CryptoCurrencyStatus.getStakedBalance() = (value.stakingBalance as? StakingBalance.Data)
?.getTotalWithRewardsStakingBalance(blockchainId = currency.network.rawId).orZero()
private fun createTitleState(
@ -264,12 +264,13 @@ class TokenItemStateConverter(
val validators = stakingApyMap[stakingKey]
?: return StakingLocalInfo(rate = null, isActive = false, rewardType = null)
val yieldBalance = currencyStatus.value.yieldBalance
val hasStakedBalance = yieldBalance is YieldBalance.Data
val stakingBalance = currencyStatus.value.stakingBalance as? StakingBalance.Data
val stakeKitBalance = stakingBalance as? StakingBalance.Data.StakeKit
val rateInfo: Pair<BigDecimal, Yield.RewardType?>? = if (hasStakedBalance) {
val rateInfo: Pair<BigDecimal, Yield.RewardType?>? = if (stakeKitBalance != null) {
// StakeKit-specific: try to find rate from validator address
val validatorsByAddress = validators.associateBy { it.address }
yieldBalance.balance.items
stakeKitBalance.balance.items
.mapNotNull { it.validatorAddress }
.firstNotNullOfOrNull { address ->
val validator = validatorsByAddress[address]
@ -284,6 +285,8 @@ class TokenItemStateConverter(
}
.maxByOrNull { it.first }
} else {
// P2P or no balance: use preferred validators
// TODO p2p
validators
.filter { it.preferred }
.mapNotNull { validator ->
@ -296,7 +299,7 @@ class TokenItemStateConverter(
return StakingLocalInfo(
rate = rateInfo?.first,
isActive = hasStakedBalance,
isActive = stakingBalance != null,
rewardType = rateInfo?.second,
)
}

View file

@ -4,28 +4,32 @@ import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.YieldBalanceItem
import com.tangem.utils.converter.Converter
import kotlinx.datetime.Instant
class YieldBalanceConverter(
/**
* Converts StakeKit DTO to [StakingBalance].
* Returns [StakingBalance.Data.StakeKit] for non-empty balances, [StakingBalance.Empty] otherwise.
*/
class StakingBalanceConverter(
private val source: StatusSource,
) : Converter<YieldBalanceWrapperDTO, YieldBalance?> {
) : Converter<YieldBalanceWrapperDTO, StakingBalance?> {
constructor(isCached: Boolean) : this(source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL)
override fun convert(value: YieldBalanceWrapperDTO): YieldBalance? {
override fun convert(value: YieldBalanceWrapperDTO): StakingBalance? {
val stakingId = StakingID(
integrationId = value.integrationId ?: return null,
address = value.addresses.address,
)
return if (value.balances.isEmpty()) {
YieldBalance.Empty(stakingId = stakingId, source = source)
StakingBalance.Empty(stakingId = stakingId, source = source)
} else {
YieldBalance.Data(
StakingBalance.Data.StakeKit(
stakingId = stakingId,
balance = YieldBalanceItem(
items = value.balances

View file

@ -3,11 +3,11 @@ package com.tangem.data.staking
import arrow.core.getOrElse
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.domain.card.common.TapWorkarounds.isWallet2
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingAvailability
@ -30,7 +30,7 @@ import kotlinx.coroutines.withContext
internal class DefaultStakingRepository(
private val stakeKitRepository: StakeKitRepository,
private val p2pEthPoolRepository: P2PEthPoolRepository,
private val stakingBalanceStoreV2: YieldsBalancesStore,
private val stakingBalanceStoreV2: StakingBalancesStore,
private val dispatchers: CoroutineDispatcherProvider,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val stakingFeatureToggles: StakingFeatureToggles,
@ -106,13 +106,13 @@ internal class DefaultStakingRepository(
return withContext(dispatchers.default) {
val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false
val hasDataYieldBalance by lazy {
balances.any { yieldBalance ->
(yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true
val hasDataStakingBalance by lazy {
balances.any { stakingBalance ->
stakingBalance is StakingBalance.Data
}
}
balances.isNotEmpty() && hasDataYieldBalance
balances.isNotEmpty() && hasDataStakingBalance
}
}
@ -135,7 +135,7 @@ internal class DefaultStakingRepository(
address = address,
),
)
if (balance != null && balance is YieldBalance.Data && balance.balance.items.isNotEmpty()) {
if ((balance as? StakingBalance.Data.StakeKit)?.balance?.items?.isNotEmpty() == true) {
return true
} else {
stakingFeatureToggles.isCardanoStakingEnabled

View file

@ -0,0 +1,60 @@
package com.tangem.data.staking.converters.ethpool
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolExitQueueDTO
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolExitRequestDTO
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolStakeDTO
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.*
import com.tangem.domain.staking.model.StakingIntegrationID
import kotlinx.datetime.Instant
/** Converts P2P ETH Pool API response to [StakingBalance.Data.P2P] */
internal object P2PStakingBalanceConverter {
fun convert(response: P2PEthPoolAccountResponse, source: StatusSource): StakingBalance.Data.P2P {
val stakingId = StakingID(
integrationId = StakingIntegrationID.P2P.EthereumPooled.value,
address = response.delegatorAddress,
)
val account = P2PStakingAccount(
delegatorAddress = response.delegatorAddress,
vaultAddress = response.vaultAddress,
stake = convertStake(response.stake),
availableToUnstake = response.availableToUnstake,
availableToWithdraw = response.availableToWithdraw,
exitQueue = convertExitQueue(response.exitQueue),
)
return StakingBalance.Data.P2P(
stakingId = stakingId,
source = source,
account = account,
)
}
private fun convertStake(dto: P2PEthPoolStakeDTO): P2PStake {
return P2PStake(
assets = dto.assets,
totalEarnedAssets = dto.totalEarnedAssets,
)
}
private fun convertExitQueue(dto: P2PEthPoolExitQueueDTO): P2PExitQueue {
return P2PExitQueue(
total = dto.total.toBigDecimal(),
requests = dto.requests.map(::convertExitRequest),
)
}
private fun convertExitRequest(dto: P2PEthPoolExitRequestDTO): P2PExitRequest {
return P2PExitRequest(
ticket = dto.ticket,
totalAssets = dto.totalAssets.toBigDecimal(),
timestamp = Instant.fromEpochSeconds(dto.timestamp),
withdrawalTimestamp = Instant.fromEpochSeconds(dto.withdrawalTimestamp),
isClaimable = dto.isClaimable,
)
}
}

View file

@ -0,0 +1,24 @@
package com.tangem.data.staking.di
import com.tangem.data.staking.multi.DefaultMultiStakingBalanceFetcher
import com.tangem.data.staking.single.DefaultSingleStakingBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface StakingBalanceFetcherModule {
@Binds
@Singleton
fun bindSingleStakingBalanceFetcher(impl: DefaultSingleStakingBalanceFetcher): SingleStakingBalanceFetcher
@Binds
@Singleton
fun bindMultiStakingBalanceFetcher(impl: DefaultMultiStakingBalanceFetcher): MultiStakingBalanceFetcher
}

View file

@ -0,0 +1,28 @@
package com.tangem.data.staking.di
import com.tangem.data.staking.multi.DefaultMultiStakingBalanceProducer
import com.tangem.data.staking.single.DefaultSingleStakingBalanceProducer
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface StakingBalanceProducerFactoryModule {
@Binds
@Singleton
fun bindSingleStakingBalanceProducerFactory(
impl: DefaultSingleStakingBalanceProducer.Factory,
): SingleStakingBalanceProducer.Factory
@Binds
@Singleton
fun bindMultiStakingBalanceProducerFactory(
impl: DefaultMultiStakingBalanceProducer.Factory,
): MultiStakingBalanceProducer.Factory
}

View file

@ -2,19 +2,17 @@ package com.tangem.data.staking.di
import androidx.datastore.core.DataStore
import com.tangem.data.staking.store.DefaultP2PBalancesStore
import com.tangem.data.staking.store.DefaultYieldsBalancesStore
import com.tangem.data.staking.store.DefaultStakingBalancesStore
import com.tangem.data.staking.store.P2PBalancesStore
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.token.P2PEthPoolVaultsStore
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
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.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runSuspendCatching
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -23,15 +21,15 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object YieldBalanceSupplierModule {
internal object StakingBalanceSupplierModule {
@Provides
@Singleton
fun provideYieldsBalancesStore(
fun provideStakingBalancesStore(
persistenceStore: DataStore<Map<String, Set<YieldBalanceWrapperDTO>>>,
dispatchers: CoroutineDispatcherProvider,
): YieldsBalancesStore {
return DefaultYieldsBalancesStore(
): StakingBalancesStore {
return DefaultStakingBalancesStore(
runtimeStore = RuntimeSharedStore(),
persistenceStore = persistenceStore,
dispatchers = dispatchers,
@ -42,25 +40,25 @@ internal object YieldBalanceSupplierModule {
@Singleton
fun provideP2PBalancesStore(
persistenceStore: DataStore<Map<String, Set<P2PEthPoolAccountResponse>>>,
p2pVaultsStore: P2PEthPoolVaultsStore,
dispatchers: CoroutineDispatcherProvider,
): P2PBalancesStore {
return DefaultP2PBalancesStore(
runtimeStore = RuntimeSharedStore(),
persistenceStore = persistenceStore,
vaultsProvider = { runSuspendCatching { p2pVaultsStore.getSync() }.getOrNull().orEmpty() },
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun provideSingleYieldBalanceSupplier(factory: SingleYieldBalanceProducer.Factory): SingleYieldBalanceSupplier {
return object : SingleYieldBalanceSupplier(
fun provideSingleStakingBalanceSupplier(
factory: SingleStakingBalanceProducer.Factory,
): SingleStakingBalanceSupplier {
return object : SingleStakingBalanceSupplier(
factory = factory,
keyCreator = { params ->
listOf(
"single_yield_balance",
"single_staking_balance",
params.userWalletId.stringValue,
params.stakingId.integrationId,
params.stakingId.address,
@ -72,10 +70,10 @@ internal object YieldBalanceSupplierModule {
@Provides
@Singleton
fun provideMultiYieldBalanceSupplier(factory: MultiYieldBalanceProducer.Factory): MultiYieldBalanceSupplier {
return object : MultiYieldBalanceSupplier(
fun provideMultiStakingBalanceSupplier(factory: MultiStakingBalanceProducer.Factory): MultiStakingBalanceSupplier {
return object : MultiStakingBalanceSupplier(
factory = factory,
keyCreator = { "multi_yields_balances_${it.userWalletId.stringValue}" },
keyCreator = { "multi_staking_balances_${it.userWalletId.stringValue}" },
) {}
}
}

View file

@ -5,7 +5,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.data.staking.*
import com.tangem.data.staking.converters.error.StakeKitErrorConverter
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.data.staking.toggles.DefaultStakingFeatureToggles
import com.tangem.data.staking.utils.DefaultStakingCleaner
import com.tangem.datasource.api.ethpool.P2PEthPoolApi
@ -55,7 +55,7 @@ internal object StakingDataModule {
fun provideStakingRepository(
stakeKitRepository: StakeKitRepository,
p2pEthPoolRepository: P2PEthPoolRepository,
yieldsBalancesStore: YieldsBalancesStore,
stakingBalancesStore: StakingBalancesStore,
dispatchers: CoroutineDispatcherProvider,
getUserWalletUseCase: GetUserWalletUseCase,
stakingFeatureToggles: StakingFeatureToggles,
@ -64,7 +64,7 @@ internal object StakingDataModule {
return DefaultStakingRepository(
stakeKitRepository = stakeKitRepository,
p2pEthPoolRepository = p2pEthPoolRepository,
stakingBalanceStoreV2 = yieldsBalancesStore,
stakingBalanceStoreV2 = stakingBalancesStore,
dispatchers = dispatchers,
getUserWalletUseCase = getUserWalletUseCase,
walletManagersFacade = walletManagersFacade,
@ -134,11 +134,11 @@ internal object StakingDataModule {
@Provides
@Singleton
fun provideStakingCleaner(
yieldsBalancesStore: YieldsBalancesStore,
stakingBalancesStore: StakingBalancesStore,
dispatchers: CoroutineDispatcherProvider,
): StakingCleaner {
return DefaultStakingCleaner(
yieldsBalancesStore = yieldsBalancesStore,
stakingBalancesStore = stakingBalancesStore,
dispatchers = dispatchers,
)
}

View file

@ -1,24 +0,0 @@
package com.tangem.data.staking.di
import com.tangem.data.staking.multi.DefaultMultiYieldBalanceFetcher
import com.tangem.data.staking.single.DefaultSingleYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface YieldBalanceFetcherModule {
@Binds
@Singleton
fun bindSingleYieldBalanceFetcher(impl: DefaultSingleYieldBalanceFetcher): SingleYieldBalanceFetcher
@Binds
@Singleton
fun bindMultiYieldBalanceFetcher(impl: DefaultMultiYieldBalanceFetcher): MultiYieldBalanceFetcher
}

View file

@ -1,28 +0,0 @@
package com.tangem.data.staking.di
import com.tangem.data.staking.multi.DefaultMultiYieldBalanceProducer
import com.tangem.data.staking.single.DefaultSingleYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface YieldBalanceProducerFactoryModule {
@Binds
@Singleton
fun bindSingleYieldBalanceProducerFactory(
impl: DefaultSingleYieldBalanceProducer.Factory,
): SingleYieldBalanceProducer.Factory
@Binds
@Singleton
fun bindMultiYieldBalanceProducerFactory(
impl: DefaultMultiYieldBalanceProducer.Factory,
): MultiYieldBalanceProducer.Factory
}

View file

@ -6,7 +6,7 @@ import arrow.core.right
import arrow.core.toOption
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.staking.store.P2PBalancesStore
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.ethpool.P2PEthPoolApi
@ -25,7 +25,7 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.model.ethpool.P2PEthPoolNetwork
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runSuspendCatching
import kotlinx.coroutines.async
@ -36,13 +36,13 @@ import timber.log.Timber
import javax.inject.Inject
/**
* Default implementation of [MultiYieldBalanceFetcher]
* Default implementation of [MultiStakingBalanceFetcher]
*
* Supports both StakeKit and P2P staking providers.
*
* @property userWalletsStore user wallets store
* @property stakingYieldsStore staking yields store
* @property yieldsBalancesStore yields balances store (StakeKit)
* @property stakingBalancesStore staking balances store (StakeKit)
* @property p2pBalancesStore P2P balances store
* @property stakeKitApi stake kit API
* @property p2pApi P2P ETH Pool API
@ -52,19 +52,19 @@ import javax.inject.Inject
[REDACTED_AUTHOR]
*/
@Suppress("LongParameterList")
internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
internal class DefaultMultiStakingBalanceFetcher @Inject constructor(
private val userWalletsStore: UserWalletsStore,
private val stakingYieldsStore: StakingYieldsStore,
private val yieldsBalancesStore: YieldsBalancesStore,
private val stakingBalancesStore: StakingBalancesStore,
private val p2pBalancesStore: P2PBalancesStore,
private val stakeKitApi: StakeKitApi,
private val p2pApi: P2PEthPoolApi,
private val p2pVaultsStore: P2PEthPoolVaultsStore,
private val dispatchers: CoroutineDispatcherProvider,
) : MultiYieldBalanceFetcher {
) : MultiStakingBalanceFetcher {
override suspend fun invoke(params: MultiYieldBalanceFetcher.Params): Either<Throwable, Unit> {
Timber.i("Start fetching yield balances for params:\n$params")
override suspend fun invoke(params: MultiStakingBalanceFetcher.Params): Either<Throwable, Unit> {
Timber.i("Start fetching staking balances for params:\n$params")
val stakingIds = params.stakingIds.ifEmpty {
Timber.i("Nothing to fetch, empty stakingIds for ${params.userWalletId}")
@ -102,10 +102,13 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
}
}
.onLeft { throwable ->
Timber.e(throwable, "Unable to fetch yield balances $params")
Timber.e(throwable, "Unable to fetch staking balances $params")
if (stakeKitIds.isNotEmpty()) {
yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakeKitIds.toSet())
stakingBalancesStore.storeError(
userWalletId = params.userWalletId,
stakingIds = stakeKitIds.toSet(),
)
}
if (p2pIds.isNotEmpty()) {
p2pBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = p2pIds.toSet())
@ -114,7 +117,7 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
}
private suspend fun fetchStakeKitBalances(userWalletId: UserWalletId, stakingIds: Set<StakingID>) {
yieldsBalancesStore.refresh(userWalletId = userWalletId, stakingIds = stakingIds)
stakingBalancesStore.refresh(userWalletId = userWalletId, stakingIds = stakingIds)
val availableStakingIds = getAvailableStakingIds(
userWalletId = userWalletId,
@ -252,7 +255,7 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
)
if (unavailableStakingIds.isNotEmpty()) {
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = unavailableStakingIds.toSet())
stakingBalancesStore.storeError(userWalletId = userWalletId, stakingIds = unavailableStakingIds.toSet())
}
return availableStakingIds.toSet().ifEmpty {
@ -301,8 +304,10 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
.toSet()
}
Timber.i("Successfully fetched yield balances for $userWalletId:\n${yieldBalances.joinToString("\n")}")
yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = yieldBalances)
Timber.i(
"Successfully fetched staking balances for $userWalletId:\n${yieldBalances.joinToString("\n")}",
)
stakingBalancesStore.storeActual(userWalletId = userWalletId, values = yieldBalances)
if (!allResponsesReceived(requests, yieldBalances)) {
val values = stakingIds.filter { stakingId ->
@ -312,13 +317,13 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
}
}
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = values.toSet())
stakingBalancesStore.storeError(userWalletId = userWalletId, stakingIds = values.toSet())
}
},
onError = { throwable ->
Timber.e(throwable, "Unable to fetch yield balances $userWalletId")
Timber.e(throwable, "Unable to fetch staking balances $userWalletId")
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = stakingIds)
stakingBalancesStore.storeError(userWalletId = userWalletId, stakingIds = stakingIds)
throw throwable
},

View file

@ -3,9 +3,9 @@ package com.tangem.data.staking.multi
import arrow.core.Option
import arrow.core.some
import com.tangem.data.staking.store.P2PBalancesStore
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -17,28 +17,28 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEmpty
/**
* Default implementation of [MultiYieldBalanceProducer]
* Default implementation of [MultiStakingBalanceProducer]
*
* Combines yield balances from both StakeKit and P2P providers.
* Combines staking balances from both StakeKit and P2P providers.
*
* @property params params
* @property yieldsBalancesStore StakeKit yields balances store
* @property stakingBalancesStore StakeKit staking balances store
* @property p2pBalancesStore P2P balances store
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
internal class DefaultMultiYieldBalanceProducer @AssistedInject constructor(
@Assisted val params: MultiYieldBalanceProducer.Params,
private val yieldsBalancesStore: YieldsBalancesStore,
internal class DefaultMultiStakingBalanceProducer @AssistedInject constructor(
@Assisted val params: MultiStakingBalanceProducer.Params,
private val stakingBalancesStore: StakingBalancesStore,
private val p2pBalancesStore: P2PBalancesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : MultiYieldBalanceProducer {
) : MultiStakingBalanceProducer {
override val fallback: Option<Set<YieldBalance>> = emptySet<YieldBalance>().some()
override val fallback: Option<Set<StakingBalance>> = emptySet<StakingBalance>().some()
override fun produce(): Flow<Set<YieldBalance>> {
val stakeKitFlow = yieldsBalancesStore.get(userWalletId = params.userWalletId)
override fun produce(): Flow<Set<StakingBalance>> {
val stakeKitFlow = stakingBalancesStore.get(userWalletId = params.userWalletId)
val p2pFlow = p2pBalancesStore.get(userWalletId = params.userWalletId)
return combine(stakeKitFlow, p2pFlow) { stakeKitBalances, p2pBalances ->
@ -50,7 +50,7 @@ internal class DefaultMultiYieldBalanceProducer @AssistedInject constructor(
}
@AssistedFactory
interface Factory : MultiYieldBalanceProducer.Factory {
override fun create(params: MultiYieldBalanceProducer.Params): DefaultMultiYieldBalanceProducer
interface Factory : MultiStakingBalanceProducer.Factory {
override fun create(params: MultiStakingBalanceProducer.Params): DefaultMultiStakingBalanceProducer
}
}

View file

@ -0,0 +1,27 @@
package com.tangem.data.staking.single
import arrow.core.Either
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import javax.inject.Inject
/**
* Default implementation of [SingleStakingBalanceFetcher]
*
* @property multiStakingBalanceFetcher multi staking balance fetcher
*
[REDACTED_AUTHOR]
*/
internal class DefaultSingleStakingBalanceFetcher @Inject constructor(
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
) : SingleStakingBalanceFetcher {
override suspend fun invoke(params: SingleStakingBalanceFetcher.Params): Either<Throwable, Unit> {
return multiStakingBalanceFetcher(
params = MultiStakingBalanceFetcher.Params(
userWalletId = params.userWalletId,
stakingIds = setOf(params.stakingId),
),
)
}
}

View file

@ -4,10 +4,10 @@ import arrow.core.Option
import arrow.core.some
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.domain.staking.multi.MultiStakingBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.indexOfFirstOrNull
import dagger.assisted.Assisted
@ -20,29 +20,29 @@ import kotlinx.coroutines.flow.mapNotNull
import timber.log.Timber
/**
* Default implementation of [SingleYieldBalanceProducer]
* Default implementation of [SingleStakingBalanceProducer]
*
* @property params params
* @property multiYieldBalanceSupplier multi yield balance supplier
* @property analyticsExceptionHandler analytics exception handler
* @property dispatchers dispatchers
* @property params params
* @property multiStakingBalanceSupplier multi staking balance supplier
* @property analyticsExceptionHandler analytics exception handler
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
@Assisted private val params: SingleYieldBalanceProducer.Params,
private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
internal class DefaultSingleStakingBalanceProducer @AssistedInject constructor(
@Assisted private val params: SingleStakingBalanceProducer.Params,
private val multiStakingBalanceSupplier: MultiStakingBalanceSupplier,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val dispatchers: CoroutineDispatcherProvider,
) : SingleYieldBalanceProducer {
) : SingleStakingBalanceProducer {
override val fallback: Option<YieldBalance> = YieldBalance.Error(stakingId = params.stakingId).some()
override val fallback: Option<StakingBalance> = StakingBalance.Error(stakingId = params.stakingId).some()
override fun produce(): Flow<YieldBalance> {
Timber.i("Producing yield balance for params:\n$params")
override fun produce(): Flow<StakingBalance> {
Timber.i("Producing staking balance for params:\n$params")
return multiYieldBalanceSupplier(
params = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId),
return multiStakingBalanceSupplier(
params = MultiStakingBalanceProducer.Params(userWalletId = params.userWalletId),
)
.mapNotNull { balances ->
val currentStakingId = params.stakingId
@ -65,7 +65,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
currentBalances.joinToString("\n"),
)
val dataIndex = currentBalances.indexOfFirstOrNull { it is YieldBalance.Data }
val dataIndex = currentBalances.indexOfFirstOrNull { it is StakingBalance.Data }
if (dataIndex != null) {
currentBalances[dataIndex]
@ -75,7 +75,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
} else {
val balance = currentBalances.firstOrNull() ?: return@mapNotNull null
Timber.i("Yield balance found for $currentStakingId:\n$balance")
Timber.i("Staking balance found for $currentStakingId:\n$balance")
balance
}
}
@ -84,7 +84,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
}
@AssistedFactory
interface Factory : SingleYieldBalanceProducer.Factory {
override fun create(params: SingleYieldBalanceProducer.Params): DefaultSingleYieldBalanceProducer
interface Factory : SingleStakingBalanceProducer.Factory {
override fun create(params: SingleStakingBalanceProducer.Params): DefaultSingleStakingBalanceProducer
}
}

View file

@ -1,27 +0,0 @@
package com.tangem.data.staking.single
import arrow.core.Either
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import javax.inject.Inject
/**
* Default implementation of [MultiYieldBalanceFetcher]
*
* @property multiYieldBalanceFetcher multi yield balance fetcher
*
[REDACTED_AUTHOR]
*/
internal class DefaultSingleYieldBalanceFetcher @Inject constructor(
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
) : SingleYieldBalanceFetcher {
override suspend fun invoke(params: SingleYieldBalanceFetcher.Params): Either<Throwable, Unit> {
return multiYieldBalanceFetcher(
params = MultiYieldBalanceFetcher.Params(
userWalletId = params.userWalletId,
stakingIds = setOf(params.stakingId),
),
)
}
}

View file

@ -1,15 +1,14 @@
package com.tangem.data.staking.store
import androidx.datastore.core.DataStore
import com.tangem.data.staking.converters.ethpool.P2PEthPoolAccountConverter
import com.tangem.data.staking.converters.ethpool.P2PYieldBalanceConverter
import com.tangem.data.staking.converters.ethpool.P2PStakingBalanceConverter
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.ethpool.P2PEthPoolVault
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.CoroutineScope
@ -19,25 +18,22 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
internal typealias WalletIdWithP2PBalances = Map<UserWalletId, Set<YieldBalance>>
internal typealias WalletIdWithP2PStakingBalances = Map<UserWalletId, Set<StakingBalance>>
internal typealias WalletIdWithP2PResponses = Map<String, Set<P2PEthPoolAccountResponse>>
/**
* Default implementation of [P2PBalancesStore]
*
* Stores P2P ETH Pool staking balances with persistence support.
* Stores P2P ETH Pool staking balances.
*
* @property runtimeStore runtime store
* @property persistenceStore persistence store
* @property vaultsProvider provider for vaults
* @param dispatchers coroutine dispatchers
*/
internal class DefaultP2PBalancesStore(
private val runtimeStore: RuntimeSharedStore<WalletIdWithP2PBalances>,
private val runtimeStore: RuntimeSharedStore<WalletIdWithP2PStakingBalances>,
private val persistenceStore: DataStore<WalletIdWithP2PResponses>,
private val vaultsProvider: suspend () -> List<P2PEthPoolVault>,
dispatchers: CoroutineDispatcherProvider,
) : P2PBalancesStore {
@ -46,19 +42,13 @@ internal class DefaultP2PBalancesStore(
init {
scope.launch {
val cachedData = persistenceStore.data.firstOrNull() ?: return@launch
val vaults = vaultsProvider()
runtimeStore.store(
value = cachedData.map { (stringWalletId, responses) ->
val key = UserWalletId(stringWalletId)
val value = responses.mapNotNull { response ->
val vault = vaults.firstOrNull { it.vaultAddress == response.vaultAddress }
?: return@mapNotNull null
val account = P2PEthPoolAccountConverter.convert(response)
P2PYieldBalanceConverter.convert(
account = account,
vault = vault,
address = account.delegatorAddress,
val value = responses.map { response ->
P2PStakingBalanceConverter.convert(
response = response,
source = StatusSource.CACHE,
)
}.toSet()
@ -69,17 +59,17 @@ internal class DefaultP2PBalancesStore(
}
}
override fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>> {
override fun get(userWalletId: UserWalletId): Flow<Set<StakingBalance>> {
return runtimeStore.get().map { it[userWalletId].orEmpty() }
}
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance? {
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): StakingBalance? {
return runtimeStore.getSyncOrNull()
?.get(userWalletId)
?.firstOrNull { it.stakingId == stakingId }
}
override suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>? {
override suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<StakingBalance>? {
return runtimeStore.getSyncOrNull()?.get(userWalletId)
}
@ -104,7 +94,7 @@ internal class DefaultP2PBalancesStore(
updateInRuntime(
userWalletId = userWalletId,
stakingIds = stakingIds,
ifNotFound = ::createErrorYieldBalance,
ifNotFound = ::createErrorStakingBalance,
update = { it.copySealed(source = StatusSource.ONLY_CACHE) },
)
}
@ -117,20 +107,9 @@ internal class DefaultP2PBalancesStore(
}
private suspend fun storeInRuntime(userWalletId: UserWalletId, values: Set<P2PEthPoolAccountResponse>) {
val vaults = vaultsProvider()
val newBalances = values.mapNotNull { response ->
val vault = vaults.firstOrNull { it.vaultAddress == response.vaultAddress }
if (vault == null) {
Timber.w("Vault not found for ${response.vaultAddress}")
return@mapNotNull null
}
val account = P2PEthPoolAccountConverter.convert(response)
P2PYieldBalanceConverter.convert(
account = account,
vault = vault,
address = account.delegatorAddress,
val newBalances = values.map { response ->
P2PStakingBalanceConverter.convert(
response = response,
source = StatusSource.ACTUAL,
)
}.toSet()
@ -173,8 +152,7 @@ internal class DefaultP2PBalancesStore(
current.toMutableMap().apply {
this[userWalletId.stringValue] = this[userWalletId.stringValue].orEmpty()
.filterNot { response ->
val responseIntegrationId = "p2p-ethereum-pooled:${response.vaultAddress}"
responseIntegrationId in integrationIds
StakingIntegrationID.P2P.EthereumPooled.value in integrationIds
}
.toSet()
}
@ -184,8 +162,8 @@ internal class DefaultP2PBalancesStore(
private suspend fun updateInRuntime(
userWalletId: UserWalletId,
stakingIds: Set<StakingID>,
ifNotFound: (StakingID) -> YieldBalance? = { null },
update: (YieldBalance) -> YieldBalance,
ifNotFound: (StakingID) -> StakingBalance? = { null },
update: (StakingBalance) -> StakingBalance,
) {
runtimeStore.update(default = emptyMap()) { stored ->
stored.toMutableMap().apply {
@ -209,5 +187,5 @@ internal class DefaultP2PBalancesStore(
}
}
private fun createErrorYieldBalance(id: StakingID): YieldBalance = YieldBalance.Error(stakingId = id)
private fun createErrorStakingBalance(id: StakingID): StakingBalance = StakingBalance.Error(stakingId = id)
}

View file

@ -3,10 +3,10 @@ package com.tangem.data.staking.store
import androidx.datastore.core.DataStore
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.token.converter.YieldBalanceConverter
import com.tangem.datasource.local.token.converter.StakingBalanceConverter
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
@ -19,10 +19,10 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
internal typealias WalletIdWithWrappers = Map<String, Set<YieldBalanceWrapperDTO>>
internal typealias WalletIdWithBalances = Map<UserWalletId, Set<YieldBalance>>
internal typealias WalletIdWithStakingBalances = Map<UserWalletId, Set<StakingBalance>>
/**
* Default implementation of [YieldsBalancesStore]
* Default implementation of [StakingBalancesStore]
*
* @property runtimeStore runtime store
* @property persistenceStore persistence store
@ -30,11 +30,11 @@ internal typealias WalletIdWithBalances = Map<UserWalletId, Set<YieldBalance>>
*
[REDACTED_AUTHOR]
*/
internal class DefaultYieldsBalancesStore(
private val runtimeStore: RuntimeSharedStore<WalletIdWithBalances>,
internal class DefaultStakingBalancesStore(
private val runtimeStore: RuntimeSharedStore<WalletIdWithStakingBalances>,
private val persistenceStore: DataStore<WalletIdWithWrappers>,
dispatchers: CoroutineDispatcherProvider,
) : YieldsBalancesStore {
) : StakingBalancesStore {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
@ -45,7 +45,7 @@ internal class DefaultYieldsBalancesStore(
runtimeStore.store(
value = cachedStatuses.map { (stringWalletId, wrappers) ->
val key = UserWalletId(stringWalletId)
val value = YieldBalanceConverter(isCached = true).convertSet(input = wrappers)
val value = StakingBalanceConverter(isCached = true).convertSet(input = wrappers)
.filterNotNull()
.toSet()
@ -56,17 +56,17 @@ internal class DefaultYieldsBalancesStore(
}
}
override fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>> {
override fun get(userWalletId: UserWalletId): Flow<Set<StakingBalance>> {
return runtimeStore.get().map { it[userWalletId].orEmpty() }
}
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance? {
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): StakingBalance? {
return runtimeStore.getSyncOrNull()
?.get(userWalletId)
?.firstOrNull { it.stakingId == stakingId }
}
override suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>? {
override suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<StakingBalance>? {
return runtimeStore.getSyncOrNull()?.get(userWalletId)
}
@ -91,7 +91,7 @@ internal class DefaultYieldsBalancesStore(
updateInRuntime(
userWalletId = userWalletId,
stakingIds = stakingIds,
ifNotFound = ::createErrorYieldBalance,
ifNotFound = ::createErrorStakingBalance,
update = { it.copySealed(source = StatusSource.ONLY_CACHE) },
)
}
@ -107,7 +107,7 @@ internal class DefaultYieldsBalancesStore(
}
private suspend fun storeInRuntime(userWalletId: UserWalletId, values: Set<YieldBalanceWrapperDTO>) {
val newBalances = YieldBalanceConverter(isCached = false).convertSet(input = values)
val newBalances = StakingBalanceConverter(isCached = false).convertSet(input = values)
.filterNotNull()
.toSet()
@ -140,8 +140,8 @@ internal class DefaultYieldsBalancesStore(
private suspend fun updateInRuntime(
userWalletId: UserWalletId,
stakingIds: Set<StakingID>,
ifNotFound: (StakingID) -> YieldBalance? = { null },
update: (YieldBalance) -> YieldBalance,
ifNotFound: (StakingID) -> StakingBalance? = { null },
update: (StakingBalance) -> StakingBalance,
) {
runtimeStore.update(default = emptyMap()) { stored ->
stored.toMutableMap().apply {
@ -165,7 +165,7 @@ internal class DefaultYieldsBalancesStore(
}
}
private fun createErrorYieldBalance(id: StakingID): YieldBalance = YieldBalance.Error(stakingId = id)
private fun createErrorStakingBalance(id: StakingID): StakingBalance = StakingBalance.Error(stakingId = id)
private fun YieldBalanceWrapperDTO.getStakingId(): StakingID? {
val integrationId = integrationId

View file

@ -1,8 +1,8 @@
package com.tangem.data.staking.store
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
@ -11,11 +11,11 @@ import kotlinx.coroutines.flow.Flow
*/
interface P2PBalancesStore {
fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>>
fun get(userWalletId: UserWalletId): Flow<Set<StakingBalance>>
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance?
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): StakingBalance?
suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>?
suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<StakingBalance>?
suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID)

View file

@ -1,39 +1,27 @@
package com.tangem.data.staking.store
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
/**
* Store of [YieldBalance]'s set
*
[REDACTED_AUTHOR]
*/
interface YieldsBalancesStore {
/** Store of StakeKit [StakingBalance] */
interface StakingBalancesStore {
/** Get flow of [YieldBalance]'s set by [userWalletId] */
fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>>
fun get(userWalletId: UserWalletId): Flow<Set<StakingBalance>>
/** Get [YieldBalance] by [userWalletId] and [stakingId] synchronously or null */
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance?
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): StakingBalance?
/** Get all [YieldBalance] by [userWalletId] synchronously or null */
suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>?
suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set<StakingBalance>?
/** Refresh balance of [stakingId] by [userWalletId] */
suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID)
/** Refresh balances of [stakingIds] by [userWalletId] */
suspend fun refresh(userWalletId: UserWalletId, stakingIds: Set<StakingID>)
/** Store actual [values] by [userWalletId] */
suspend fun storeActual(userWalletId: UserWalletId, values: Set<YieldBalanceWrapperDTO>)
/** Store error by [userWalletId] and [stakingIds] */
suspend fun storeError(userWalletId: UserWalletId, stakingIds: Set<StakingID>)
/** Clear balances of [stakingIds] by [userWalletId] */
suspend fun clear(userWalletId: UserWalletId, stakingIds: Set<StakingID>)
}

View file

@ -1,6 +1,6 @@
package com.tangem.data.staking.utils
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.utils.StakingCleaner
@ -9,13 +9,13 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
/**
* Default implementation of [StakingCleaner].
*
* @property yieldsBalancesStore Store to manage yields balances.
* @property stakingBalancesStore Store to manage staking balances.
* @property dispatchers Coroutine dispatchers provider.
*
[REDACTED_AUTHOR]
*/
internal class DefaultStakingCleaner(
private val yieldsBalancesStore: YieldsBalancesStore,
private val stakingBalancesStore: StakingBalancesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : StakingCleaner {
@ -23,7 +23,7 @@ internal class DefaultStakingCleaner(
if (stakingIds.isEmpty()) return
with(dispatchers.default) {
yieldsBalancesStore.clear(userWalletId, stakingIds)
stakingBalancesStore.clear(userWalletId, stakingIds)
}
}
}

View file

@ -1,28 +1,19 @@
package com.tangem.data.staking
import com.tangem.common.test.data.staking.MockP2PEthPoolAccountResponseFactory
import com.tangem.data.staking.converters.ethpool.P2PEthPoolAccountConverter
import com.tangem.data.staking.converters.ethpool.P2PYieldBalanceConverter
import com.tangem.data.staking.converters.ethpool.P2PStakingBalanceConverter
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.token.converter.YieldBalanceConverter
import com.tangem.datasource.local.token.converter.StakingBalanceConverter
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.staking.model.ethpool.P2PEthPoolVault
import com.tangem.domain.models.staking.StakingBalance
internal fun YieldBalanceWrapperDTO.toDomain(source: StatusSource = StatusSource.CACHE): YieldBalance {
return YieldBalanceConverter(source = source).convert(this)!!
internal fun YieldBalanceWrapperDTO.toDomain(source: StatusSource = StatusSource.CACHE): StakingBalance {
return StakingBalanceConverter(isCached = source == StatusSource.CACHE).convert(this)!!
}
internal fun P2PEthPoolAccountResponse.toDomain(
vault: P2PEthPoolVault = MockP2PEthPoolAccountResponseFactory.createMockVault(vaultAddress = vaultAddress),
source: StatusSource = StatusSource.CACHE,
): YieldBalance {
val account = P2PEthPoolAccountConverter.convert(this)
return P2PYieldBalanceConverter.convert(
account = account,
vault = vault,
address = account.delegatorAddress,
internal fun P2PEthPoolAccountResponse.toDomain(source: StatusSource = StatusSource.CACHE): StakingBalance.Data.P2P {
return P2PStakingBalanceConverter.convert(
response = this,
source = source,
)
}

View file

@ -5,7 +5,7 @@ import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.data.staking.MockYieldDTOFactory
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
import com.tangem.data.staking.store.P2PBalancesStore
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError
@ -17,7 +17,7 @@ import com.tangem.datasource.local.token.StakingYieldsStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.test.core.assertEitherLeft
import com.tangem.test.core.assertEitherRight
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
@ -31,20 +31,20 @@ import org.junit.jupiter.api.TestInstance
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class DefaultMultiYieldBalanceFetcherTest {
internal class DefaultMultiStakingBalanceFetcherTest {
private val userWalletsStore: UserWalletsStore = mockk()
private val stakingYieldsStore: StakingYieldsStore = mockk()
private val yieldsBalancesStore: YieldsBalancesStore = mockk(relaxUnitFun = true)
private val stakingBalancesStore: StakingBalancesStore = mockk(relaxUnitFun = true)
private val p2pBalancesStore: P2PBalancesStore = mockk(relaxUnitFun = true)
private val stakeKitApi: StakeKitApi = mockk()
private val p2pApi: P2PEthPoolApi = mockk()
private val p2pVaultsStore: P2PEthPoolVaultsStore = mockk()
private val fetcher = DefaultMultiYieldBalanceFetcher(
private val fetcher = DefaultMultiStakingBalanceFetcher(
userWalletsStore = userWalletsStore,
stakingYieldsStore = stakingYieldsStore,
yieldsBalancesStore = yieldsBalancesStore,
stakingBalancesStore = stakingBalancesStore,
p2pBalancesStore = p2pBalancesStore,
stakeKitApi = stakeKitApi,
p2pApi = p2pApi,
@ -54,13 +54,13 @@ internal class DefaultMultiYieldBalanceFetcherTest {
@BeforeEach
fun resetMocks() {
clearMocks(userWalletsStore, stakingYieldsStore, yieldsBalancesStore, stakeKitApi)
clearMocks(userWalletsStore, stakingYieldsStore, stakingBalancesStore, stakeKitApi)
}
@Test
fun `fetch yields balances successfully`() = runTest {
fun `fetch staking balances successfully`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
@ -81,21 +81,21 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
stakeKitApi.getMultipleYieldBalances(requests)
yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result)
stakingBalancesStore.storeActual(userWalletId = userWalletId, values = result)
}
coVerify(inverse = true) { yieldsBalancesStore.storeError(any(), any()) }
coVerify(inverse = true) { stakingBalancesStore.storeError(any(), any()) }
assertEitherRight(actual)
}
@Test
fun `fetch yields balances successfully if one of stakingIds is unavailable`() = runTest {
fun `fetch staking balances successfully if one of stakingIds is unavailable`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
@ -113,20 +113,20 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = setOf(solanaId))
stakingBalancesStore.storeError(userWalletId = userWalletId, stakingIds = setOf(solanaId))
stakeKitApi.getMultipleYieldBalances(requests)
yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result)
stakingBalancesStore.storeActual(userWalletId = userWalletId, values = result)
}
assertEitherRight(actual)
}
@Test
fun `fetch yields balances failure if user wallet is not supported`() = runTest {
fun `fetch staking balances failure if user wallet is not supported`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val userWallet = MockUserWalletFactory.create().copy(isMultiCurrency = false)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
@ -138,11 +138,11 @@ internal class DefaultMultiYieldBalanceFetcherTest {
coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) }
coVerify(inverse = true) {
yieldsBalancesStore.refresh(userWalletId = any(), stakingIds = any())
stakingBalancesStore.refresh(userWalletId = any(), stakingIds = any())
stakingYieldsStore.getSyncWithTimeout()
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
yieldsBalancesStore.storeError(userWalletId = any(), stakingIds = any())
stakingBalancesStore.storeActual(userWalletId = any(), values = any())
stakingBalancesStore.storeError(userWalletId = any(), stakingIds = any())
}
val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${userWallet.toOption()}")
@ -151,9 +151,9 @@ internal class DefaultMultiYieldBalanceFetcherTest {
}
@Test
fun `fetch yields balances failure if userWalletsStore returns null`() = runTest {
fun `fetch staking balances failure if userWalletsStore returns null`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns null
@ -164,11 +164,11 @@ internal class DefaultMultiYieldBalanceFetcherTest {
coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) }
coVerify(inverse = true) {
yieldsBalancesStore.refresh(userWalletId = any(), stakingIds = any())
stakingBalancesStore.refresh(userWalletId = any(), stakingIds = any())
stakingYieldsStore.getSyncWithTimeout()
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
yieldsBalancesStore.storeError(userWalletId = any(), stakingIds = any())
stakingBalancesStore.storeActual(userWalletId = any(), values = any())
stakingBalancesStore.storeError(userWalletId = any(), stakingIds = any())
}
val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${null.toOption()}")
@ -177,9 +177,9 @@ internal class DefaultMultiYieldBalanceFetcherTest {
}
@Test
fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns null`() = runTest {
fun `fetch staking balances failure if stakingYieldsStore getSyncWithTimeout returns null`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns null
@ -190,14 +190,14 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds)
stakingBalancesStore.refresh(params.userWalletId, tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
stakingBalancesStore.storeError(userWalletId, tonAndSolanaIds)
}
coVerify(inverse = true) {
stakeKitApi.getMultipleYieldBalances(any())
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
stakingBalancesStore.storeActual(userWalletId = any(), values = any())
}
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
@ -206,9 +206,9 @@ internal class DefaultMultiYieldBalanceFetcherTest {
}
@Test
fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns empty list`() = runTest {
fun `fetch staking balances failure if stakingYieldsStore getSyncWithTimeout returns empty list`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns emptyList()
@ -219,14 +219,14 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
stakingBalancesStore.storeError(userWalletId, tonAndSolanaIds)
}
coVerify(inverse = true) {
stakeKitApi.getMultipleYieldBalances(any())
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
stakingBalancesStore.storeActual(userWalletId = any(), values = any())
}
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
@ -235,9 +235,9 @@ internal class DefaultMultiYieldBalanceFetcherTest {
}
@Test
fun `fetch yields balances failure if yields converting is failed`() = runTest {
fun `fetch staking balances failure if yields converting is failed`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
@ -253,14 +253,14 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
stakingBalancesStore.storeError(userWalletId, tonAndSolanaIds)
}
coVerify(inverse = true) {
stakeKitApi.getMultipleYieldBalances(any())
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
stakingBalancesStore.storeActual(userWalletId = any(), values = any())
}
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
@ -269,9 +269,9 @@ internal class DefaultMultiYieldBalanceFetcherTest {
}
@Test
fun `fetch yields balances failure if available yields does not contain ids from params`() = runTest {
fun `fetch staking balances failure if available yields does not contain ids from params`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
@ -284,14 +284,14 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
stakingBalancesStore.storeError(userWalletId, tonAndSolanaIds)
}
coVerify(inverse = true) {
stakeKitApi.getMultipleYieldBalances(any())
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
stakingBalancesStore.storeActual(userWalletId = any(), values = any())
}
val expected = IllegalStateException(
@ -306,9 +306,9 @@ internal class DefaultMultiYieldBalanceFetcherTest {
}
@Test
fun `fetch yields balances failure if stakeKitApi getMultipleYieldBalances is failed`() = runTest {
fun `fetch staking balances failure if stakeKitApi getMultipleYieldBalances is failed`() = runTest {
// Arrange
val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
val params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
@ -329,13 +329,13 @@ internal class DefaultMultiYieldBalanceFetcherTest {
// Assert
coVerifyOrder {
userWalletsStore.getSyncOrNull(params.userWalletId)
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
stakingYieldsStore.getSyncWithTimeout()
stakeKitApi.getMultipleYieldBalances(requests)
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
stakingBalancesStore.storeError(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
}
coVerify(inverse = true) { yieldsBalancesStore.storeActual(userWalletId = any(), values = any()) }
coVerify(inverse = true) { stakingBalancesStore.storeActual(userWalletId = any(), values = any()) }
val expected = ApiResponseError.NetworkException()

View file

@ -4,12 +4,13 @@ import com.google.common.truth.Truth
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.data.staking.MockP2PEthPoolAccountResponseFactory
import com.tangem.data.staking.store.P2PBalancesStore
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.data.staking.toDomain
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.*
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
@ -22,17 +23,17 @@ import org.junit.Test
/**
[REDACTED_AUTHOR]
*/
internal class DefaultMultiYieldBalanceProducerTest {
internal class DefaultMultiStakingBalanceProducerTest {
private val params = MultiYieldBalanceProducer.Params(userWalletId = UserWalletId("011"))
private val params = MultiStakingBalanceProducer.Params(userWalletId = UserWalletId("011"))
private val yieldsBalancesStore = mockk<YieldsBalancesStore>()
private val stakingBalancesStore = mockk<StakingBalancesStore>()
private val p2pBalancesStore = mockk<P2PBalancesStore>()
private val dispatchers = TestingCoroutineDispatcherProvider()
private val producer = DefaultMultiYieldBalanceProducer(
private val producer = DefaultMultiStakingBalanceProducer(
params = params,
yieldsBalancesStore = yieldsBalancesStore,
stakingBalancesStore = stakingBalancesStore,
p2pBalancesStore = p2pBalancesStore,
dispatchers = dispatchers,
)
@ -46,13 +47,13 @@ internal class DefaultMultiYieldBalanceProducerTest {
val networksStatusesFlow = flowOf(balances)
every { yieldsBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { stakingBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { p2pBalancesStore.get(params.userWalletId) } returns flowOf(emptySet())
val actual = producer.produce()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
val values = getEmittedValues(flow = actual)
@ -63,15 +64,15 @@ internal class DefaultMultiYieldBalanceProducerTest {
@Test
fun `test that flow is updated if balances are updated`() = runTest {
val networksStatusesFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2)
val networksStatusesFlow = MutableSharedFlow<Set<StakingBalance>>(replay = 2)
every { yieldsBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { stakingBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { p2pBalancesStore.get(params.userWalletId) } returns flowOf(emptySet())
val actual = producer.produce()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
// first emit
@ -104,15 +105,15 @@ internal class DefaultMultiYieldBalanceProducerTest {
@Test
fun `test that flow is filtered the same balance`() = runTest {
val networksStatusesFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2)
val networksStatusesFlow = MutableSharedFlow<Set<StakingBalance>>(replay = 2)
every { yieldsBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { stakingBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { p2pBalancesStore.get(params.userWalletId) } returns flowOf(emptySet())
val actual = producer.produce()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
// first emit
@ -155,19 +156,19 @@ internal class DefaultMultiYieldBalanceProducerTest {
}
.buffer(capacity = 5)
every { yieldsBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { stakingBalancesStore.get(params.userWalletId) } returns networksStatusesFlow
every { p2pBalancesStore.get(params.userWalletId) } returns flowOf(emptySet())
val actual = producer.produceWithFallback()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(emptySet<YieldBalance>()))
Truth.assertThat(values1).isEqualTo(listOf(emptySet<StakingBalance>()))
innerFlow.emit(value = true)
@ -179,19 +180,19 @@ internal class DefaultMultiYieldBalanceProducerTest {
@Test
fun `test that flow is empty`() = runTest {
every { yieldsBalancesStore.get(params.userWalletId) } returns emptyFlow()
every { stakingBalancesStore.get(params.userWalletId) } returns emptyFlow()
every { p2pBalancesStore.get(params.userWalletId) } returns emptyFlow()
val actual = producer.produce()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(emptySet<YieldBalance>()))
Truth.assertThat(values).isEqualTo(listOf(emptySet<StakingBalance>()))
}
@Test
@ -199,13 +200,13 @@ internal class DefaultMultiYieldBalanceProducerTest {
val stakeKitBalances = createStakeKitBalances()
val p2pBalances = createP2PBalances()
every { yieldsBalancesStore.get(params.userWalletId) } returns flowOf(stakeKitBalances)
every { stakingBalancesStore.get(params.userWalletId) } returns flowOf(stakeKitBalances)
every { p2pBalancesStore.get(params.userWalletId) } returns flowOf(p2pBalances)
val actual = producer.produce()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
val values = getEmittedValues(flow = actual)
@ -217,15 +218,15 @@ internal class DefaultMultiYieldBalanceProducerTest {
@Test
fun `test that P2P balances are updated independently from StakeKit`() = runTest {
val stakeKitBalances = createStakeKitBalancesWithTonOnly()
val p2pFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2)
val p2pFlow = MutableSharedFlow<Set<StakingBalance>>(replay = 2)
every { yieldsBalancesStore.get(params.userWalletId) } returns flowOf(stakeKitBalances)
every { stakingBalancesStore.get(params.userWalletId) } returns flowOf(stakeKitBalances)
every { p2pBalancesStore.get(params.userWalletId) } returns p2pFlow
val actual = producer.produce()
// check after producer.produce()
verify { yieldsBalancesStore.get(params.userWalletId) }
verify { stakingBalancesStore.get(params.userWalletId) }
verify { p2pBalancesStore.get(params.userWalletId) }
// first emit - empty P2P
@ -254,24 +255,24 @@ internal class DefaultMultiYieldBalanceProducerTest {
address = "0x1",
)
val p2pEthereumId = StakingID(
integrationId = "p2p-ethereum-pooled",
integrationId = StakingIntegrationID.P2P.EthereumPooled.value,
address = "0x5aa711F440Eb6d4361148bBD89d03464628ace84",
)
fun createStakeKitBalances(): Set<YieldBalance> {
fun createStakeKitBalances(): Set<StakingBalance> {
return setOf(
MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain(),
MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain(),
)
}
fun createStakeKitBalancesWithTonOnly(): Set<YieldBalance> {
fun createStakeKitBalancesWithTonOnly(): Set<StakingBalance> {
return setOf(
MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain(),
)
}
fun createP2PBalances(): Set<YieldBalance> {
fun createP2PBalances(): Set<StakingBalance> {
return setOf(
MockP2PEthPoolAccountResponseFactory.createWithBalance(stakingId = p2pEthereumId).toDomain(
source = StatusSource.ACTUAL,

View file

@ -5,8 +5,8 @@ import arrow.core.right
import com.google.common.truth.Truth
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.coVerify
@ -20,32 +20,32 @@ import org.junit.jupiter.api.TestInstance
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class DefaultSingleYieldBalanceFetcherTest {
internal class DefaultSingleStakingBalanceFetcherTest {
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher = mockk()
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher = mockk()
private val fetcher = DefaultSingleYieldBalanceFetcher(
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
private val fetcher = DefaultSingleStakingBalanceFetcher(
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
)
@BeforeEach
fun resetMocks() {
clearMocks(multiYieldBalanceFetcher)
clearMocks(multiStakingBalanceFetcher)
}
@Test
fun `fetch yield balance successfully`() = runTest {
fun `fetch staking balance successfully`() = runTest {
// Arrange
val params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = tonId)
val params = SingleStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingId = tonId)
val multiParams = MultiYieldBalanceFetcher.Params(
val multiParams = MultiStakingBalanceFetcher.Params(
userWalletId = userWalletId,
stakingIds = setOf(tonId),
)
val multiResult = Unit.right()
coEvery { multiYieldBalanceFetcher(params = multiParams) } returns multiResult
coEvery { multiStakingBalanceFetcher(params = multiParams) } returns multiResult
// Act
val actual = fetcher.invoke(params).isRight()
@ -53,26 +53,26 @@ internal class DefaultSingleYieldBalanceFetcherTest {
// Assert
Truth.assertThat(actual).isTrue()
coVerify { multiYieldBalanceFetcher(params = multiParams) }
coVerify { multiStakingBalanceFetcher(params = multiParams) }
}
@Test
fun `fetch yield balance failure`() = runTest {
fun `fetch staking balance failure`() = runTest {
// Arrange
val params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = tonId)
val params = SingleStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingId = tonId)
val multiParams = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = setOf(tonId))
val multiParams = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = setOf(tonId))
val multiResult = IllegalStateException().left()
coEvery { multiYieldBalanceFetcher(params = multiParams) } returns multiResult
coEvery { multiStakingBalanceFetcher(params = multiParams) } returns multiResult
// Act
val actual = fetcher.invoke(params)
// Assert
Truth.assertThat(actual).isEqualTo(multiResult)
coVerify { multiYieldBalanceFetcher(params = multiParams) }
coVerify { multiStakingBalanceFetcher(params = multiParams) }
}
private companion object {

View file

@ -4,12 +4,12 @@ import com.google.common.truth.Truth
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.data.staking.toDomain
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.domain.staking.multi.MultiStakingBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.clearMocks
@ -26,20 +26,20 @@ import org.junit.jupiter.api.TestInstance
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class DefaultSingleYieldBalanceProducerTest {
internal class DefaultSingleStakingBalanceProducerTest {
private val params = SingleYieldBalanceProducer.Params(
private val params = SingleStakingBalanceProducer.Params(
userWalletId = UserWalletId(stringValue = "011"),
stakingId = tonId,
)
private val multiNetworkStatusSupplier = mockk<MultiYieldBalanceSupplier>()
private val multiNetworkStatusSupplier = mockk<MultiStakingBalanceSupplier>()
private val analyticsExceptionHandler = mockk<AnalyticsExceptionHandler>(relaxUnitFun = true)
private val dispatchers = TestingCoroutineDispatcherProvider()
private val producer = DefaultSingleYieldBalanceProducer(
private val producer = DefaultSingleStakingBalanceProducer(
params = params,
multiYieldBalanceSupplier = multiNetworkStatusSupplier,
multiStakingBalanceSupplier = multiNetworkStatusSupplier,
analyticsExceptionHandler = analyticsExceptionHandler,
dispatchers = dispatchers,
)
@ -61,7 +61,7 @@ internal class DefaultSingleYieldBalanceProducerTest {
),
)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
val multiParams = MultiStakingBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
// Act
@ -74,17 +74,17 @@ internal class DefaultSingleYieldBalanceProducerTest {
}
@Test
fun `flow is updated if yield balance is updated`() = runTest {
fun `flow is updated if staking balance is updated`() = runTest {
// Arrange
val multiFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
val multiFlow = MutableSharedFlow<Set<StakingBalance>>(replay = 2, extraBufferCapacity = 1)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
val multiParams = MultiStakingBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val producerFlow = producer.produceWithFallback()
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
val updatedBalance = YieldBalance.Error(stakingId = tonId)
val updatedBalance = StakingBalance.Error(stakingId = tonId)
// Act (first emit)
multiFlow.emit(value = setOf(balance))
@ -108,9 +108,9 @@ internal class DefaultSingleYieldBalanceProducerTest {
@Test
fun `flow is filtered the same status`() = runTest {
// Arrange
val multiFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
val multiFlow = MutableSharedFlow<Set<StakingBalance>>(replay = 2, extraBufferCapacity = 1)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
val multiParams = MultiStakingBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val producerFlow = producer.produceWithFallback()
@ -153,7 +153,7 @@ internal class DefaultSingleYieldBalanceProducerTest {
}
.buffer(capacity = 5)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
val multiParams = MultiStakingBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val producerFlow = producer.produceWithFallback()
@ -162,7 +162,7 @@ internal class DefaultSingleYieldBalanceProducerTest {
val actual1 = getEmittedValues(flow = producerFlow)
// Assert (first emit)
val fallbackStatus = YieldBalance.Error(stakingId = tonId.copy(address = "0x1"))
val fallbackStatus = StakingBalance.Error(stakingId = tonId.copy(address = "0x1"))
Truth.assertThat(actual1).hasSize(1)
Truth.assertThat(actual1).containsExactly(fallbackStatus)
@ -184,7 +184,7 @@ internal class DefaultSingleYieldBalanceProducerTest {
val multiFlow = flowOf(setOf(balance))
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
val multiParams = MultiStakingBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val producerFlow = producer.produce()

View file

@ -5,7 +5,7 @@ import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.data.staking.toDomain
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
@ -15,12 +15,12 @@ import org.junit.Test
/**
[REDACTED_AUTHOR]
*/
internal class YieldsBalancesStoreGetMethodTest {
internal class StakingBalancesStoreGetMethodTest {
private val runtimeStore = RuntimeSharedStore<WalletIdWithBalances>()
private val runtimeStore = RuntimeSharedStore<WalletIdWithStakingBalances>()
private val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(default = emptyMap())
private val store = DefaultYieldsBalancesStore(
private val store = DefaultStakingBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
@ -32,7 +32,7 @@ internal class YieldsBalancesStoreGetMethodTest {
val values = getEmittedValues(flow = actual)
val expected = listOf(emptySet<YieldBalance>())
val expected = listOf(emptySet<StakingBalance>())
Truth.assertThat(values).isEqualTo(expected)
}
@ -44,7 +44,7 @@ internal class YieldsBalancesStoreGetMethodTest {
val values = getEmittedValues(flow = actual)
val expected = listOf(emptySet<YieldBalance>())
val expected = listOf(emptySet<StakingBalance>())
Truth.assertThat(values).isEqualTo(expected)
}
@ -59,7 +59,7 @@ internal class YieldsBalancesStoreGetMethodTest {
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(emptySet<YieldBalance>()))
Truth.assertThat(values).isEqualTo(listOf(emptySet<StakingBalance>()))
}
@Test

View file

@ -6,7 +6,7 @@ import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.data.staking.toDomain
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
@ -18,16 +18,16 @@ import org.junit.Test
/**
[REDACTED_AUTHOR]
*/
internal class YieldsBalancesStoreInitializationTest {
internal class StakingBalancesStoreInitializationTest {
@Test
fun `test initialization if cache store is empty`() = runTest {
val runtimeStore = RuntimeSharedStore<WalletIdWithBalances>()
val runtimeStore = RuntimeSharedStore<WalletIdWithStakingBalances>()
val persistenceStore: DataStore<WalletIdWithWrappers> = mockk()
every { persistenceStore.data } returns emptyFlow()
DefaultYieldsBalancesStore(
DefaultStakingBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
@ -38,21 +38,21 @@ internal class YieldsBalancesStoreInitializationTest {
@Test
fun `test initialization if cache store contains empty map`() = runTest {
val runtimeStore = RuntimeSharedStore<WalletIdWithBalances>()
val runtimeStore = RuntimeSharedStore<WalletIdWithStakingBalances>()
val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(default = emptyMap())
DefaultYieldsBalancesStore(
DefaultStakingBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptyMap<String, Set<YieldBalance>>())
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptyMap<String, Set<StakingBalance>>())
}
@Test
fun `test initialization if cache store is not empty`() = runTest {
val runtimeStore = RuntimeSharedStore<WalletIdWithBalances>()
val runtimeStore = RuntimeSharedStore<WalletIdWithStakingBalances>()
val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(default = emptyMap())
val wrapper = MockYieldBalanceWrapperDTOFactory.createWithBalance()
@ -63,7 +63,7 @@ internal class YieldsBalancesStoreInitializationTest {
}
}
DefaultYieldsBalancesStore(
DefaultStakingBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),

View file

@ -7,8 +7,8 @@ import com.tangem.data.staking.toDomain
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import kotlinx.coroutines.flow.firstOrNull
@ -18,12 +18,12 @@ import org.junit.Test
/**
[REDACTED_AUTHOR]
*/
internal class YieldsBalancesStoreUpdateMethodsTest {
internal class StakingBalancesStoreUpdateMethodsTest {
private val runtimeStore = RuntimeSharedStore<WalletIdWithBalances>()
private val runtimeStore = RuntimeSharedStore<WalletIdWithStakingBalances>()
private val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(default = emptyMap())
private val store = DefaultYieldsBalancesStore(
private val store = DefaultStakingBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
@ -33,7 +33,7 @@ internal class YieldsBalancesStoreUpdateMethodsTest {
fun `refresh the single id if runtime store is empty`() = runTest {
store.refresh(userWalletId = userWalletId, stakingId = stakingId)
val runtimeExpected = mapOf(userWalletId to emptySet<YieldBalance>())
val runtimeExpected = mapOf(userWalletId to emptySet<StakingBalance>())
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<YieldBalanceWrapperDTO>>())
@ -63,7 +63,7 @@ internal class YieldsBalancesStoreUpdateMethodsTest {
fun `refresh the multi ids if runtime store is empty`() = runTest {
store.refresh(userWalletId = userWalletId, stakingIds = stakingIds)
val runtimeExpected = mapOf(userWalletId to emptySet<YieldBalance>())
val runtimeExpected = mapOf(userWalletId to emptySet<StakingBalance>())
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<YieldBalanceWrapperDTO>>())
@ -129,7 +129,7 @@ internal class YieldsBalancesStoreUpdateMethodsTest {
store.storeError(userWalletId = userWalletId, stakingIds = setOf(stakingId))
val runtimeExpected = mapOf(
userWalletId to setOf(YieldBalance.Error(stakingId)),
userWalletId to setOf(StakingBalance.Error(stakingId)),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)

View file

@ -1,6 +1,6 @@
package com.tangem.data.staking.utils
import com.tangem.data.staking.store.YieldsBalancesStore
import com.tangem.data.staking.store.StakingBalancesStore
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingIntegrationID
@ -16,9 +16,9 @@ import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DefaultStakingCleanerTest {
private val yieldsBalancesStore = mockk<YieldsBalancesStore>(relaxed = true)
private val stakingBalancesStore = mockk<StakingBalancesStore>(relaxed = true)
private val cleaner = DefaultStakingCleaner(
yieldsBalancesStore = yieldsBalancesStore,
stakingBalancesStore = stakingBalancesStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
private val userWalletId = UserWalletId("011")
@ -28,7 +28,7 @@ class DefaultStakingCleanerTest {
@BeforeEach
fun setUp() {
clearMocks(yieldsBalancesStore)
clearMocks(stakingBalancesStore)
}
@Test
@ -38,7 +38,7 @@ class DefaultStakingCleanerTest {
// Assert
coVerifyOrder {
yieldsBalancesStore.clear(userWalletId = userWalletId, stakingIds = stakingIds)
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = stakingIds)
}
}
@ -49,7 +49,7 @@ class DefaultStakingCleanerTest {
// Assert
coVerifyOrder(inverse = true) {
yieldsBalancesStore.clear(userWalletId = any(), stakingIds = any())
stakingBalancesStore.clear(userWalletId = any(), stakingIds = any())
}
}
}

View file

@ -403,7 +403,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
value = NetworkStatus.MissedDerivation, // Caution!!! Do not change this status
).some(),
maybeQuoteStatus = quoteStatus.toOption(),
maybeYieldBalance = none(),
maybeStakingBalance = none(),
)
}

View file

@ -9,7 +9,7 @@ import com.tangem.data.tokens.converters.UtxoConverter
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.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.utils.getTotalStakingBalance
import com.tangem.domain.tokens.model.CurrencyAmount
@ -136,7 +136,7 @@ internal class DefaultCurrencyChecksRepository(
val rentData = walletManagersFacade.getRentInfo(userWalletId, currencyStatus.currency.network) ?: return null
val balanceValue = currencyStatus.value as? CryptoCurrencyStatus.Loaded ?: return null
val stakingBalance = balanceValue.yieldBalance as? YieldBalance.Data
val stakingBalance = balanceValue.stakingBalance as? StakingBalance.Data
val stakingTotalBalance = stakingBalance?.getTotalStakingBalance(
blockchainId = currencyStatus.currency.network.rawId,
).orZero()

View file

@ -53,7 +53,7 @@ internal class DefaultGetTangemPayCurrencyStatusUseCase @Inject constructor(
),
sources = CryptoCurrencyStatus.Sources(),
pendingTransactions = emptySet(),
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
),

View file

@ -1,7 +1,7 @@
==========================================
Detekt Baseline Updater & Issue Counter
==========================================
Date: 2025-12-03 22:08:49
Date: 2025-12-04 17:37:53
Step 1: Running detekt to check for new issues...
@ -17,13 +17,13 @@ Counting issues in baseline files...
==========================================
Summary:
Total Issues: 1385
Total Issues: 1380
Modules with Issues: 50
Average Issues per Module: 27
Progress:
Fixed: 417 out of 1802 (23%)
Remaining: 1385
Fixed: 422 out of 1802 (23%)
Remaining: 1380
==========================================
All Modules with Issues (sorted by count)
@ -38,18 +38,18 @@ features/swap/impl 67
features/send-v2/impl 57
data/wallet-connect 55
features/hot-wallet/impl 53
features/tokendetails/impl 48
features/staking/impl 48
features/walletconnect/impl 47
features/tokendetails/impl 46
features/staking/impl 46
features/manage-tokens/impl 45
features/swap-v2/impl 40
domain/wallets 37
features/nft/impl 34
features/tester/impl 30
core/ui 27
domain/tokens 26
common/ui 26
features/swap/domain 25
domain/tokens 25
data/visa 22
features/yield-supply/impl 21
features/tangempay/details/impl 21

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(),

View file

@ -10,7 +10,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.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository
@ -28,7 +28,7 @@ class SaveManagedTokensUseCase(
private val derivationsRepository: DerivationsRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
private val stakingIdFactory: StakingIdFactory,
private val parallelUpdatingScope: CoroutineScope,
) {
@ -70,7 +70,7 @@ class SaveManagedTokensUseCase(
)
}
launch {
refreshUpdatedYieldBalances(
refreshUpdatedStakingBalances(
userWalletId = userWalletId,
addedCurrencies = savedCurrencies,
)
@ -127,7 +127,7 @@ class SaveManagedTokensUseCase(
)
}
private suspend fun refreshUpdatedYieldBalances(
private suspend fun refreshUpdatedStakingBalances(
userWalletId: UserWalletId,
addedCurrencies: List<CryptoCurrency>,
) {
@ -135,8 +135,8 @@ class SaveManagedTokensUseCase(
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
}
multiYieldBalanceFetcher(
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
multiStakingBalanceFetcher(
params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
)
}

View file

@ -9,7 +9,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.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository
@ -36,7 +36,7 @@ class SaveMarketTokensUseCase(
private val currenciesRepository: CurrenciesRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
private val stakingIdFactory: StakingIdFactory,
private val parallelUpdatingScope: CoroutineScope,
) {
@ -85,7 +85,7 @@ class SaveMarketTokensUseCase(
syncTokens(userWalletId, savedCurrencies)
launch { refreshUpdatedNetworks(userWalletId, savedCurrencies) }
launch { refreshUpdatedYieldBalances(userWalletId, savedCurrencies) }
launch { refreshUpdatedStakingBalances(userWalletId, savedCurrencies) }
launch { refreshUpdatedQuotes(savedCurrencies) }
}
}
@ -121,7 +121,7 @@ class SaveMarketTokensUseCase(
)
}
private suspend fun refreshUpdatedYieldBalances(
private suspend fun refreshUpdatedStakingBalances(
userWalletId: UserWalletId,
existingCurrencies: List<CryptoCurrency>,
) {
@ -129,8 +129,8 @@ class SaveMarketTokensUseCase(
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
}
multiYieldBalanceFetcher(
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
multiStakingBalanceFetcher(
params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
)
}

View file

@ -5,7 +5,7 @@ import com.tangem.domain.models.getResultStatusSource
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
import kotlinx.serialization.Serializable
@ -56,13 +56,10 @@ data class CryptoCurrencyStatus(
/** The network address */
val networkAddress: NetworkAddress? get() = null
/** Staking yield balance */
val yieldBalance: YieldBalance? get() = null
/** Staking balance */
val stakingBalance: StakingBalance? get() = null
/**
* !!! DO NOT CONFUSE with STAKING YIELD BALANCE
* Yield supply status
*/
/** Yield supply status */
val yieldSupplyStatus: YieldSupplyStatus? get() = null
/** Sources */
@ -73,11 +70,11 @@ data class CryptoCurrencyStatus(
data class Sources(
val networkSource: StatusSource = StatusSource.ACTUAL,
val quoteSource: StatusSource = StatusSource.ACTUAL,
val yieldBalanceSource: StatusSource = StatusSource.ACTUAL,
val stakingBalanceSource: StatusSource = StatusSource.ACTUAL,
) {
val total: StatusSource by lazy {
listOf(networkSource, quoteSource, yieldBalanceSource).getResultStatusSource()
listOf(networkSource, quoteSource, stakingBalanceSource).getResultStatusSource()
}
}
@ -163,7 +160,7 @@ data class CryptoCurrencyStatus(
override val fiatAmount: SerializedBigDecimal,
override val fiatRate: SerializedBigDecimal,
override val priceChange: SerializedBigDecimal,
override val yieldBalance: YieldBalance?,
override val stakingBalance: StakingBalance?,
override val yieldSupplyStatus: YieldSupplyStatus?,
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxInfo>,
@ -191,7 +188,7 @@ data class CryptoCurrencyStatus(
override val fiatAmount: SerializedBigDecimal?,
override val fiatRate: SerializedBigDecimal?,
override val priceChange: SerializedBigDecimal?,
override val yieldBalance: YieldBalance?,
override val stakingBalance: StakingBalance?,
override val yieldSupplyStatus: YieldSupplyStatus?,
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxInfo>,
@ -213,7 +210,7 @@ data class CryptoCurrencyStatus(
@Serializable
data class NoQuote(
override val amount: SerializedBigDecimal,
override val yieldBalance: YieldBalance?,
override val stakingBalance: StakingBalance?,
override val yieldSupplyStatus: YieldSupplyStatus?,
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxInfo>,

View file

@ -0,0 +1,37 @@
package com.tangem.domain.models.staking
import com.tangem.domain.models.serialization.SerializedBigDecimal
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
/** P2P.org staking account */
@Serializable
data class P2PStakingAccount(
val delegatorAddress: String,
val vaultAddress: String,
val stake: P2PStake,
val availableToUnstake: SerializedBigDecimal,
val availableToWithdraw: SerializedBigDecimal,
val exitQueue: P2PExitQueue,
)
@Serializable
data class P2PStake(
val assets: SerializedBigDecimal,
val totalEarnedAssets: SerializedBigDecimal,
)
@Serializable
data class P2PExitQueue(
val total: SerializedBigDecimal,
val requests: List<P2PExitRequest>,
)
@Serializable
data class P2PExitRequest(
val ticket: String,
val totalAssets: SerializedBigDecimal,
val timestamp: Instant,
val withdrawalTimestamp: Instant,
val isClaimable: Boolean,
)

View file

@ -0,0 +1,101 @@
package com.tangem.domain.models.staking
import com.tangem.domain.models.StatusSource
import kotlinx.serialization.Serializable
import java.math.BigDecimal
/**
* Staking balance facade covering StakeKit and P2P balances
*/
@Serializable
sealed interface StakingBalance {
val stakingId: StakingID
val source: StatusSource
val totalStaked: BigDecimal
val totalRewards: BigDecimal?
val unstakingAmount: BigDecimal?
val withdrawableAmount: BigDecimal?
@Serializable
sealed interface Data : StakingBalance {
@Serializable
data class StakeKit(
override val stakingId: StakingID,
override val source: StatusSource,
val balance: YieldBalanceItem,
) : Data {
override val totalStaked: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.STAKED }
.sumOf { it.amount }
override val totalRewards: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
override val unstakingAmount: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.UNSTAKING || it.type == BalanceType.UNLOCKING }
.sumOf { it.amount }
override val withdrawableAmount: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.UNSTAKED }
.sumOf { it.amount }
}
@Serializable
data class P2P(
override val stakingId: StakingID,
override val source: StatusSource,
val account: P2PStakingAccount,
) : Data {
override val totalStaked: BigDecimal
get() = account.stake.assets
override val totalRewards: BigDecimal
get() = account.stake.totalEarnedAssets
override val unstakingAmount: BigDecimal
get() = account.exitQueue.total
override val withdrawableAmount: BigDecimal
get() = account.availableToWithdraw
}
}
@Serializable
data class Empty(
override val stakingId: StakingID,
override val source: StatusSource,
) : StakingBalance {
override val totalStaked: BigDecimal get() = BigDecimal.ZERO
override val totalRewards: BigDecimal? get() = null
override val unstakingAmount: BigDecimal? get() = null
override val withdrawableAmount: BigDecimal? get() = null
}
@Serializable
data class Error(override val stakingId: StakingID) : StakingBalance {
override val source: StatusSource get() = StatusSource.ACTUAL
override val totalStaked: BigDecimal get() = BigDecimal.ZERO
override val totalRewards: BigDecimal? get() = null
override val unstakingAmount: BigDecimal? get() = null
override val withdrawableAmount: BigDecimal? get() = null
}
fun copySealed(source: StatusSource): StakingBalance {
return when (this) {
is Data.StakeKit -> copy(source = source)
is Data.P2P -> copy(source = source)
is Empty -> copy(source = source)
is Error -> this
}
}
}

View file

@ -7,8 +7,8 @@
<ID>MultilineLambdaItParameter:InvalidatePendingTransactionsUseCase.kt$InvalidatePendingTransactionsUseCase${ !it.isPending &amp;&amp; action.amount &lt; it.amount &amp;&amp; it.type == BalanceType.STAKED &amp;&amp; it.validatorAddress == action.validatorAddress }</ID>
<ID>NamedArguments:GetConstructedStakingTransactionUseCase.kt$GetConstructedStakingTransactionUseCase$constructTransaction(networkId, fee, amount, transactionId)</ID>
<ID>NullableToStringCall:StakingApyFlowUseCase.kt$StakingApyFlowUseCase$${yield.token.coinGeckoId}</ID>
<ID>UnnecessaryAbstractClass:MultiYieldBalanceSupplier.kt$MultiYieldBalanceSupplier$MultiYieldBalanceSupplier</ID>
<ID>UnnecessaryAbstractClass:SingleYieldBalanceSupplier.kt$SingleYieldBalanceSupplier$SingleYieldBalanceSupplier</ID>
<ID>UnnecessaryAbstractClass:MultiStakingBalanceSupplier.kt$MultiStakingBalanceSupplier$MultiStakingBalanceSupplier</ID>
<ID>UnnecessaryAbstractClass:SingleStakingBalanceSupplier.kt$SingleStakingBalanceSupplier$SingleStakingBalanceSupplier</ID>
<ID>UseEmptyCounterpart:StakingAnalyticsEvent.kt$StakingAnalyticsEvent$mapOf()</ID>
<ID>UseOrEmpty:InvalidatePendingTransactionsUseCase.kt$InvalidatePendingTransactionsUseCase$action.validatorAddress ?: action.validatorAddresses?.getOrNull(0) ?: ""</ID>
</CurrentIssues>

View file

@ -40,8 +40,7 @@ sealed interface StakingOption {
* Wraps P2P ETH Pool vault information
*/
data class P2P(val vault: P2PEthPoolVault) : StakingOption {
override val integrationId: String =
"p2p-ethereum-pooled"
override val integrationId: String = "p2p-ethereum-pooled"
override val apy: SerializedBigDecimal = vault.apy
override val token: YieldToken = createEthToken()
override val isAvailable: Boolean = !vault.isPrivate

View file

@ -7,10 +7,10 @@ import arrow.core.right
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
class FetchStakingYieldBalanceUseCase(
private val singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
private val singleStakingBalanceFetcher: SingleStakingBalanceFetcher,
private val stakingIdFactory: StakingIdFactory,
) {
@ -32,8 +32,8 @@ class FetchStakingYieldBalanceUseCase(
return@either
}
singleYieldBalanceFetcher(
params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId),
singleStakingBalanceFetcher(
params = SingleStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId),
)
.mapLeft { StakingError.DomainError("$it") }
.bind()

View file

@ -5,17 +5,17 @@ import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
/**
* Params for fetchers of yield balance
* Params for fetchers of staking balance
*
[REDACTED_AUTHOR]
*/
sealed interface YieldBalanceFetcherParams {
sealed interface StakingBalanceFetcherParams {
/** User wallet ID */
val userWalletId: UserWalletId
/**
* Params for fetching multiple yield balances
* Params for fetching multiple staking balances
*
* @property userWalletId user wallet ID
* @property currencyIdWithNetworkMap map of currency ID to network
@ -23,10 +23,10 @@ sealed interface YieldBalanceFetcherParams {
data class Multi(
override val userWalletId: UserWalletId,
val currencyIdWithNetworkMap: Map<CryptoCurrency.ID, Network>,
) : YieldBalanceFetcherParams
) : StakingBalanceFetcherParams
/**
* Params for fetching single yield balance
* Params for fetching single staking balance
*
* @property userWalletId user wallet ID
* @property currencyId currency ID
@ -36,5 +36,5 @@ sealed interface YieldBalanceFetcherParams {
override val userWalletId: UserWalletId,
val currencyId: CryptoCurrency.ID,
val network: Network,
) : YieldBalanceFetcherParams
) : StakingBalanceFetcherParams
}

View file

@ -5,14 +5,14 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.staking.StakingID
/**
* Fetcher of yields balances
* Fetcher of staking balances
*
[REDACTED_AUTHOR]
*/
interface MultiYieldBalanceFetcher : FlowFetcher<MultiYieldBalanceFetcher.Params> {
interface MultiStakingBalanceFetcher : FlowFetcher<MultiStakingBalanceFetcher.Params> {
/**
* Params for fetching multiple yield balances
* Params for fetching multiple staking balances
*
* @property userWalletId user wallet ID
* @property stakingIds map of currency ID to network
@ -24,7 +24,7 @@ interface MultiYieldBalanceFetcher : FlowFetcher<MultiYieldBalanceFetcher.Params
override fun toString(): String {
return """
MultiYieldBalanceFetcher.Params(
MultiStakingBalanceFetcher.Params(
userWalletId = $userWalletId,
stakingIds: ${stakingIds.joinToString()}
)

View file

@ -0,0 +1,17 @@
package com.tangem.domain.staking.multi
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
/**
* Producer of all staking balances for selected wallet [UserWalletId]
*
[REDACTED_AUTHOR]
*/
interface MultiStakingBalanceProducer : FlowProducer<Set<StakingBalance>> {
data class Params(val userWalletId: UserWalletId)
interface Factory : FlowProducer.Factory<Params, MultiStakingBalanceProducer>
}

View file

@ -0,0 +1,18 @@
package com.tangem.domain.staking.multi
import com.tangem.domain.core.flow.FlowCachingSupplier
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.staking.StakingBalance
/**
* Supplier of all staking balances for selected wallet [MultiStakingBalanceProducer.Params]
*
* @property factory factory for creating [MultiStakingBalanceProducer]
* @property keyCreator key creator
*
[REDACTED_AUTHOR]
*/
abstract class MultiStakingBalanceSupplier(
override val factory: FlowProducer.Factory<MultiStakingBalanceProducer.Params, MultiStakingBalanceProducer>,
override val keyCreator: (MultiStakingBalanceProducer.Params) -> String,
) : FlowCachingSupplier<MultiStakingBalanceProducer, MultiStakingBalanceProducer.Params, Set<StakingBalance>>()

View file

@ -1,17 +0,0 @@
package com.tangem.domain.staking.multi
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
/**
* Producer of all yield balances for selected wallet [UserWalletId]
*
[REDACTED_AUTHOR]
*/
interface MultiYieldBalanceProducer : FlowProducer<Set<YieldBalance>> {
data class Params(val userWalletId: UserWalletId)
interface Factory : FlowProducer.Factory<Params, MultiYieldBalanceProducer>
}

View file

@ -1,18 +0,0 @@
package com.tangem.domain.staking.multi
import com.tangem.domain.core.flow.FlowCachingSupplier
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.staking.YieldBalance
/**
* Supplier of all yield balances for selected wallet [MultiYieldBalanceProducer.Params]
*
* @property factory factory for creating [MultiYieldBalanceProducer]
* @property keyCreator key creator
*
[REDACTED_AUTHOR]
*/
abstract class MultiYieldBalanceSupplier(
override val factory: FlowProducer.Factory<MultiYieldBalanceProducer.Params, MultiYieldBalanceProducer>,
override val keyCreator: (MultiYieldBalanceProducer.Params) -> String,
) : FlowCachingSupplier<MultiYieldBalanceProducer, MultiYieldBalanceProducer.Params, Set<YieldBalance>>()

View file

@ -5,14 +5,14 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.staking.StakingID
/**
* Fetcher of yield balance
* Fetcher of staking balance
*
[REDACTED_AUTHOR]
*/
interface SingleYieldBalanceFetcher : FlowFetcher<SingleYieldBalanceFetcher.Params> {
interface SingleStakingBalanceFetcher : FlowFetcher<SingleStakingBalanceFetcher.Params> {
/**
* Params for fetching single yield balance
* Params for fetching single staking balance
*
* @property userWalletId user wallet ID
* @property stakingId staking ID

View file

@ -2,15 +2,15 @@ package com.tangem.domain.staking.single
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
/**
* Producer of yield balance for selected wallet [UserWalletId]
* Producer of staking balance for selected wallet [UserWalletId]
*
[REDACTED_AUTHOR]
*/
interface SingleYieldBalanceProducer : FlowProducer<YieldBalance> {
interface SingleStakingBalanceProducer : FlowProducer<StakingBalance> {
data class Params(
val userWalletId: UserWalletId,
@ -19,7 +19,7 @@ interface SingleYieldBalanceProducer : FlowProducer<YieldBalance> {
override fun toString(): String {
return """
SingleYieldBalanceProducer.Params(
SingleStakingBalanceProducer.Params(
userWalletId = $userWalletId,
stakingId = $stakingId,
)
@ -27,5 +27,5 @@ interface SingleYieldBalanceProducer : FlowProducer<YieldBalance> {
}
}
interface Factory : FlowProducer.Factory<Params, SingleYieldBalanceProducer>
interface Factory : FlowProducer.Factory<Params, SingleStakingBalanceProducer>
}

View file

@ -0,0 +1,18 @@
package com.tangem.domain.staking.single
import com.tangem.domain.core.flow.FlowCachingSupplier
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.staking.StakingBalance
/**
* Supplier of staking balance for selected wallet [SingleStakingBalanceProducer.Params]
*
* @property factory factory for creating [SingleStakingBalanceProducer]
* @property keyCreator key creator
*
[REDACTED_AUTHOR]
*/
abstract class SingleStakingBalanceSupplier(
override val factory: FlowProducer.Factory<SingleStakingBalanceProducer.Params, SingleStakingBalanceProducer>,
override val keyCreator: (SingleStakingBalanceProducer.Params) -> String,
) : FlowCachingSupplier<SingleStakingBalanceProducer, SingleStakingBalanceProducer.Params, StakingBalance>()

View file

@ -1,18 +0,0 @@
package com.tangem.domain.staking.single
import com.tangem.domain.core.flow.FlowCachingSupplier
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.staking.YieldBalance
/**
* Supplier of yield balance for selected wallet [SingleYieldBalanceProducer.Params]
*
* @property factory factory for creating [SingleYieldBalanceProducer]
* @property keyCreator key creator
*
[REDACTED_AUTHOR]
*/
abstract class SingleYieldBalanceSupplier(
override val factory: FlowProducer.Factory<SingleYieldBalanceProducer.Params, SingleYieldBalanceProducer>,
override val keyCreator: (SingleYieldBalanceProducer.Params) -> String,
) : FlowCachingSupplier<SingleYieldBalanceProducer, SingleYieldBalanceProducer.Params, YieldBalance>()

View file

@ -0,0 +1,89 @@
package com.tangem.domain.staking.utils
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.lib.crypto.BlockchainUtils
import java.math.BigDecimal
/**
* Provider-agnostic extension to get total balance including rewards.
* Works for both StakeKit and P2P providers.
*
* Returns sum of all staking-related balances including rewards
* (staked + unstaking + withdrawable + rewards).
*
* When [BlockchainUtils.isIncludeStakingTotalBalance] is false, the staked balance
* is already included in the main wallet balance, so we only return rewards.
*/
fun StakingBalance.Data.getTotalWithRewardsStakingBalance(blockchainId: String): BigDecimal {
return when (this) {
is StakingBalance.Data.StakeKit -> getTotalWithRewardsStakingBalanceStakeKit(blockchainId)
is StakingBalance.Data.P2P -> {
val rewards = totalRewards
if (BlockchainUtils.isIncludeStakingTotalBalance(blockchainId)) {
totalStaked + unstakingAmount + withdrawableAmount + rewards
} else {
rewards
}
}
}
}
/**
* Provider-agnostic extension to get total staking balance excluding rewards.
* Works for both StakeKit and P2P providers.
*
* Returns sum of all staking-related balances (staked + unstaking + withdrawable)
* excluding rewards.
*/
fun StakingBalance.Data.getTotalStakingBalance(blockchainId: String): BigDecimal {
return when (this) {
is StakingBalance.Data.StakeKit -> getTotalStakingBalanceStakeKit(blockchainId)
is StakingBalance.Data.P2P -> totalStaked + unstakingAmount + withdrawableAmount
}
}
/**
* StakeKit-specific extension to get total balance including rewards.
*/
private fun StakingBalance.Data.StakeKit.getTotalWithRewardsStakingBalanceStakeKit(blockchainId: String): BigDecimal {
return if (BlockchainUtils.isIncludeStakingTotalBalance(blockchainId = blockchainId)) {
balance.items.sumOf { it.amount }
} else {
getRewardStakingBalance()
}
}
/**
* StakeKit-specific extension to get total staked balance excluding rewards.
*/
private fun StakingBalance.Data.StakeKit.getTotalStakingBalanceStakeKit(blockchainId: String): BigDecimal {
return if (BlockchainUtils.isIncludeStakingTotalBalance(blockchainId = blockchainId)) {
balance.items
.filterNot { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
} else {
balance.items
.filterNot { it.type == BalanceType.REWARDS }
.sumOf { it.amount } - getRewardStakingBalance()
}
}
/**
* StakeKit-specific extension to get reward balance.
*/
fun StakingBalance.Data.StakeKit.getRewardStakingBalance(): BigDecimal {
return balance.items
.filter { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
}
/**
* StakeKit-specific extension to get validators count.
*/
fun StakingBalance.Data.StakeKit.getValidatorsCount(): Int {
return balance.items
.filterNot { it.validatorAddress.isNullOrBlank() }
.distinctBy { it.validatorAddress }
.size
}

View file

@ -1,39 +0,0 @@
package com.tangem.domain.staking.utils
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.lib.crypto.BlockchainUtils
import java.math.BigDecimal
fun YieldBalance.Data.getTotalWithRewardsStakingBalance(blockchainId: String): BigDecimal {
return if (BlockchainUtils.isIncludeStakingTotalBalance(blockchainId = blockchainId)) {
balance.items.sumOf { it.amount }
} else {
getRewardStakingBalance()
}
}
fun YieldBalance.Data.getTotalStakingBalance(blockchainId: String): BigDecimal {
return if (BlockchainUtils.isIncludeStakingTotalBalance(blockchainId = blockchainId)) {
balance.items
.filterNot { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
} else {
balance.items
.filterNot { it.type == BalanceType.REWARDS }
.sumOf { it.amount } - getRewardStakingBalance()
}
}
fun YieldBalance.Data.getRewardStakingBalance(): BigDecimal {
return balance.items
.filter { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
}
fun YieldBalance.Data.getValidatorsCount(): Int {
return balance.items
.filterNot { it.validatorAddress.isNullOrBlank() }
.distinctBy { it.validatorAddress }
.size
}

View file

@ -18,7 +18,6 @@
<ID>MultilineLambdaItParameter:PriceChangeCalculator.kt$PriceChangeCalculator${ val weight = it.value.fiatAmount.orZero().divide(balance, 2, RoundingMode.HALF_UP) val priceChange = it.value.priceChange.orZero() weight * priceChange }</ID>
<ID>MultilineLambdaItParameter:WalletBalanceFetcher.kt$WalletBalanceFetcher${ val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it) if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${it.id}") } stakingId }</ID>
<ID>NamedArguments:ApplyTokenListSortingUseCase.kt$ApplyTokenListSortingUseCase$saveTokens(userWalletId, currencies, isGrouped, isSortedByBalance)</ID>
<ID>NamedArguments:CryptoCurrencyStatusFactory.kt$CryptoCurrencyStatusFactory$createStatus(currency, status, quoteStatus, maybeYieldBalance)</ID>
<ID>NamedArguments:GetCurrencyWarningsUseCase.kt$GetCurrencyWarningsUseCase$combine( getCoinRelatedWarnings( userWalletId = userWalletId, networkId = currency.network.id, currencyId = currency.id, derivationPath = derivationPath, isSingleWalletWithTokens = isSingleWalletWithTokens, ), flowOf(currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus)), flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)), flowOf(currencyChecksRepository.getFeeResourceAmount(userWalletId, currency.network)), ) { coinRelatedWarnings, maybeRentWarning, maybeEdWarning, maybeFeeResource -&gt; setOfNotNull( maybeRentWarning, maybeEdWarning?.let { getExistentialDepositWarning(currency, it) }, maybeFeeResource?.let { getFeeResourceWarning(it) }, * coinRelatedWarnings.toTypedArray(), getNetworkUnavailableWarning(currencyStatus), getNetworkNoAccountWarning(currencyStatus), getBeaconChainShutdownWarning(rawId = currency.network.id.rawId), getAssetRequirementsWarning(userWalletId = userWalletId, currency = currency), getMigrationFromMaticToPolWarning(currency), ) }</ID>
<ID>NoNameShadowing:WalletBalanceFetcher.kt$WalletBalanceFetcher${ it is StakingIdFactory.Error.UnableToGetAddress }</ID>
<ID>NullableBooleanCheck:GetCurrencyCheckUseCase.kt$GetCurrencyCheckUseCase$recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( userWalletId, network, recipientAddress, ) } ?: false</ID>

View file

@ -12,7 +12,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.single.SingleYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import kotlinx.coroutines.async
@ -32,7 +32,7 @@ class AddCryptoCurrenciesUseCase(
private val walletManagersFacade: WalletManagersFacade,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
private val singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
private val singleStakingBalanceFetcher: SingleStakingBalanceFetcher,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
) {
@ -82,7 +82,7 @@ class AddCryptoCurrenciesUseCase(
awaitAll(
async { refreshUpdatedNetworks(userWalletId, currencyToAdd, existingCurrencies) },
async { refreshUpdatedYieldBalances(userWalletId, currencyToAdd) },
async { refreshUpdatedStakingBalances(userWalletId, currencyToAdd) },
async { refreshUpdatedQuotes(currencyToAdd) },
)
}
@ -122,7 +122,7 @@ class AddCryptoCurrenciesUseCase(
awaitAll(
async { refreshUpdatedNetworks(userWalletId, tokenToAdd, existingCurrencies) },
async { refreshUpdatedYieldBalances(userWalletId, tokenToAdd) },
async { refreshUpdatedStakingBalances(userWalletId, tokenToAdd) },
async { refreshUpdatedQuotes(tokenToAdd) },
)
}
@ -177,7 +177,7 @@ class AddCryptoCurrenciesUseCase(
)
}
private suspend fun refreshUpdatedYieldBalances(
private suspend fun refreshUpdatedStakingBalances(
userWalletId: UserWalletId,
addedCurrency: CryptoCurrency,
): Either<Throwable, Unit> = either {
@ -195,8 +195,8 @@ class AddCryptoCurrenciesUseCase(
return@either
}
singleYieldBalanceFetcher(
params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId),
singleStakingBalanceFetcher(
params = SingleStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId),
)
.bind()
}

View file

@ -12,7 +12,7 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleStakingBalanceFetcher
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.repository.CurrenciesRepository
import kotlinx.coroutines.async
@ -32,7 +32,7 @@ class FetchCurrencyStatusUseCase(
private val currenciesRepository: CurrenciesRepository,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
private val singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
private val singleStakingBalanceFetcher: SingleStakingBalanceFetcher,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
) {
@ -149,8 +149,8 @@ class FetchCurrencyStatusUseCase(
return@either
}
singleYieldBalanceFetcher(
params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId),
singleStakingBalanceFetcher(
params = SingleStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId),
)
.bind()
}

View file

@ -6,7 +6,7 @@ import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
internal fun CurrenciesStatusesOperations.Error.mapToCurrencyError(): CurrencyStatusError {
return when (this) {
is CurrenciesStatusesOperations.Error.DataError -> CurrencyStatusError.DataError(this.cause)
is CurrenciesStatusesOperations.Error.EmptyYieldBalances,
is CurrenciesStatusesOperations.Error.EmptyStakingBalances,
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
is CurrenciesStatusesOperations.Error.EmptyQuotes,
is CurrenciesStatusesOperations.Error.EmptyCurrencies,

View file

@ -12,7 +12,7 @@ internal fun CurrenciesStatusesOperations.Error.mapToTokenListError(): TokenList
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
is CurrenciesStatusesOperations.Error.EmptyAddresses,
is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus,
is CurrenciesStatusesOperations.Error.EmptyYieldBalances,
is CurrenciesStatusesOperations.Error.EmptyStakingBalances,
-> TokenListError.EmptyTokens
}
}

View file

@ -10,8 +10,8 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.multi.MultiNetworkStatusProducer
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
@ -22,10 +22,10 @@ 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.model.isStakingSupported
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
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
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.error.TokenListError
@ -48,8 +48,8 @@ abstract class BaseCurrencyStatusOperations(
private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
private val singleStakingBalanceSupplier: SingleStakingBalanceSupplier,
private val multiStakingBalanceSupplier: MultiStakingBalanceSupplier,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
) {
@ -83,7 +83,7 @@ abstract class BaseCurrencyStatusOperations(
userWalletId: UserWalletId,
currency: CryptoCurrency,
includeQuotes: Boolean = true,
subscribeOnYieldBalance: Boolean = true,
subscribeOnStakingBalance: Boolean = true,
): Flow<Either<Error, CryptoCurrencyStatus>> {
val rawCurrencyId = currency.id.rawCurrencyId
@ -104,7 +104,7 @@ abstract class BaseCurrencyStatusOperations(
val isStakingSupported = currency.network.toBlockchain().isStakingSupported
val yieldBalanceFlow = if (isStakingSupported) {
val stakingBalanceFlow = if (isStakingSupported) {
val stakingId = stakingIdFactory.create(
userWalletId = userWalletId,
currencyId = currency.id,
@ -113,19 +113,19 @@ abstract class BaseCurrencyStatusOperations(
.getOrNull()
stakingId?.let {
getYieldBalance(userWalletId = userWalletId, stakingId = it)
getStakingBalance(userWalletId = userWalletId, stakingId = it)
}
} else {
null
}
return if (subscribeOnYieldBalance && yieldBalanceFlow != null) {
combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance ->
return if (subscribeOnStakingBalance && stakingBalanceFlow != null) {
combine(quoteFlow, statusFlow, stakingBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeStakingBalance ->
currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuoteStatus = maybeQuote,
maybeNetworkStatus = maybeNetworkStatus,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
}
} else {
@ -134,7 +134,7 @@ abstract class BaseCurrencyStatusOperations(
currency = currency,
maybeQuoteStatus = maybeQuote,
maybeNetworkStatus = maybeNetworkStatus,
maybeYieldBalance = null,
maybeStakingBalance = null,
)
}
}
@ -209,13 +209,13 @@ abstract class BaseCurrencyStatusOperations(
.firstOrNull()
.right()
val yieldBalances = getYieldBalanceSync(userWalletId, currency)
val stakingBalances = getStakingBalanceSync(userWalletId, currency)
return currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuoteStatus = quote,
maybeNetworkStatus = networkStatuses,
maybeYieldBalance = yieldBalances,
maybeStakingBalance = stakingBalances,
)
},
catch = { raise(Error.DataError(it)) },
@ -245,7 +245,7 @@ abstract class BaseCurrencyStatusOperations(
userWalletId = userWalletId,
currency = currency,
includeQuotes = includeQuotes,
subscribeOnYieldBalance = false,
subscribeOnStakingBalance = false,
)
}
@ -271,13 +271,13 @@ abstract class BaseCurrencyStatusOperations(
.orEmpty()
.right()
val yieldBalances = getYieldBalancesSync(userWalletId, nonEmptyCurrencies)
val stakingBalances = getStakingBalancesSync(userWalletId, nonEmptyCurrencies)
return currencyStatusProxyCreator.createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
maybeQuotes = quotes,
maybeNetworkStatuses = networkStatuses,
maybeYieldBalances = yieldBalances,
maybeStakingBalances = stakingBalances,
)
},
catch = { raise(Error.DataError(it)) },
@ -304,13 +304,13 @@ abstract class BaseCurrencyStatusOperations(
.firstOrNull()
.right()
val yieldBalances = getYieldBalanceSync(userWalletId, currency)
val stakingBalances = getStakingBalanceSync(userWalletId, currency)
return currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuoteStatus = quotes,
maybeNetworkStatus = networkStatus,
maybeYieldBalance = yieldBalances,
maybeStakingBalance = stakingBalances,
)
}
@ -338,16 +338,16 @@ abstract class BaseCurrencyStatusOperations(
.bind()
}
private fun getYieldBalance(userWalletId: UserWalletId, stakingId: StakingID): EitherFlow<Error, YieldBalance> {
return singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(
private fun getStakingBalance(userWalletId: UserWalletId, stakingId: StakingID): EitherFlow<Error, StakingBalance> {
return singleStakingBalanceSupplier(
params = SingleStakingBalanceProducer.Params(
userWalletId = userWalletId,
stakingId = stakingId,
),
)
.map<YieldBalance, Either<Error, YieldBalance>> { it.right() }
.map<StakingBalance, Either<Error, StakingBalance>> { it.right() }
.catch { emit(Error.DataError(it).left()) }
.onEmpty { emit(Error.EmptyYieldBalances.left()) }
.onEmpty { emit(Error.EmptyStakingBalances.left()) }
}
private fun getNetworkStatus(userWalletId: UserWalletId, network: Network): EitherFlow<Error, NetworkStatus> {
@ -389,32 +389,32 @@ abstract class BaseCurrencyStatusOperations(
.bind()
}
private suspend fun getYieldBalancesSync(
private suspend fun getStakingBalancesSync(
userWalletId: UserWalletId,
cryptoCurrencies: List<CryptoCurrency>,
): Either<Error.EmptyYieldBalances, List<YieldBalance>> = either {
): Either<Error.EmptyStakingBalances, List<StakingBalance>> = either {
val stakingIds = cryptoCurrencies.mapNotNull { cryptoCurrency ->
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrency)
.getOrNull()
}
ensure(stakingIds.isNotEmpty()) { Error.EmptyYieldBalances }
ensure(stakingIds.isNotEmpty()) { Error.EmptyStakingBalances }
val balances = multiYieldBalanceSupplier.getSyncOrNull(
params = MultiYieldBalanceProducer.Params(userWalletId = userWalletId),
val balances = multiStakingBalanceSupplier.getSyncOrNull(
params = MultiStakingBalanceProducer.Params(userWalletId = userWalletId),
)
.orEmpty()
.filter { it.stakingId in stakingIds }
ensure(balances.isNotEmpty()) { Error.EmptyYieldBalances }
ensure(balances.isNotEmpty()) { Error.EmptyStakingBalances }
balances
}
private suspend fun getYieldBalanceSync(
private suspend fun getStakingBalanceSync(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
): Either<Error, YieldBalance> = either {
): Either<Error, StakingBalance> = either {
val stakingId = stakingIdFactory.create(userWalletId, cryptoCurrency)
.mapLeft {
val exception = IllegalStateException("$it")
@ -422,14 +422,14 @@ abstract class BaseCurrencyStatusOperations(
}
.bind()
val yieldBalance = singleYieldBalanceSupplier.getSyncOrNull(
params = SingleYieldBalanceProducer.Params(
val yieldBalance = singleStakingBalanceSupplier.getSyncOrNull(
params = SingleStakingBalanceProducer.Params(
userWalletId = userWalletId,
stakingId = stakingId,
),
)
ensureNotNull(yieldBalance) { Error.EmptyYieldBalances }
ensureNotNull(yieldBalance) { Error.EmptyStakingBalances }
}
private suspend fun Raise<Error>.getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {

View file

@ -16,7 +16,7 @@ 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.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import com.tangem.domain.networks.single.SingleNetworkStatusProducer
@ -26,9 +26,9 @@ 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.model.StakingIntegrationID
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
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.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
@ -46,8 +46,8 @@ class CachedCurrenciesStatusesOperations(
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
private val singleStakingBalanceSupplier: SingleStakingBalanceSupplier,
multiStakingBalanceSupplier: MultiStakingBalanceSupplier,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
) : BaseCurrencyStatusOperations(
@ -56,8 +56,8 @@ class CachedCurrenciesStatusesOperations(
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
multiYieldBalanceSupplier = multiYieldBalanceSupplier,
singleStakingBalanceSupplier = singleStakingBalanceSupplier,
multiStakingBalanceSupplier = multiStakingBalanceSupplier,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
) {
@ -95,7 +95,7 @@ class CachedCurrenciesStatusesOperations(
currencies = currencies,
maybeNetworkStatuses = null,
maybeQuotes = null,
maybeYieldBalances = null,
maybeStakingBalances = null,
isUpdating = true,
)
@ -109,13 +109,13 @@ class CachedCurrenciesStatusesOperations(
fun createCurrenciesStatuses(
maybeQuotes: Either<TokenListError, Set<QuoteStatus>>,
maybeNetworkStatuses: Either<TokenListError, Set<NetworkStatus>>,
maybeYieldBalances: Either<TokenListError, List<YieldBalance>>,
maybeStakingBalances: Either<TokenListError, List<StakingBalance>>,
isUpdating: Boolean,
) = createCurrenciesStatuses(
currencies = currencies,
maybeQuotes = maybeQuotes,
maybeNetworkStatuses = maybeNetworkStatuses,
maybeYieldBalances = maybeYieldBalances,
maybeStakingBalances = maybeStakingBalances,
isUpdating = isUpdating,
)
@ -166,13 +166,13 @@ class CachedCurrenciesStatusesOperations(
currencies: NonEmptyList<CryptoCurrency>,
maybeQuotes: Either<TokenListError, Set<QuoteStatus>>?,
maybeNetworkStatuses: Either<TokenListError, Set<NetworkStatus>>?,
maybeYieldBalances: Either<TokenListError, List<YieldBalance>>?,
maybeStakingBalances: Either<TokenListError, List<StakingBalance>>?,
isUpdating: Boolean,
): Lce<TokenListError, List<CryptoCurrencyStatus>> = lce {
isLoading.set(isUpdating)
val networksStatuses = maybeNetworkStatuses?.bindEither()?.toNonEmptySetOrNull()
val yieldBalances = maybeYieldBalances?.bindEither()
val stakingBalances = maybeStakingBalances?.bindEither()
val quotes = recover({ maybeQuotes?.bind()?.toNonEmptySetOrNull() }) {
null
}
@ -180,25 +180,25 @@ class CachedCurrenciesStatusesOperations(
currencies.map { currency ->
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
val yieldBalance = findYieldBalanceOrNull(yieldBalances, currency, networkStatus)
val stakingBalance = findStakingBalanceOrNull(stakingBalances, currency, networkStatus)
val currencyStatus = CryptoCurrencyStatusFactory.create(
currency = currency,
maybeNetworkStatus = networkStatus.toOption(),
maybeQuoteStatus = quote.toOption(),
maybeYieldBalance = yieldBalance.toOption(),
maybeStakingBalance = stakingBalance.toOption(),
)
currencyStatus
}
}
private fun findYieldBalanceOrNull(
yieldBalances: List<YieldBalance>?,
private fun findStakingBalanceOrNull(
stakingBalances: List<StakingBalance>?,
currency: CryptoCurrency,
networkStatus: NetworkStatus?,
): YieldBalance? {
if (yieldBalances.isNullOrEmpty()) return null
): StakingBalance? {
if (stakingBalances.isNullOrEmpty()) return null
val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value
val address = networkStatus.getAddress()
@ -206,8 +206,8 @@ class CachedCurrenciesStatusesOperations(
return if (supportedIntegration != null && address != null) {
val stakingId = StakingID(integrationId = supportedIntegration, address = address)
yieldBalances.firstOrNull { it.stakingId == stakingId }
?: YieldBalance.Error(stakingId = stakingId)
stakingBalances.firstOrNull { it.stakingId == stakingId }
?: StakingBalance.Error(stakingId = stakingId)
} else {
null
}
@ -302,9 +302,9 @@ class CachedCurrenciesStatusesOperations(
private fun getYieldsBalancesUpdates(
userWalletId: UserWalletId,
cryptoCurrencies: Map<CryptoCurrency.ID, String?>,
): EitherFlow<TokenListError, List<YieldBalance>> {
): EitherFlow<TokenListError, List<StakingBalance>> {
return channelFlow {
val state = MutableStateFlow(emptyList<YieldBalance>())
val state = MutableStateFlow(emptyList<StakingBalance>())
val stakingIds = cryptoCurrencies.mapNotNullTo(hashSetOf()) { currencyWithAddress ->
stakingIdFactory.create(
@ -316,8 +316,11 @@ class CachedCurrenciesStatusesOperations(
stakingIds.onEach { stakingId ->
launch {
singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
singleStakingBalanceSupplier(
params = SingleStakingBalanceProducer.Params(
userWalletId = userWalletId,
stakingId = stakingId,
),
)
.onEach { balance ->
state.update { loadedBalances ->

View file

@ -8,12 +8,12 @@ import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.network.TxInfo
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.yield.supply.YieldSupplyStatus
import java.math.BigDecimal
/**
* Factory to create [CryptoCurrencyStatus] from [NetworkStatus], [QuoteStatus] and [YieldBalance].
* Factory to create [CryptoCurrencyStatus] from [NetworkStatus], [QuoteStatus] and [StakingBalance].
*
[REDACTED_AUTHOR]
*/
@ -26,25 +26,25 @@ object CryptoCurrencyStatusFactory {
get() = (this?.value as? QuoteStatus.Data)?.priceChange
/**
* Creates [CryptoCurrencyStatus] from [NetworkStatus], [QuoteStatus] and [YieldBalance].
* Creates [CryptoCurrencyStatus] from [NetworkStatus], [QuoteStatus] and [StakingBalance].
*
* @param maybeNetworkStatus An optional network status containing blockchain information.
* @param maybeQuoteStatus An optional quote status containing price information.
* @param maybeYieldBalance An optional yield balance containing staking information.
* @param maybeStakingBalance An optional staking balance containing staking information.
*/
fun create(
currency: CryptoCurrency,
maybeNetworkStatus: Option<NetworkStatus>,
maybeQuoteStatus: Option<QuoteStatus>,
maybeYieldBalance: Option<YieldBalance>,
maybeStakingBalance: Option<StakingBalance>,
): CryptoCurrencyStatus {
return CryptoCurrencyStatus(
currency = currency,
value = createStatus(
currency = currency,
maybeNetworkStatus = maybeNetworkStatus,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
maybeQuoteStatus = maybeQuoteStatus,
),
)
@ -54,7 +54,7 @@ object CryptoCurrencyStatusFactory {
currency: CryptoCurrency,
maybeNetworkStatus: Option<NetworkStatus>,
maybeQuoteStatus: Option<QuoteStatus>,
maybeYieldBalance: Option<YieldBalance>,
maybeStakingBalance: Option<StakingBalance>,
): CryptoCurrencyStatus.Value {
val quoteStatus = maybeQuoteStatus.getOrNull()
@ -62,7 +62,12 @@ object CryptoCurrencyStatusFactory {
is NetworkStatus.MissedDerivation -> createMissedDerivation(quoteStatus)
is NetworkStatus.Unreachable -> createUnreachable(status, quoteStatus)
is NetworkStatus.NoAccount -> createNoAccount(status, quoteStatus)
is NetworkStatus.Verified -> createStatus(currency, status, quoteStatus, maybeYieldBalance)
is NetworkStatus.Verified -> createStatus(
currency = currency,
status = status,
quoteStatus = quoteStatus,
maybeStakingBalance = maybeStakingBalance,
)
null -> CryptoCurrencyStatus.Loading
}
}
@ -106,7 +111,7 @@ object CryptoCurrencyStatusFactory {
currency: CryptoCurrency,
status: NetworkStatus.Verified,
quoteStatus: QuoteStatus?,
maybeYieldBalance: Option<YieldBalance>,
maybeStakingBalance: Option<StakingBalance>,
): CryptoCurrencyStatus.Value {
val amount = when (val amount = status.amounts[currency.id]) {
is NetworkStatus.Amount.Loaded -> amount.value
@ -118,7 +123,7 @@ object CryptoCurrencyStatusFactory {
}
}
val yieldBalance = maybeYieldBalance.getOrNull(id = currency.id, address = status.address)
val stakingBalance = maybeStakingBalance.getOrNull(id = currency.id, address = status.address)
if (currency is CryptoCurrency.Token && currency.isCustom) {
return createCustom(
@ -126,7 +131,7 @@ object CryptoCurrencyStatusFactory {
status = status,
amount = amount,
quoteStatus = quoteStatus,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
)
}
@ -138,7 +143,7 @@ object CryptoCurrencyStatusFactory {
status = status,
amount = amount,
quoteStatus = quoteStatus,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
)
}
is QuoteStatus.Data -> {
@ -147,7 +152,7 @@ object CryptoCurrencyStatusFactory {
status = status,
amount = amount,
quoteStatus = quoteValue,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
)
}
null -> CryptoCurrencyStatus.Loading
@ -166,7 +171,7 @@ object CryptoCurrencyStatusFactory {
status: NetworkStatus.Verified,
amount: BigDecimal,
quoteStatus: QuoteStatus?,
yieldBalance: YieldBalance.Data?,
stakingBalance: StakingBalance.Data?,
): CryptoCurrencyStatus.Custom {
return CryptoCurrencyStatus.Custom(
amount = amount,
@ -176,11 +181,11 @@ object CryptoCurrencyStatusFactory {
hasCurrentNetworkTransactions = status.hasCurrentNetworkTransactions(),
pendingTransactions = status.getCurrentTransactions(id),
networkAddress = status.address,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
yieldSupplyStatus = status.getYieldSupplyStatus(id),
sources = CryptoCurrencyStatus.Sources(
networkSource = status.source,
yieldBalanceSource = yieldBalance?.source ?: StatusSource.ACTUAL,
stakingBalanceSource = stakingBalance?.source ?: StatusSource.ACTUAL,
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
),
)
@ -191,18 +196,18 @@ object CryptoCurrencyStatusFactory {
status: NetworkStatus.Verified,
amount: BigDecimal,
quoteStatus: QuoteStatus?,
yieldBalance: YieldBalance.Data?,
stakingBalance: StakingBalance.Data?,
): CryptoCurrencyStatus.NoQuote {
return CryptoCurrencyStatus.NoQuote(
amount = amount,
hasCurrentNetworkTransactions = status.hasCurrentNetworkTransactions(),
pendingTransactions = status.getCurrentTransactions(id),
networkAddress = status.address,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
yieldSupplyStatus = status.getYieldSupplyStatus(id),
sources = CryptoCurrencyStatus.Sources(
networkSource = status.source,
yieldBalanceSource = yieldBalance?.source ?: StatusSource.ACTUAL,
stakingBalanceSource = stakingBalance?.source ?: StatusSource.ACTUAL,
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
),
)
@ -213,7 +218,7 @@ object CryptoCurrencyStatusFactory {
status: NetworkStatus.Verified,
amount: BigDecimal,
quoteStatus: QuoteStatus.Data,
yieldBalance: YieldBalance.Data?,
stakingBalance: StakingBalance.Data?,
): CryptoCurrencyStatus.Loaded {
return CryptoCurrencyStatus.Loaded(
amount = amount,
@ -223,11 +228,11 @@ object CryptoCurrencyStatusFactory {
hasCurrentNetworkTransactions = status.hasCurrentNetworkTransactions(),
pendingTransactions = status.getCurrentTransactions(id),
networkAddress = status.address,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
yieldSupplyStatus = status.getYieldSupplyStatus(id),
sources = CryptoCurrencyStatus.Sources(
networkSource = status.source,
yieldBalanceSource = yieldBalance?.source ?: StatusSource.ACTUAL,
stakingBalanceSource = stakingBalance?.source ?: StatusSource.ACTUAL,
quoteSource = quoteStatus.source,
),
)
@ -243,20 +248,34 @@ object CryptoCurrencyStatusFactory {
return yieldSupplyStatuses[id]
}
private fun Option<YieldBalance>.getOrNull(id: CryptoCurrency.ID, address: NetworkAddress): YieldBalance.Data? {
val yieldBalance = this.getOrNull() as? YieldBalance.Data ?: return null
private fun Option<StakingBalance>.getOrNull(
id: CryptoCurrency.ID,
address: NetworkAddress,
): StakingBalance.Data? {
return when (val stakingBalance = this.getOrNull()) {
is StakingBalance.Data.StakeKit -> {
val isCurrentAddressStaking = stakingBalance.stakingId.address == address.defaultAddress.value
val filteredTokenBalances = stakingBalance.balance.items.filter {
it.token.coinGeckoId == id.rawCurrencyId?.value
}
val isCurrentAddressStaking = yieldBalance.stakingId.address == address.defaultAddress.value
val filteredTokenBalances = yieldBalance.balance.items.filter {
it.token.coinGeckoId == id.rawCurrencyId?.value
}
return if (isCurrentAddressStaking && filteredTokenBalances.isNotEmpty()) {
yieldBalance.copy(
balance = yieldBalance.balance.copy(items = filteredTokenBalances),
)
} else {
null
if (isCurrentAddressStaking && filteredTokenBalances.isNotEmpty()) {
stakingBalance.copy(
balance = stakingBalance.balance.copy(items = filteredTokenBalances),
)
} else {
null
}
}
is StakingBalance.Data.P2P -> {
// TODO p2p
val isCurrentAddressStaking = stakingBalance.stakingId.address == address.defaultAddress.value
if (isCurrentAddressStaking) stakingBalance else null
}
is StakingBalance.Empty,
is StakingBalance.Error,
null,
-> null
}
}

View file

@ -16,6 +16,6 @@ class CurrenciesStatusesOperations {
data class DataError(val cause: Throwable) : Error()
data object EmptyYieldBalances : Error()
data object EmptyStakingBalances : Error()
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.domain.models.TokensGroupType
import com.tangem.domain.models.TokensSortType
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
@ -123,10 +123,10 @@ object TokenListFactory {
}
private fun CryptoCurrencyStatus.calculateBalance(): BigDecimal {
val yieldBalance = value.yieldBalance as? YieldBalance.Data
val totalYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance(currency.network.rawId).orZero()
val totalFiatYieldBalance = totalYieldBalance.multiply(value.fiatRate.orZero())
val stakingBalance = value.stakingBalance as? StakingBalance.Data
val totalStakingBalance = stakingBalance?.getTotalWithRewardsStakingBalance(currency.network.rawId).orZero()
val totalFiatStakingBalance = totalStakingBalance.multiply(value.fiatRate.orZero())
return value.fiatAmount?.plus(totalFiatYieldBalance).orZero()
return value.fiatAmount?.plus(totalFiatStakingBalance).orZero()
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.getResultStatusSource
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.lib.crypto.BlockchainUtils
@ -153,17 +153,17 @@ object TotalFiatBalanceCalculator {
}
private fun CryptoCurrencyStatus.Loaded.getFiatStakingBalance(blockchainId: String): BigDecimal {
val yieldBalance = yieldBalance as? YieldBalance.Data
val stakingBalance = yieldBalance?.getTotalWithRewardsStakingBalance(blockchainId).orZero()
val stakingBalanceData = stakingBalance as? StakingBalance.Data
val totalStakingBalance = stakingBalanceData?.getTotalWithRewardsStakingBalance(blockchainId).orZero()
return fiatRate.times(stakingBalance)
return fiatRate.times(totalStakingBalance)
}
private fun CryptoCurrencyStatus.Custom.getFiatStakingBalance(blockchainId: String): BigDecimal {
val yieldBalance = yieldBalance as? YieldBalance.Data
val stakingBalance = yieldBalance?.getTotalWithRewardsStakingBalance(blockchainId).orZero()
val stakingBalanceData = stakingBalance as? StakingBalance.Data
val totalStakingBalance = stakingBalanceData?.getTotalWithRewardsStakingBalance(blockchainId).orZero()
return fiatRate?.times(stakingBalance).orZero()
return fiatRate?.times(totalStakingBalance).orZero()
}
private inline fun TotalFiatBalance.fold(

View file

@ -11,8 +11,8 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
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.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.tokens.operations.CryptoCurrencyStatusFactory
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
@ -28,20 +28,20 @@ class CurrencyStatusProxyCreator {
currency: CryptoCurrency,
maybeQuoteStatus: Either<Error, QuoteStatus?>,
maybeNetworkStatus: Either<Error, NetworkStatus?>,
maybeYieldBalance: Either<Error, YieldBalance>?,
maybeStakingBalance: Either<Error, StakingBalance>?,
): Either<Error, CryptoCurrencyStatus> = either {
val networkStatus = maybeNetworkStatus.bind()
val quote = recover(
block = { maybeQuoteStatus.bind() },
recover = { null },
)
val yieldBalance = maybeYieldBalance?.getOrNull()
val stakingBalance = maybeStakingBalance?.getOrNull()
createCurrencyStatus(
currency = currency,
quoteStatus = quote,
networkStatus = networkStatus,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
)
}
@ -49,12 +49,12 @@ class CurrencyStatusProxyCreator {
currencies: NonEmptyList<CryptoCurrency>,
maybeQuotes: Either<Error, Set<QuoteStatus>>?,
maybeNetworkStatuses: Either<Error, Set<NetworkStatus>>,
maybeYieldBalances: Either<Error, List<YieldBalance>>,
maybeStakingBalances: Either<Error, List<StakingBalance>>,
): Either<Error, List<CryptoCurrencyStatus>> = either {
val networksStatuses = maybeNetworkStatuses.bind().toNonEmptySetOrNull()
val quoteStatuses: Set<QuoteStatus>? = maybeQuotes?.getOrNull()?.ifEmpty { null }
val yieldBalances = maybeYieldBalances.getOrNull()
val stakingBalances = maybeStakingBalances.getOrNull()
currencies.map { currency ->
val quote = quoteStatuses?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
@ -63,11 +63,11 @@ class CurrencyStatusProxyCreator {
val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value
val yieldBalance = if (supportedIntegration != null && address != null) {
val stakingBalance = if (supportedIntegration != null && address != null) {
val stakingId = StakingID(integrationId = supportedIntegration, address = address)
yieldBalances?.firstOrNull { it.stakingId == stakingId }
?: YieldBalance.Error(stakingId = stakingId)
stakingBalances?.firstOrNull { it.stakingId == stakingId }
?: StakingBalance.Error(stakingId = stakingId)
} else {
null
}
@ -76,7 +76,7 @@ class CurrencyStatusProxyCreator {
currency = currency,
quoteStatus = quote,
networkStatus = networkStatus,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
)
}
}
@ -85,13 +85,13 @@ class CurrencyStatusProxyCreator {
currency: CryptoCurrency,
quoteStatus: QuoteStatus?,
networkStatus: NetworkStatus?,
yieldBalance: YieldBalance?,
stakingBalance: StakingBalance?,
): CryptoCurrencyStatus {
return CryptoCurrencyStatusFactory.create(
currency = currency,
maybeNetworkStatus = networkStatus.toOption(),
maybeQuoteStatus = quoteStatus.toOption(),
maybeYieldBalance = yieldBalance.toOption(),
maybeStakingBalance = stakingBalance.toOption(),
)
}
}

View file

@ -9,7 +9,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.MultiWalletCryptoCurrenciesFetcher
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -31,7 +31,7 @@ import timber.log.Timber
* @property singleWalletBalanceFetcher balance fetcher of single-currency wallet
* @property multiNetworkStatusFetcher networks statuses fetcher
* @property multiQuoteStatusFetcher quotes statuses fetcher
* @property multiYieldBalanceFetcher yields balances fetcher
* @property multiStakingBalanceFetcher yields balances fetcher
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
@ -44,7 +44,7 @@ class WalletBalanceFetcher internal constructor(
private val singleWalletBalanceFetcher: BaseWalletBalanceFetcher,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
private val stakingIdFactory: StakingIdFactory,
private val dispatchers: CoroutineDispatcherProvider,
) : FlowFetcher<WalletBalanceFetcher.Params> {
@ -56,7 +56,7 @@ class WalletBalanceFetcher internal constructor(
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
) : this(
@ -71,7 +71,7 @@ class WalletBalanceFetcher internal constructor(
singleWalletBalanceFetcher = SingleWalletBalanceFetcher(currenciesRepository = currenciesRepository),
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
dispatchers = dispatchers,
)
@ -164,8 +164,8 @@ class WalletBalanceFetcher internal constructor(
val stakingIds = maybeStakingIds.mapNotNullTo(hashSetOf()) { it.getOrNull() }
if (stakingIds.isNotEmpty()) {
multiYieldBalanceFetcher(
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
multiStakingBalanceFetcher(
params = MultiStakingBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
)
.bind()
} else {

View file

@ -154,7 +154,7 @@ internal object MockTokensStates {
networkAddress = requireNotNull(
value = MockNetworks.verifiedNetworksStatuses.first { it.network == status.currency.network }.value as? NetworkStatus.Verified,
).address,
yieldBalance = null,
stakingBalance = null,
sources = CryptoCurrencyStatus.Sources(),
yieldSupplyStatus = null,
)
@ -166,7 +166,7 @@ internal object MockTokensStates {
pendingTransactions = emptySet(),
hasCurrentNetworkTransactions = false,
networkAddress = requireNotNull(networkStatus.value as? NetworkStatus.Verified).address,
yieldBalance = null,
stakingBalance = null,
sources = CryptoCurrencyStatus.Sources(),
yieldSupplyStatus = null,
)
@ -185,7 +185,7 @@ internal object MockTokensStates {
.first { it.network == status.currency.network }
.value as? NetworkStatus.Verified,
).address,
yieldBalance = null,
stakingBalance = null,
sources = CryptoCurrencyStatus.Sources(),
yieldSupplyStatus = null,
),

View file

@ -14,8 +14,8 @@ import com.tangem.domain.models.network.NetworkStatus.Amount
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.YieldBalanceItem
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
import com.tangem.domain.staking.model.StakingIntegrationID
@ -54,7 +54,7 @@ class CryptoCurrencyStatusFactoryTest {
inner class MissedDerivation {
private val networkStatus = NetworkStatus.MissedDerivation.toStatus()
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
private val maybeStakingBalance = none<StakingBalance>() // not relevant for this test
@Test
fun `network is MissedDerivation and QuoteStatus is Data`() {
@ -63,7 +63,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = fullQuote.toStatus().some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -82,7 +82,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = emptyQuoteStatus.some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -97,7 +97,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = none(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -112,7 +112,7 @@ class CryptoCurrencyStatusFactoryTest {
private val networkStatus = NetworkStatus.Unreachable(address = networkAddress).toStatus()
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
private val maybeStakingBalance = none<StakingBalance>() // not relevant for this test
@Test
fun `network is Unreachable and QuoteStatus is Data`() {
@ -121,7 +121,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = fullQuote.toStatus().some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -141,7 +141,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = emptyQuoteStatus.some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -161,7 +161,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = none(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -185,7 +185,7 @@ class CryptoCurrencyStatusFactoryTest {
source = StatusSource.ACTUAL,
).toStatus()
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
private val maybeStakingBalance = none<StakingBalance>() // not relevant for this test
@Test
fun `network is NoAccount and QuoteStatus is Data`() {
@ -194,7 +194,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = fullQuote.toStatus().some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -217,7 +217,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = emptyQuoteStatus.some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -239,7 +239,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = none(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -261,7 +261,7 @@ class CryptoCurrencyStatusFactoryTest {
private val networkStatus = createVerified(amounts = mapOf(currency.id to Amount.NotFound)).toStatus()
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
private val maybeStakingBalance = none<StakingBalance>() // not relevant for this test
@Test
fun `network is Verified with Amount is NotFound and QuoteStatus is Data`() {
@ -270,7 +270,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = fullQuote.toStatus().some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -289,7 +289,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = emptyQuoteStatus.some(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -304,7 +304,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = none(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -317,7 +317,7 @@ class CryptoCurrencyStatusFactoryTest {
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class Loading {
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
private val maybeStakingBalance = none<StakingBalance>() // not relevant for this test
@Test
fun `network is null`() {
@ -326,7 +326,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = none(),
maybeQuoteStatus = none(), // not relevant for this test,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -344,7 +344,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = none(), // not relevant for this test,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -364,7 +364,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = none(),
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -387,14 +387,14 @@ class CryptoCurrencyStatusFactoryTest {
fun `network is Verified, QuoteStatus is null, YieldBalance is null`() {
// Arrange
val maybeQuoteStatus = none<QuoteStatus>()
val maybeYieldBalance = none<YieldBalance>()
val maybeStakingBalance = none<StakingBalance>()
// Act
val actual = CryptoCurrencyStatusFactory.create(
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = maybeQuoteStatus,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -405,7 +405,7 @@ class CryptoCurrencyStatusFactoryTest {
fiatAmount = null,
fiatRate = null,
priceChange = null,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -420,7 +420,7 @@ class CryptoCurrencyStatusFactoryTest {
@Test
fun `network is Verified, QuoteStatus is Data, YieldBalance is Data`() {
// Arrange
val yieldBalance = YieldBalance.Data(
val stakeKitBalance = StakingBalance.Data.StakeKit(
stakingId = StakingID(
integrationId = StakingIntegrationID.StakeKit.Coin.Cardano.value,
address = networkAddress.defaultAddress.value,
@ -444,7 +444,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = fullQuote.toStatus().some(),
maybeYieldBalance = yieldBalance.some(),
maybeStakingBalance = stakeKitBalance.some(),
)
// Assert
@ -455,9 +455,9 @@ class CryptoCurrencyStatusFactoryTest {
fiatAmount = BigDecimal.TEN * fullQuote.fiatRate,
fiatRate = fullQuote.fiatRate,
priceChange = fullQuote.priceChange,
yieldBalance = yieldBalance.copy(
balance = yieldBalance.balance.copy(
items = yieldBalance.balance.items.subList(0, 1),
stakingBalance = stakeKitBalance.copy(
balance = stakeKitBalance.balance.copy(
items = stakeKitBalance.balance.items.subList(0, 1),
),
),
yieldSupplyStatus = null,
@ -490,20 +490,20 @@ class CryptoCurrencyStatusFactoryTest {
@Test
fun `network is Verified and YieldBalance is null`() {
// Arrange
val maybeYieldBalance = none<YieldBalance>()
val maybeStakingBalance = none<StakingBalance>()
// Act
val actual = CryptoCurrencyStatusFactory.create(
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = maybeQuoteStatus,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
val expected = CryptoCurrencyStatus.NoQuote(
amount = BigDecimal.TEN,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = yieldSupplyStatuses[currency.id]!!,
hasCurrentNetworkTransactions = true,
pendingTransactions = pendingTransactions[currency.id]!!,
@ -517,7 +517,7 @@ class CryptoCurrencyStatusFactoryTest {
@Test
fun `network is Verified and YieldBalance is Data`() {
// Arrange
val yieldBalance = YieldBalance.Data(
val stakeKitBalance = StakingBalance.Data.StakeKit(
stakingId = StakingID(
integrationId = StakingIntegrationID.StakeKit.Coin.Cardano.value,
address = networkAddress.defaultAddress.value,
@ -541,15 +541,15 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = maybeQuoteStatus,
maybeYieldBalance = yieldBalance.some(),
maybeStakingBalance = stakeKitBalance.some(),
)
// Assert
val expected = CryptoCurrencyStatus.NoQuote(
amount = BigDecimal.TEN,
yieldBalance = yieldBalance.copy(
balance = yieldBalance.balance.copy(
items = yieldBalance.balance.items.subList(0, 1),
stakingBalance = stakeKitBalance.copy(
balance = stakeKitBalance.balance.copy(
items = stakeKitBalance.balance.items.subList(0, 1),
),
),
yieldSupplyStatus = yieldSupplyStatuses[currency.id]!!,
@ -576,14 +576,14 @@ class CryptoCurrencyStatusFactoryTest {
@Test
fun `network is Verified and YieldBalance is null`() {
// Arrange
val maybeYieldBalance = none<YieldBalance>()
val maybeStakingBalance = none<StakingBalance>()
// Act
val actual = CryptoCurrencyStatusFactory.create(
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = maybeQuoteStatus,
maybeYieldBalance = maybeYieldBalance,
maybeStakingBalance = maybeStakingBalance,
)
// Assert
@ -592,7 +592,7 @@ class CryptoCurrencyStatusFactoryTest {
fiatAmount = BigDecimal.TEN * fullQuote.fiatRate,
fiatRate = fullQuote.fiatRate,
priceChange = fullQuote.priceChange,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -606,7 +606,7 @@ class CryptoCurrencyStatusFactoryTest {
@Test
fun `network is Verified and YieldBalance is Data`() {
// Arrange
val yieldBalance = YieldBalance.Data(
val stakeKitBalance = StakingBalance.Data.StakeKit(
stakingId = StakingID(
integrationId = StakingIntegrationID.StakeKit.Coin.Cardano.value,
address = networkAddress.defaultAddress.value,
@ -630,7 +630,7 @@ class CryptoCurrencyStatusFactoryTest {
currency = currency,
maybeNetworkStatus = networkStatus.some(),
maybeQuoteStatus = fullQuote.toStatus().some(),
maybeYieldBalance = yieldBalance.some(),
maybeStakingBalance = stakeKitBalance.some(),
)
// Assert
@ -639,9 +639,9 @@ class CryptoCurrencyStatusFactoryTest {
fiatAmount = BigDecimal.TEN * fullQuote.fiatRate,
fiatRate = fullQuote.fiatRate,
priceChange = fullQuote.priceChange,
yieldBalance = yieldBalance.copy(
balance = yieldBalance.balance.copy(
items = yieldBalance.balance.items.subList(0, 1),
stakingBalance = stakeKitBalance.copy(
balance = stakeKitBalance.balance.copy(
items = stakeKitBalance.balance.items.subList(0, 1),
),
),
yieldSupplyStatus = null,

View file

@ -137,7 +137,7 @@ class PriceChangeCalculatorTest {
fiatAmount = amount,
fiatRate = BigDecimal.ONE,
priceChange = priceChange,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),

View file

@ -11,7 +11,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.YieldBalanceItem
import io.mockk.every
import io.mockk.mockk
@ -192,7 +192,7 @@ class TotalFiatBalanceCalculatorTest {
createCustom(
currency = cryptoCurrencyFactory.cardano,
fiatAmount = BigDecimal.ZERO,
yieldBalance = createYieldBalance(
stakingBalance = createStakeKitBalance(
amount = BigDecimal(20),
// It is important to use `BalanceType.REWARDS` because Cardano should not include the full
// staking balance. See `getTotalWithRewardsStakingBalance`.
@ -207,7 +207,7 @@ class TotalFiatBalanceCalculatorTest {
createCustom(
currency = cryptoCurrencyFactory.stellar,
fiatAmount = BigDecimal.ONE,
yieldBalance = createYieldBalance(
stakingBalance = createStakeKitBalance(
amount = BigDecimal(9),
balanceType = BalanceType.STAKED,
),
@ -246,7 +246,7 @@ class TotalFiatBalanceCalculatorTest {
createLoaded(
currency = cryptoCurrencyFactory.cardano,
fiatAmount = BigDecimal.ZERO,
yieldBalance = createYieldBalance(
stakingBalance = createStakeKitBalance(
amount = BigDecimal(20),
// It is important to use `BalanceType.REWARDS` because Cardano should not include the full
// staking balance. See `getTotalWithRewardsStakingBalance`.
@ -261,7 +261,7 @@ class TotalFiatBalanceCalculatorTest {
createLoaded(
currency = cryptoCurrencyFactory.stellar,
fiatAmount = BigDecimal.ONE,
yieldBalance = createYieldBalance(
stakingBalance = createStakeKitBalance(
amount = BigDecimal(9),
balanceType = BalanceType.STAKED,
),
@ -497,7 +497,7 @@ class TotalFiatBalanceCalculatorTest {
currency = currency,
value = CryptoCurrencyStatus.NoQuote(
amount = BigDecimal.ONE,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -545,7 +545,7 @@ class TotalFiatBalanceCalculatorTest {
private fun createCustom(
currency: CryptoCurrency,
fiatAmount: BigDecimal?,
yieldBalance: YieldBalance? = null,
stakingBalance: StakingBalance.Data.StakeKit? = null,
): CryptoCurrencyStatus {
return CryptoCurrencyStatus(
currency = currency,
@ -554,7 +554,7 @@ class TotalFiatBalanceCalculatorTest {
fiatAmount = fiatAmount,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -567,7 +567,7 @@ class TotalFiatBalanceCalculatorTest {
private fun createLoaded(
currency: CryptoCurrency,
fiatAmount: BigDecimal,
yieldBalance: YieldBalance? = null,
stakingBalance: StakingBalance.Data.StakeKit? = null,
source: StatusSource = StatusSource.ACTUAL,
): CryptoCurrencyStatus {
return CryptoCurrencyStatus(
@ -577,7 +577,7 @@ class TotalFiatBalanceCalculatorTest {
fiatAmount = fiatAmount,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
yieldBalance = yieldBalance,
stakingBalance = stakingBalance,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -587,8 +587,8 @@ class TotalFiatBalanceCalculatorTest {
)
}
private fun createYieldBalance(amount: BigDecimal, balanceType: BalanceType): YieldBalance.Data {
return YieldBalance.Data(
private fun createStakeKitBalance(amount: BigDecimal, balanceType: BalanceType): StakingBalance.Data.StakeKit {
return StakingBalance.Data.StakeKit(
stakingId = mockk(),
source = StatusSource.ACTUAL,
balance = YieldBalanceItem(

View file

@ -12,7 +12,7 @@ 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.model.StakingIntegrationID
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.wallet.FetchingSource.*
import com.tangem.domain.tokens.wallet.implementor.MultiWalletBalanceFetcher
@ -41,7 +41,7 @@ internal class WalletBalanceFetcherTest {
private val singleWalletBalanceFetcher: SingleWalletBalanceFetcher = mockk()
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher = mockk()
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher = mockk()
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher = mockk()
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher = mockk()
private val stakingIdFactory: StakingIdFactory = mockk()
private val fetcher = WalletBalanceFetcher(
@ -51,7 +51,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher = singleWalletBalanceFetcher,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@ -65,7 +65,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher,
multiNetworkStatusFetcher,
multiQuoteStatusFetcher,
multiYieldBalanceFetcher,
multiStakingBalanceFetcher,
)
}
@ -91,7 +91,7 @@ internal class WalletBalanceFetcherTest {
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -122,7 +122,7 @@ internal class WalletBalanceFetcherTest {
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -156,7 +156,7 @@ internal class WalletBalanceFetcherTest {
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -188,7 +188,7 @@ internal class WalletBalanceFetcherTest {
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -234,7 +234,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
multiQuoteStatusFetcher(params = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -280,7 +280,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
multiNetworkStatusFetcher(params = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -292,7 +292,7 @@ internal class WalletBalanceFetcherTest {
}
val currencies = cryptoCurrencyFactory.ethereumAndStellar.toSet()
val yieldBalanceFetcherParams = MultiYieldBalanceFetcher.Params(
val stakingBalanceFetcherParams = MultiStakingBalanceFetcher.Params(
userWalletId = userWalletId,
stakingIds = setOf(ethereumStakingId, stellarStakingId),
)
@ -308,7 +308,7 @@ internal class WalletBalanceFetcherTest {
coEvery {
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar)
} returns Either.Right(stellarStakingId)
coEvery { multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } returns exception.left()
coEvery { multiStakingBalanceFetcher(params = stakingBalanceFetcherParams) } returns exception.left()
// Act
val actual = fetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId))
@ -326,7 +326,7 @@ internal class WalletBalanceFetcherTest {
multiWalletBalanceFetcher.fetchingSources
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum)
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar)
multiYieldBalanceFetcher(params = yieldBalanceFetcherParams)
multiStakingBalanceFetcher(params = stakingBalanceFetcherParams)
}
coVerify(inverse = true) {
@ -372,7 +372,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -414,7 +414,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -462,7 +462,7 @@ internal class WalletBalanceFetcherTest {
singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
multiNetworkStatusFetcher(params = any())
multiQuoteStatusFetcher(params = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -485,7 +485,7 @@ internal class WalletBalanceFetcherTest {
appCurrencyId = null,
)
val yieldBalanceFetcherParams = MultiYieldBalanceFetcher.Params(
val stakingBalanceFetcherParams = MultiStakingBalanceFetcher.Params(
userWalletId = userWalletId,
stakingIds = setOf(ethereumStakingId, stellarStakingId),
)
@ -503,7 +503,7 @@ internal class WalletBalanceFetcherTest {
coEvery {
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar)
} returns Either.Right(stellarStakingId)
coEvery { multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } returns exception.left()
coEvery { multiStakingBalanceFetcher(params = stakingBalanceFetcherParams) } returns exception.left()
// Act
val actual = fetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId))
@ -525,7 +525,7 @@ internal class WalletBalanceFetcherTest {
multiQuoteStatusFetcher(params = quoteStatusFetcherParams)
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum)
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar)
multiYieldBalanceFetcher(params = yieldBalanceFetcherParams)
multiStakingBalanceFetcher(params = stakingBalanceFetcherParams)
}
coVerify(inverse = true) {
@ -553,7 +553,7 @@ internal class WalletBalanceFetcherTest {
appCurrencyId = null,
)
val yieldBalanceFetcherParams = MultiYieldBalanceFetcher.Params(
val stakingBalanceFetcherParams = MultiStakingBalanceFetcher.Params(
userWalletId = userWalletId,
stakingIds = setOf(ethereumStakingId, stellarStakingId),
)
@ -569,7 +569,7 @@ internal class WalletBalanceFetcherTest {
coEvery {
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar)
} returns Either.Right(stellarStakingId)
coEvery { multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } returns Unit.right()
coEvery { multiStakingBalanceFetcher(params = stakingBalanceFetcherParams) } returns Unit.right()
// Act
val actual = fetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId))
@ -586,7 +586,7 @@ internal class WalletBalanceFetcherTest {
multiQuoteStatusFetcher(params = quoteStatusFetcherParams)
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum)
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar)
multiYieldBalanceFetcher(params = yieldBalanceFetcherParams)
multiStakingBalanceFetcher(params = stakingBalanceFetcherParams)
}
coVerify(inverse = true) {
@ -642,7 +642,7 @@ internal class WalletBalanceFetcherTest {
multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}
@ -692,7 +692,7 @@ internal class WalletBalanceFetcherTest {
multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any())
singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any())
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
multiYieldBalanceFetcher(params = any())
multiStakingBalanceFetcher(params = any())
}
}

View file

@ -51,7 +51,7 @@ class YieldSupplyMinAmountUseCaseTest {
fiatAmount = BigDecimal.ZERO,
fiatRate = tokenFiatRate,
priceChange = BigDecimal.ZERO,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -103,7 +103,7 @@ class YieldSupplyMinAmountUseCaseTest {
fiatAmount = null,
fiatRate = null,
priceChange = null,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -131,7 +131,7 @@ class YieldSupplyMinAmountUseCaseTest {
fiatAmount = BigDecimal.ZERO,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),

View file

@ -59,7 +59,7 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
fiatAmount = null,
fiatRate = BigDecimal.ONE,
priceChange = null,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -90,7 +90,7 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
fiatAmount = null,
fiatRate = BigDecimal.ONE,
priceChange = null,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
@ -135,7 +135,7 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
fiatAmount = null,
fiatRate = BigDecimal.ONE,
priceChange = null,
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),

View file

@ -168,7 +168,7 @@ class FeeCalculationUtilsTest {
fiatAmount = BigDecimal.ZERO,
fiatRate = BigDecimal.ZERO,
priceChange = BigDecimal.ZERO,
yieldBalance = null,
stakingBalance = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
networkAddress = mockk(relaxed = true),

View file

@ -5,7 +5,6 @@
<ID>BooleanPropertyNaming:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer$val showNotification = sendingAmount + feeAmount &gt; balance</ID>
<ID>BooleanPropertyNaming:AmountCurrencyChangeStateTransformer.kt$AmountCurrencyChangeStateTransformer$private val value: Boolean</ID>
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noBalanceState = balanceState == null</ID>
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data</ID>
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingStates.InitialInfoState.Data$val showBanner: Boolean</ID>
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingUiState$val showColdWalletInteractionIcon: Boolean</ID>
<ID>CanBeNonNullable:StakingScreen.kt$bottomSheetConfig: TangemBottomSheetConfig?</ID>
@ -42,7 +41,6 @@
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(PRIVACY_POLICY_URL) }</ID>
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(TERMS_OF_USE_URL) }</ID>
<ID>UnsafeCallOnNullableType:StakingModel.kt$StakingModel$tonAccountInitializeTransaction!!</ID>
<ID>UseOrEmpty:StakingModel.kt$StakingModel$yieldBalance?.balance?.items ?: emptyList()</ID>
<ID>UseOrEmpty:StakingTransactionSender.kt$StakingTransactionSender$getExplorerTransactionUrlUseCase( txHash = transactionHashes.last(), networkId = cryptoCurrencyStatus.currency.network.id, ).getOrNull() ?: ""</ID>
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var actionsJobHolder: JobHolder = JobHolder()</ID>
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var approvalJobHolder: JobHolder = JobHolder()</ID>

View file

@ -190,9 +190,9 @@ internal class StakingModel @Inject constructor(
private val balancesToShow: List<BalanceItem>
get() {
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
return invalidatePendingTransactionsUseCase(
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
balanceItems = stakeKitBalance?.balance?.items.orEmpty(),
stakingActions = stakingActions,
token = yield.token,
).getOrElse { emptyList() }
@ -277,10 +277,10 @@ internal class StakingModel @Inject constructor(
modelScope.launch {
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
val noBalanceState = balanceState == null
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
val hasNoYieldBalanceData = cryptoCurrencyStatus.value.stakingBalance !is StakingBalance.Data.StakeKit
when {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && hasNoYieldBalanceData -> {
stakingEventFactory.createStakingValidatorsUnavailableAlert()
return@launch
}
@ -1116,7 +1116,7 @@ internal class StakingModel @Inject constructor(
private suspend fun onDataLoaded(status: CryptoCurrencyStatus) {
if (!isInitialInfoAnalyticSent) {
isInitialInfoAnalyticSent = true
val balances = status.value.yieldBalance as? YieldBalance.Data
val balances = status.value.stakingBalance as? StakingBalance.Data.StakeKit
paramsInterceptorHolder.addParamsInterceptor(
interceptor = StakingParamsInterceptor(status.currency.symbol),
)

View file

@ -10,7 +10,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.BalanceType.Companion.isClickable
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.utils.getRewardStakingBalance
@ -77,11 +77,11 @@ internal class BalanceItemConverter(
val isIncludeStakingTotalBalance = BlockchainUtils.isIncludeStakingTotalBalance(
blockchainId = cryptoCurrencyStatus.currency.network.rawId,
)
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
return if (isIncludeStakingTotalBalance) {
amount
} else {
amount - yieldBalance?.getRewardStakingBalance().orZero()
amount - stakeKitBalance?.getRewardStakingBalance().orZero()
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
@ -24,9 +24,9 @@ internal class RewardsValidatorStateConverter(
private val yield: Yield,
) : Converter<Unit, StakingStates.RewardsValidatorsState> {
override fun convert(value: Unit): StakingStates.RewardsValidatorsState {
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
return if (yieldBalance is YieldBalance.Data) {
val balances = yieldBalance.balance.items
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
return if (stakingBalance is StakingBalance.Data.StakeKit) {
val balances = stakingBalance.balance.items
StakingStates.RewardsValidatorsState.Data(
isPrimaryButtonEnabled = true,
rewards = balances
@ -35,6 +35,7 @@ internal class RewardsValidatorStateConverter(
.toPersistentList(),
)
} else {
// TODO p2p
StakingStates.RewardsValidatorsState.Empty()
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.RewardBlockType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.utils.getRewardStakingBalance
@ -36,11 +36,11 @@ internal class YieldBalancesConverter(
val appCurrency = appCurrencyProvider()
val cryptoCurrency = cryptoCurrencyStatus.currency
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
val balanceToShowItems = balancesToShowProvider()
return if (yieldBalance != null || balanceToShowItems.any { it.isPending }) {
val cryptoRewardsValue = yieldBalance?.getRewardStakingBalance()
return if (stakeKitBalance != null || balanceToShowItems.any { it.isPending }) {
val cryptoRewardsValue = stakeKitBalance?.getRewardStakingBalance()
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
@ -49,13 +49,13 @@ internal class YieldBalancesConverter(
null
}
val type = getRewardBlockType()
val pendingRewardsConstraints = yieldBalance?.balance?.items
val pendingRewardsConstraints = stakeKitBalance?.balance?.items
?.firstOrNull { it.type == BalanceType.REWARDS }
?.pendingActionsConstraints
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
InnerYieldBalanceState.Data(
integrationId = yieldBalance?.stakingId?.integrationId,
integrationId = stakeKitBalance?.stakingId?.integrationId,
reward = YieldReward(
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
rewardsFiat = fiatRewardsValue.format {
@ -71,6 +71,7 @@ internal class YieldBalancesConverter(
balances = balanceToShowItems.mapBalances(),
)
} else {
// TODO p2p
InnerYieldBalanceState.Empty
}
}
@ -84,8 +85,8 @@ internal class YieldBalancesConverter(
private fun getRewardBlockType(): RewardBlockType {
val blockchainId = cryptoCurrencyStatus.currency.network.rawId
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val rewards = yieldBalance?.balance?.items
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
val rewards = stakeKitBalance?.balance?.items
?.filter { it.type == BalanceType.REWARDS && !it.amount.isZero() }
val isActionable = rewards?.any { it.pendingActions.isNotEmpty() } == true

View file

@ -35,7 +35,7 @@ internal class SetConfirmationStateAssentTransformer(
isFeeApproximate = isFeeApproximate,
),
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
)
} else {

View file

@ -26,7 +26,7 @@ internal class SetConfirmationStateCompletedTransformer(
return if (this is StakingStates.ConfirmationState.Data) {
copy(
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
innerState = InnerConfirmationStakingState.COMPLETED,
footerText = TextReference.EMPTY,

View file

@ -15,7 +15,7 @@ internal class SetConfirmationStateResetAssentTransformer(
confirmationState = if (confirmationState is StakingStates.ConfirmationState.Data) {
confirmationState.copy(
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
innerState = InnerConfirmationStakingState.ASSENT,
)

View file

@ -90,7 +90,7 @@ internal class SetInitialDataStateTransformer(
val status = cryptoCurrencyStatus.value
return StakingStates.InitialInfoState.Data(
isPrimaryButtonEnabled = with(status) {
!amount.isNullOrZero() && sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
!amount.isNullOrZero() && sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
showBanner = !isAnyTokenStaked && yieldBalance == InnerYieldBalanceState.Empty,
infoItems = getInfoItems(),

View file

@ -37,7 +37,7 @@ internal class SetConfirmationStateAssentApprovalTransformer(
isFeeApproximate = false,
),
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
isApprovalNeeded = true,
)

View file

@ -127,7 +127,7 @@ internal class AddStakingNotificationsTransformer(
}
val isActualSources = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
}
return prevState.copy(

View file

@ -6,7 +6,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
@ -134,7 +134,7 @@ internal class StakingInfoNotificationsFactory(
private fun MutableList<NotificationUM>.addTronRevoteNotification() {
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val isTron = isTron(cryptoCurrencyStatus.currency.network.rawId)
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
val hasStakedBalance = (cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit)?.balance
?.items?.any {
it.type == BalanceType.PREPARING ||
it.type == BalanceType.STAKED ||

View file

@ -36,7 +36,6 @@
<ID>NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$tokenId</ID>
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingCryptoAmount</ID>
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingEntryInfo</ID>
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$yieldBalance</ID>
<ID>PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$network</ID>
<ID>PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$token</ID>
<ID>PropertyUsedBeforeDeclaration:TokenDetailsModel.kt$TokenDetailsModel$uiState</ID>
@ -48,7 +47,6 @@
<ID>UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent.Notice$mapOf()</ID>
<ID>UseOrEmpty:ExchangeStatusFactory.kt$ExchangeStatusFactory$savedTransactions ?.flatMap { setOf(it.fromCryptoCurrency.id, it.toCryptoCurrency.id) } ?.toSet() ?.getQuotesOrEmpty() ?: emptySet()</ID>
<ID>UseOrEmpty:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$yieldBalance?.balance?.items ?: emptyList()</ID>
<ID>VarCouldBeVal:TokenDetailsModel.kt$TokenDetailsModel$private var expressTxStatusTaskScheduler = SingleTaskScheduler&lt;PersistentList&lt;ExpressTransactionStateUM&gt;&gt;()</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
@ -29,8 +29,8 @@ internal class TokenDetailsBalanceSelectStateConverter(
return this
}
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakingCryptoAmount = yieldBalance?.getTotalWithRewardsStakingBalance(
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data
val stakingCryptoAmount = stakingBalance?.getTotalWithRewardsStakingBalance(
cryptoCurrencyStatus.currency.network.rawId,
)
val stakingFiatAmount = stakingCryptoAmount?.let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) }

View file

@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
@ -73,7 +73,7 @@ internal class TokenDetailsLoadedBalanceConverter(
status: CryptoCurrencyStatus,
): TokenDetailsBalanceBlockState {
val stakingCryptoAmount =
(status.value.yieldBalance as? YieldBalance.Data)?.getTotalWithRewardsStakingBalance(
(status.value.stakingBalance as? StakingBalance.Data)?.getTotalWithRewardsStakingBalance(
status.currency.network.rawId,
)
val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) }

View file

@ -10,7 +10,7 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.RewardBlockType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.utils.getRewardStakingBalance
@ -54,18 +54,29 @@ internal class TokenDetailsStakingInfoConverter(
}
}
@Suppress("CyclomaticComplexMethod")
private fun getStakingInfoBlock(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? {
val yieldBalance = status.value.yieldBalance as? YieldBalance.Data
val stakingBalance = status.value.stakingBalance as? StakingBalance.Data
val stakingCryptoAmount = yieldBalance?.getTotalStakingBalance(status.currency.network.rawId)
val pendingBalances = yieldBalance?.balance?.items ?: emptyList()
val stakingCryptoAmount = stakingBalance?.getTotalStakingBalance(status.currency.network.rawId)
val hasPendingBalances = when (stakingBalance) {
is StakingBalance.Data.StakeKit -> stakingBalance.balance.items.isNotEmpty()
is StakingBalance.Data.P2P -> !stakingBalance.unstakingAmount.isNullOrZero()
null -> false
}
val pendingAmount = when (stakingBalance) {
is StakingBalance.Data.StakeKit -> stakingBalance.balance.items.sumOf { it.amount }
is StakingBalance.Data.P2P -> stakingBalance.unstakingAmount
null -> BigDecimal.ZERO
}
val iconState = state.tokenInfoBlockState.iconState
Timber.i(
"""
getStakingInfoBlock:
yieldBalance: $yieldBalance
stakingBalance: ${stakingBalance ?: "null"}
stakingCryptoAmount: $stakingCryptoAmount
stakingEntryInfo: $stakingEntryInfo
""".trimIndent(),
@ -73,16 +84,24 @@ internal class TokenDetailsStakingInfoConverter(
return when {
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> {
if (pendingBalances.isEmpty()) {
if (!hasPendingBalances) {
getStakeAvailableState(stakingEntryInfo, iconState, isStakingButtonEnabled(status))
} else {
getStakedBlockWithFiatAmount(status, pendingBalances.sumOf { it.amount }, null)
getStakedBlockWithFiatAmount(status, pendingAmount, null)
}
}
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> {
null
}
else -> getStakedBlockWithFiatAmount(status, stakingCryptoAmount, yieldBalance?.getRewardStakingBalance())
else -> getStakedBlockWithFiatAmount(
status = status,
stakingAmount = stakingCryptoAmount,
rewardAmount = when (stakingBalance) {
is StakingBalance.Data.StakeKit -> stakingBalance.getRewardStakingBalance()
is StakingBalance.Data.P2P -> stakingBalance.totalRewards
else -> BigDecimal.ZERO
},
)
}
}

View file

@ -8,7 +8,7 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
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.isLocked
@ -239,7 +239,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
token = currencyStatus.currency.symbol,
blockchain = currencyStatus.currency.network.name,
action = "Staking",
state = if (currencyStatus.value.yieldBalance is YieldBalance.Data) {
state = if (currencyStatus.value.stakingBalance is StakingBalance.Data) {
"Enabled"
} else {
"Disabled"

Some files were not shown because too many files have changed in this diff Show more