Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-25 16:16:39 +04:00
parent 8ec30bd373
commit b2a199a568
10 changed files with 177 additions and 187 deletions

View file

@ -210,4 +210,10 @@ internal object StakingDomainModule {
fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase {
return GetActionRequirementAmountUseCase()
}
@Provides
@Singleton
fun provideStakingIdFactory(walletManagersFacade: WalletManagersFacade): StakingIdFactory {
return StakingIdFactory(walletManagersFacade = walletManagersFacade)
}
}

View file

@ -11,6 +11,7 @@ import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
@ -405,6 +406,7 @@ internal object TokensDomainModule {
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): BaseCurrenciesStatusesOperations {
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
@ -420,6 +422,7 @@ internal object TokensDomainModule {
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
)
}
@ -439,6 +442,7 @@ internal object TokensDomainModule {
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): BaseCurrencyStatusOperations {
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
@ -454,6 +458,7 @@ internal object TokensDomainModule {
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}

View file

@ -42,9 +42,8 @@ internal object YieldBalanceSupplierModule {
listOf(
"single_yield_balance",
params.userWalletId.stringValue,
params.currencyId.value,
params.network.id.rawId,
params.network.id.derivationPath,
params.stakingId.integrationId,
params.stakingId.address,
)
.joinToString(separator = "_")
},

View file

@ -2,8 +2,6 @@ package com.tangem.data.staking.single
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.data.staking.utils.StakingIdFactory
import com.tangem.domain.staking.model.StakingID
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
@ -24,7 +22,7 @@ import timber.log.Timber
*
* @property params params
* @property multiYieldBalanceSupplier multi yield balance supplier
* @property stakingIdFactory factory for creating [StakingID]
* @property analyticsExceptionHandler analytics exception handler
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
@ -32,20 +30,17 @@ import timber.log.Timber
internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
@Assisted private val params: SingleYieldBalanceProducer.Params,
private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
private val stakingIdFactory: StakingIdFactory,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val dispatchers: CoroutineDispatcherProvider,
) : SingleYieldBalanceProducer {
override val fallback: YieldBalance by lazy {
YieldBalance.Error(
integrationId = stakingIdFactory.createIntegrationId(currencyId = params.currencyId),
address = null,
integrationId = params.stakingId.integrationId,
address = params.stakingId.address,
)
}
private var stakingId: StakingID? = null
override fun produce(): Flow<YieldBalance> {
Timber.i("Producing yield balance for params:\n$params")
@ -53,12 +48,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
params = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId),
)
.mapNotNull { balances ->
val currentStakingId = getStakingId()
if (currentStakingId == null) {
Timber.i("Staking ID is null for params: $params")
return@mapNotNull YieldBalance.Unsupported
}
val currentStakingId = params.stakingId
val currentBalances = balances.filter { it.getStakingId() == currentStakingId }
@ -86,34 +76,16 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
currentBalances.first()
}
} else {
val balance = currentBalances.firstOrNull()
val balance = currentBalances.firstOrNull() ?: return@mapNotNull null
if (balance != null) {
Timber.i("Yield balance found for $currentStakingId:\n$balance")
balance
} else {
Timber.i("No yield balance found for $currentStakingId:\n${YieldBalance.Unsupported}")
YieldBalance.Unsupported
}
Timber.i("Yield balance found for $currentStakingId:\n$balance")
balance
}
}
.distinctUntilChanged()
.flowOn(dispatchers.default)
}
private suspend fun getStakingId(): StakingID? {
val saved = stakingId
if (saved != null) return saved
return stakingIdFactory.create(
userWalletId = params.userWalletId,
currencyId = params.currencyId,
network = params.network,
)
.also { stakingId = it }
}
@AssistedFactory
interface Factory : SingleYieldBalanceProducer.Factory {
override fun create(params: SingleYieldBalanceProducer.Params): DefaultSingleYieldBalanceProducer

View file

@ -1,13 +1,10 @@
package com.tangem.data.staking.single
import com.google.common.truth.Truth
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.common.test.utils.getEmittedValues
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.data.staking.toDomain
import com.tangem.data.staking.utils.StakingIdFactory
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingID
import com.tangem.domain.staking.model.stakekit.YieldBalance
@ -15,40 +12,49 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import io.mockk.clearMocks
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
/**
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class DefaultSingleYieldBalanceProducerTest {
private val params = SingleYieldBalanceProducer.Params(
userWalletId = UserWalletId(stringValue = "011"),
currencyId = ton.id,
network = ton.network,
stakingId = tonId,
)
private val multiNetworkStatusSupplier = mockk<MultiYieldBalanceSupplier>()
private val stakingIdFactory = mockk<StakingIdFactory>()
private val analyticsExceptionHandler = mockk<AnalyticsExceptionHandler>(relaxUnitFun = true)
private val dispatchers = TestingCoroutineDispatcherProvider()
private val producer = DefaultSingleYieldBalanceProducer(
params = params,
stakingIdFactory = stakingIdFactory,
multiYieldBalanceSupplier = multiNetworkStatusSupplier,
analyticsExceptionHandler = analyticsExceptionHandler,
dispatchers = dispatchers,
)
@BeforeEach
fun resetMocks() {
clearMocks(multiNetworkStatusSupplier, analyticsExceptionHandler)
}
@Test
fun `test that flow is mapped for data from params`() = runTest {
fun `flow is mapped for data from params`() = runTest {
// Arrange
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
val expected = flowOf(
val multiFlow = flowOf(
setOf(
balance,
MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain(),
@ -56,97 +62,89 @@ internal class DefaultSingleYieldBalanceProducerTest {
)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns expected
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val actual = producer.produce()
// Act
val actual = getEmittedValues(flow = producer.produce())
verify { multiNetworkStatusSupplier(multiParams) }
Truth.assertThat(actual).hasSize(1)
Truth.assertThat(actual).containsExactly(balance)
val values = getEmittedValues(flow = actual)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(balance))
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
}
@Test
fun `test that flow is updated if yield balance is updated`() = runTest {
val expected = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
fun `flow is updated if yield balance is updated`() = runTest {
// Arrange
val multiFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns expected
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val actual = producer.produceWithFallback()
val producerFlow = producer.produceWithFallback()
verify { multiNetworkStatusSupplier(multiParams) }
// first emit
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
expected.emit(value = setOf(balance))
val updatedBalance = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address)
val values1 = getEmittedValues(flow = actual)
// Act (first emit)
multiFlow.emit(value = setOf(balance))
val actual1 = getEmittedValues(flow = producerFlow)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
// Assert (first emit)
Truth.assertThat(actual1).hasSize(1)
Truth.assertThat(actual1).containsExactly(balance)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(balance))
// Act (second emit)
multiFlow.emit(value = setOf(updatedBalance))
val actual2 = getEmittedValues(flow = producerFlow)
// second emit
val updatedStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address)
expected.emit(value = setOf(updatedStatus))
// Assert (second emit)
Truth.assertThat(actual2).hasSize(2)
Truth.assertThat(actual2).containsExactly(balance, updatedBalance)
val values2 = getEmittedValues(flow = actual)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
Truth.assertThat(values2.size).isEqualTo(2)
Truth.assertThat(values2).isEqualTo(listOf(balance, updatedStatus))
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
}
@Test
fun `test that flow is filtered the same status`() = runTest {
val expected = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
fun `flow is filtered the same status`() = runTest {
// Arrange
val multiFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns expected
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val actual = producer.produceWithFallback()
val producerFlow = producer.produceWithFallback()
verify { multiNetworkStatusSupplier(multiParams) }
// first emit
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
expected.emit(value = setOf(balance))
val values1 = getEmittedValues(flow = actual)
// Act (first emit)
multiFlow.emit(value = setOf(balance))
val actual1 = getEmittedValues(flow = producerFlow)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
// Assert (first emit)
Truth.assertThat(actual1).hasSize(1)
Truth.assertThat(actual1).containsExactly(balance)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(balance))
// Act (second emit)
multiFlow.emit(value = setOf(balance))
val actual2 = getEmittedValues(flow = producerFlow)
// second emit
expected.emit(value = setOf(balance))
// Assert (second emit)
Truth.assertThat(actual2).hasSize(1)
Truth.assertThat(actual2).containsExactly(balance)
val values2 = getEmittedValues(flow = actual)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
Truth.assertThat(values2.size).isEqualTo(1)
Truth.assertThat(values2).isEqualTo(listOf(balance))
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
}
@Test
fun `test if flow throws exception`() = runTest {
fun `flow throws exception`() = runTest {
// Arrange
val exception = IllegalStateException()
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
val innerFlow = MutableStateFlow(value = false)
val expected = flow {
val multiFlow = flow {
if (innerFlow.value) {
emit(setOf(balance))
} else {
@ -156,83 +154,52 @@ internal class DefaultSingleYieldBalanceProducerTest {
.buffer(capacity = 5)
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns expected
every { stakingIdFactory.createIntegrationId(currencyId = params.currencyId) } returns tonId.integrationId
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val actual = producer.produceWithFallback()
val producerFlow = producer.produceWithFallback()
verify { multiNetworkStatusSupplier(multiParams) }
// Act (first emit)
val actual1 = getEmittedValues(flow = producerFlow)
val values1 = getEmittedValues(flow = actual)
// Assert (first emit)
val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = "0x1")
coVerify(inverse = true) { stakingIdFactory.create(any(), any(), any()) }
Truth.assertThat(values1.size).isEqualTo(1)
val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = null)
Truth.assertThat(values1).isEqualTo(listOf(fallbackStatus))
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
Truth.assertThat(actual1).hasSize(1)
Truth.assertThat(actual1).containsExactly(fallbackStatus)
// Act (second emit)
innerFlow.emit(value = true)
val actual2 = getEmittedValues(flow = producerFlow)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(actual2).hasSize(1)
Truth.assertThat(actual2).containsExactly(balance)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
Truth.assertThat(values2.size).isEqualTo(1)
Truth.assertThat(values2).isEqualTo(listOf(balance))
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
}
@Test
fun `test if flow doesn't contain network from params`() = runTest {
fun `flow doesn't contain network from params`() = runTest {
// Arrange
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain()
val yieldBalancesFlow = flowOf(setOf(balance))
val multiFlow = flowOf(setOf(balance))
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns yieldBalancesFlow
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
val actual = producer.produce()
val producerFlow = producer.produce()
verify { multiNetworkStatusSupplier(multiParams) }
// Act
val actual = getEmittedValues(flow = producerFlow)
val values = getEmittedValues(flow = actual)
// Assert
Truth.assertThat(actual).isEmpty()
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
val expected = YieldBalance.Unsupported
Truth.assertThat(values.first()).isEqualTo(expected)
}
@Test
fun `test if wallet manager facade returns null`() = runTest {
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
val yieldBalancesFlow = flowOf(setOf(balance))
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
every { multiNetworkStatusSupplier(multiParams) } returns yieldBalancesFlow
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns null
val actual = producer.produce()
verify { multiNetworkStatusSupplier(multiParams) }
val values = getEmittedValues(flow = actual)
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
val expected = YieldBalance.Unsupported
Truth.assertThat(values.first()).isEqualTo(expected)
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
}
private companion object {
val mocks = MockCryptoCurrencyFactory()
val ton = mocks.createCoin(Blockchain.TON)
val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId
val solanaId = StakingID(
integrationId = "solana-sol-native-multivalidator-staking",

View file

@ -42,12 +42,37 @@ class StakingIdFactory(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
network: Network,
): Either<Error, StakingID> {
return createInternal(
currencyId = currencyId,
defaultAddressProvider = {
walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network)
},
)
}
/**
* Creates a [StakingID] for the given cryptocurrency and default address
*
* @param currencyId the identifier of the cryptocurrency
* @param defaultAddress the default address for staking, can be null
*/
fun create(currencyId: CryptoCurrency.ID, defaultAddress: String?): Either<Error, StakingID> {
return createInternal(
currencyId = currencyId,
defaultAddressProvider = { defaultAddress },
)
}
private inline fun createInternal(
currencyId: CryptoCurrency.ID,
defaultAddressProvider: () -> String?,
): Either<Error, StakingID> = either {
val integrationId = StakingIntegrationID.create(currencyId = currencyId)
ensureNotNull(integrationId) { Error.UnsupportedCurrency }
val address = walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network)
val address = defaultAddressProvider().takeUnless { it.isNullOrEmpty() }
ensureNotNull(address) { Error.UnableToGetAddress(integrationId = integrationId) }

View file

@ -1,10 +1,9 @@
package com.tangem.domain.staking.single
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingID
import com.tangem.domain.staking.model.stakekit.YieldBalance
/**
* Producer of yield balance for selected wallet [UserWalletId]
@ -15,16 +14,14 @@ interface SingleYieldBalanceProducer : FlowProducer<YieldBalance> {
data class Params(
val userWalletId: UserWalletId,
val currencyId: CryptoCurrency.ID,
val network: Network,
val stakingId: StakingID,
) {
override fun toString(): String {
return """
SingleYieldBalanceProducer.Params(
userWalletId = $userWalletId,
currencyId = $currencyId,
network = $network
stakingId = $stakingId,
)
""".trimIndent()
}

View file

@ -54,6 +54,10 @@ dependencies {
implementation(deps.jodatime)
implementation(deps.reKotlin)
implementation(tangemDeps.blockchain) {
exclude(module = "joda-time")
}
/** Tests */
testImplementation(deps.test.coroutine)
testImplementation(deps.test.junit5)
@ -61,7 +65,4 @@ dependencies {
testImplementation(deps.test.mockk)
testImplementation(deps.test.truth)
testImplementation(projects.common.test)
testImplementation(tangemDeps.blockchain) {
exclude(module = "joda-time")
}
}

View file

@ -5,6 +5,7 @@ import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import arrow.core.raise.recover
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
@ -18,6 +19,9 @@ import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.model.StakingID
import com.tangem.domain.staking.model.isStakingSupported
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
@ -49,6 +53,7 @@ abstract class BaseCurrencyStatusOperations(
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
@ -82,7 +87,7 @@ abstract class BaseCurrencyStatusOperations(
return getCurrencyStatusFlow(userWalletId = userWalletId, currency = currency)
}
fun getCurrencyStatusFlow(
suspend fun getCurrencyStatusFlow(
userWalletId: UserWalletId,
currency: CryptoCurrency,
includeQuotes: Boolean = true,
@ -105,9 +110,24 @@ abstract class BaseCurrencyStatusOperations(
val statusFlow = getNetworkStatus(userWalletId = userWalletId, network = currency.network)
val yieldBalanceFlow = getYieldBalance(userWalletId = userWalletId, cryptoCurrency = currency)
val isStakingSupported = currency.network.toBlockchain().isStakingSupported
return if (subscribeOnYieldBalance) {
val yieldBalanceFlow = if (isStakingSupported) {
val stakingId = stakingIdFactory.create(
userWalletId = userWalletId,
currencyId = currency.id,
network = currency.network,
)
.getOrNull()
stakingId?.let {
getYieldBalance(userWalletId = userWalletId, stakingId = it)
}
} else {
null
}
return if (subscribeOnYieldBalance && yieldBalanceFlow != null) {
combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance ->
currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
@ -334,15 +354,11 @@ abstract class BaseCurrencyStatusOperations(
.bind()
}
private fun getYieldBalance(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
): EitherFlow<Error, YieldBalance> {
private fun getYieldBalance(userWalletId: UserWalletId, stakingId: StakingID): EitherFlow<Error, YieldBalance> {
return singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
network = cryptoCurrency.network,
stakingId = stakingId,
),
)
.map<YieldBalance, Either<Error, YieldBalance>> { it.right() }

View file

@ -24,6 +24,7 @@ import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
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.model.stakekit.YieldBalance
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
@ -56,6 +57,7 @@ class CachedCurrenciesStatusesOperations(
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val stakingIdFactory: StakingIdFactory,
private val tokensFeatureToggles: TokensFeatureToggles,
) : BaseCurrenciesStatusesOperations,
BaseCurrencyStatusOperations(
@ -67,6 +69,7 @@ class CachedCurrenciesStatusesOperations(
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
tokensFeatureToggles = tokensFeatureToggles,
) {
@ -371,20 +374,19 @@ class CachedCurrenciesStatusesOperations(
return channelFlow {
val state = MutableStateFlow(emptyList<YieldBalance>())
cryptoCurrencies.onEach {
val stakingIds = cryptoCurrencies.mapNotNullTo(hashSetOf()) {
stakingIdFactory.create(userWalletId = userWalletId, currencyId = it.id, network = it.network)
.getOrNull()
}
stakingIds.onEach {
launch {
singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(
userWalletId = userWalletId,
currencyId = it.id,
network = it.network,
),
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = it),
)
.onEach { balance ->
state.update { loadedBalances ->
loadedBalances.addOrReplace(balance) {
it.integrationId == balance.integrationId && it.address == balance.address
}
loadedBalances.addOrReplace(balance) { balance.getStakingId() == it }
}
}
.launchIn(scope = this)