diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt index 213e3753c7..7f6857ddf0 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt @@ -210,4 +210,10 @@ internal object StakingDomainModule { fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase { return GetActionRequirementAmountUseCase() } + + @Provides + @Singleton + fun provideStakingIdFactory(walletManagersFacade: WalletManagersFacade): StakingIdFactory { + return StakingIdFactory(walletManagersFacade = walletManagersFacade) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index eddbbc0fd4..b1d8469ec4 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -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, ) } diff --git a/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt b/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt index 3407a3d99b..3ef90f5b74 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt @@ -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 = "_") }, diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt index bd38f407a4..cac9b3c1a3 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt @@ -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 { 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 diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt index 1d7e5af029..b6f14b7e6b 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt @@ -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() - private val stakingIdFactory = mockk() private val analyticsExceptionHandler = mockk(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>(replay = 2, extraBufferCapacity = 1) + fun `flow is updated if yield balance is updated`() = runTest { + // Arrange + val multiFlow = MutableSharedFlow>(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>(replay = 2, extraBufferCapacity = 1) + fun `flow is filtered the same status`() = runTest { + // Arrange + val multiFlow = MutableSharedFlow>(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", diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt b/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt index 0da69ebb56..a830af4484 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt @@ -42,12 +42,37 @@ class StakingIdFactory( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, network: Network, + ): Either { + 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 { + return createInternal( + currencyId = currencyId, + defaultAddressProvider = { defaultAddress }, + ) + } + + private inline fun createInternal( + currencyId: CryptoCurrency.ID, + defaultAddressProvider: () -> String?, ): Either = 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) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt index aae567c354..7fea7e7014 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt @@ -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 { 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() } diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index 2bcbd75a88..a1d0b5b08f 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -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") - } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 351c2e0f89..d3c6c507a6 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -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 { + private fun getYieldBalance(userWalletId: UserWalletId, stakingId: StakingID): EitherFlow { return singleYieldBalanceSupplier( params = SingleYieldBalanceProducer.Params( userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - network = cryptoCurrency.network, + stakingId = stakingId, ), ) .map> { it.right() } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 6bcb07b46b..5c5c5d91b2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -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()) - 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)