From 3d16de3f4f54464e39db6141c459df8f56c74061 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Apr 2025 11:23:21 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../CommonYieldBalanceFetcherDelegate.kt | 31 ++++++- .../multi/DefaultMultiYieldBalanceFetcher.kt | 4 + .../DefaultSingleYieldBalanceFetcher.kt | 4 + .../DefaultMultiYieldBalanceFetcherTest.kt | 77 ++++++++++++++++++ .../DefaultSingleYieldBalanceFetcherTest.kt | 81 +++++++++++++++++++ .../BaseCurrencyStatusOperations.kt | 34 ++++++-- 6 files changed, 221 insertions(+), 10 deletions(-) diff --git a/data/staking/src/main/java/com/tangem/data/staking/fetcher/CommonYieldBalanceFetcherDelegate.kt b/data/staking/src/main/java/com/tangem/data/staking/fetcher/CommonYieldBalanceFetcherDelegate.kt index 7204495961..413cf7046d 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/fetcher/CommonYieldBalanceFetcherDelegate.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/fetcher/CommonYieldBalanceFetcherDelegate.kt @@ -6,27 +6,32 @@ import arrow.core.left import arrow.core.raise.catch import arrow.core.raise.either import arrow.core.raise.ensure +import arrow.core.toOption import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO import com.tangem.datasource.local.token.StakingYieldsStore +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.core.utils.catchOn import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import timber.log.Timber internal fun commonFetcher( implementor: YieldBalanceFetcherImplementor, + userWalletsStore: UserWalletsStore, stakingYieldsStore: StakingYieldsStore, yieldsBalancesStore: YieldsBalancesStore, dispatchers: CoroutineDispatcherProvider, ): FlowFetcher { return CommonYieldBalanceFetcher( implementor = implementor, + userWalletsStore = userWalletsStore, stakingYieldsStore = stakingYieldsStore, yieldsBalancesStore = yieldsBalancesStore, dispatchers = dispatchers, @@ -36,19 +41,25 @@ internal fun commonFetcher( /** * Common implementation of YieldBalanceFetcher * - * @param implementor fetcher implementor + * @property implementor fetcher implementor + * @property userWalletsStore user wallets store * @property stakingYieldsStore staking yields store * @property yieldsBalancesStore yields balances store * @property dispatchers dispatchers */ private class CommonYieldBalanceFetcher( private val implementor: YieldBalanceFetcherImplementor, + private val userWalletsStore: UserWalletsStore, private val stakingYieldsStore: StakingYieldsStore, private val yieldsBalancesStore: YieldsBalancesStore, private val dispatchers: CoroutineDispatcherProvider, ) : FlowFetcher { override suspend fun invoke(params: Params): Either { + checkIsSupportedByWalletOrElse(userWalletId = params.userWalletId) { + return it.left() + } + val stakingIds = getStakingIds(params).getOrElse { return it.left() } @@ -58,7 +69,23 @@ private class CommonYieldBalanceFetcher( implementor.fetch(params = params, stakingIds = stakingIds, requests) } - .onLeft { yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds) } + .onLeft { + Timber.e(it, "Unable to fetch yield balances $params") + yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds) + } + } + + private inline fun checkIsSupportedByWalletOrElse(userWalletId: UserWalletId, ifNotSupported: (Throwable) -> Unit) { + val maybeUserWallet = userWalletsStore.getSyncOrNull(key = userWalletId).toOption() + + val isSupportedByWallet = maybeUserWallet.isSome(UserWallet::isMultiCurrency) + + if (!isSupportedByWallet) { + val exception = IllegalStateException("Wallet $userWalletId is not supported: $maybeUserWallet") + Timber.e(exception) + + ifNotSupported(exception) + } } private suspend fun getStakingIds(params: Params): Either> = either { diff --git a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt index 7a5e721099..f804e9cdd7 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt @@ -6,6 +6,7 @@ import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.local.token.StakingYieldsStore +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher @@ -15,6 +16,7 @@ import javax.inject.Inject /** * Default implementation of [MultiYieldBalanceFetcher] * + * @property userWalletsStore user wallets store * @property stakingYieldsStore staking yields store * @property yieldsBalancesStore yields balances store * @property stakingIdFactory factory for creating StakingID @@ -24,6 +26,7 @@ import javax.inject.Inject [REDACTED_AUTHOR] */ internal class DefaultMultiYieldBalanceFetcher @Inject constructor( + private val userWalletsStore: UserWalletsStore, private val stakingYieldsStore: StakingYieldsStore, private val yieldsBalancesStore: YieldsBalancesStore, private val stakingIdFactory: StakingIdFactory, @@ -32,6 +35,7 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor( ) : MultiYieldBalanceFetcher, FlowFetcher by commonFetcher( implementor = createMultiFetcherImplementor(yieldsBalancesStore, stakingIdFactory, stakeKitApi, dispatchers), + userWalletsStore = userWalletsStore, stakingYieldsStore = stakingYieldsStore, yieldsBalancesStore = yieldsBalancesStore, dispatchers = dispatchers, diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt index a59151f771..6640397ffb 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt @@ -6,6 +6,7 @@ import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.local.token.StakingYieldsStore +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher @@ -16,6 +17,7 @@ import javax.inject.Inject /** * Default implementation of [MultiYieldBalanceFetcher] * + * @property userWalletsStore user wallets store * @property stakingYieldsStore staking yields store * @property yieldsBalancesStore yields balances store * @property stakingIdFactory factory for creating StakingID @@ -25,6 +27,7 @@ import javax.inject.Inject [REDACTED_AUTHOR] */ internal class DefaultSingleYieldBalanceFetcher @Inject constructor( + private val userWalletsStore: UserWalletsStore, private val stakingYieldsStore: StakingYieldsStore, private val yieldsBalancesStore: YieldsBalancesStore, private val stakingIdFactory: StakingIdFactory, @@ -33,6 +36,7 @@ internal class DefaultSingleYieldBalanceFetcher @Inject constructor( ) : SingleYieldBalanceFetcher, FlowFetcher by commonFetcher( implementor = createSingleFetcherImplementor(yieldsBalancesStore, stakingIdFactory, stakeKitApi, dispatchers), + userWalletsStore = userWalletsStore, stakingYieldsStore = stakingYieldsStore, yieldsBalancesStore = yieldsBalancesStore, dispatchers = dispatchers, diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt index 5f57b9d20d..789b151646 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt @@ -1,10 +1,12 @@ package com.tangem.data.staking.multi +import arrow.core.toOption 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.data.staking.MockYieldDTOFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory @@ -13,6 +15,7 @@ import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO import com.tangem.datasource.local.token.StakingYieldsStore +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.model.StakingID import com.tangem.domain.wallets.models.UserWalletId @@ -26,12 +29,14 @@ import org.junit.Test */ internal class DefaultMultiYieldBalanceFetcherTest { + private val userWalletsStore: UserWalletsStore = mockk() private val stakingYieldsStore: StakingYieldsStore = mockk() private val yieldsBalancesStore: YieldsBalancesStore = mockk() private val stakingIdFactory: StakingIdFactory = mockk() private val stakeKitApi: StakeKitApi = mockk() private val fetcher = DefaultMultiYieldBalanceFetcher( + userWalletsStore = userWalletsStore, stakingYieldsStore = stakingYieldsStore, yieldsBalancesStore = yieldsBalancesStore, stakingIdFactory = stakingIdFactory, @@ -45,6 +50,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId) coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId) coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs @@ -63,6 +69,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) @@ -76,18 +83,77 @@ internal class DefaultMultiYieldBalanceFetcherTest { Truth.assertThat(actual.isRight()).isTrue() } + @Test + fun `fetch yields balances failure if user wallet is not supported`() = runTest { + val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) + + val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + + val userWallet = MockUserWalletFactory.create().copy(isMultiCurrency = false) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet + + val actual = fetcher(params) + + coVerify { userWalletsStore.getSyncOrNull(params.userWalletId) } + + coVerify(inverse = true) { + stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) + yieldsBalancesStore.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()) + } + + val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${userWallet.toOption()}") + + Truth.assertThat(actual.isLeft()).isTrue() + Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) + Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + } + + @Test + fun `fetch yields balances failure if userWalletsStore returns null`() = runTest { + val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) + + val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns null + + val actual = fetcher(params) + + coVerify { userWalletsStore.getSyncOrNull(params.userWalletId) } + + coVerify(inverse = true) { + stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) + yieldsBalancesStore.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()) + } + + val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${null.toOption()}") + + Truth.assertThat(actual.isLeft()).isTrue() + Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) + Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + } + @Test fun `fetch yields balances failure if stakingIdFactory returns empty list`() = runTest { val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns emptySet() coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns emptySet() val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } @@ -113,6 +179,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId) coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId) coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs @@ -122,6 +189,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) @@ -147,6 +215,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId) coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId) coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs @@ -156,6 +225,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) @@ -181,6 +251,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId) coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId) coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs @@ -195,6 +266,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) @@ -226,6 +298,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId) coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId) coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs @@ -237,6 +310,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) @@ -268,6 +342,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId) coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId) coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs @@ -287,6 +362,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.create(params.userWalletId, ton.id, ton.network) stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) @@ -306,6 +382,7 @@ internal class DefaultMultiYieldBalanceFetcherTest { private companion object { val userWalletId = UserWalletId("011") + val userWallet = MockUserWalletFactory.create() val mocks = MockCryptoCurrencyFactory() diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt index 16e2049744..476fc620d8 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt @@ -1,10 +1,12 @@ package com.tangem.data.staking.single +import arrow.core.toOption 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.data.staking.MockYieldDTOFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory @@ -17,6 +19,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO import com.tangem.datasource.local.token.StakingYieldsStore +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.model.StakingID import com.tangem.domain.wallets.models.UserWalletId @@ -31,12 +34,14 @@ import java.math.BigDecimal */ internal class DefaultSingleYieldBalanceFetcherTest { + private val userWalletsStore: UserWalletsStore = mockk() private val stakingYieldsStore: StakingYieldsStore = mockk() private val yieldsBalancesStore: YieldsBalancesStore = mockk() private val stakingIdFactory: StakingIdFactory = mockk() private val stakeKitApi: StakeKitApi = mockk() private val fetcher = DefaultSingleYieldBalanceFetcher( + userWalletsStore = userWalletsStore, stakingYieldsStore = stakingYieldsStore, yieldsBalancesStore = yieldsBalancesStore, stakingIdFactory = stakingIdFactory, @@ -52,6 +57,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId coEvery { yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) @@ -70,6 +76,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) stakingYieldsStore.getSyncWithTimeout() @@ -82,6 +89,67 @@ internal class DefaultSingleYieldBalanceFetcherTest { Truth.assertThat(actual.isRight()).isTrue() } + @Test + fun `fetch yields balances failure if user wallet is not supported`() = runTest { + val params = YieldBalanceFetcherParams.Single( + userWalletId = userWalletId, + currencyId = ton.id, + network = ton.network, + ) + + val userWallet = MockUserWalletFactory.create().copy(isMultiCurrency = false) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet + + val actual = fetcher(params) + + coVerify { userWalletsStore.getSyncOrNull(params.userWalletId) } + + coVerify(inverse = true) { + stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) + yieldsBalancesStore.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()) + } + + val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${userWallet.toOption()}") + + Truth.assertThat(actual.isLeft()).isTrue() + Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) + Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + } + + @Test + fun `fetch yields balances failure if userWalletsStore returns null`() = runTest { + val params = YieldBalanceFetcherParams.Single( + userWalletId = userWalletId, + currencyId = ton.id, + network = ton.network, + ) + + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns null + + val actual = fetcher(params) + + coVerify { userWalletsStore.getSyncOrNull(params.userWalletId) } + + coVerify(inverse = true) { + stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) + yieldsBalancesStore.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()) + } + + val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${null.toOption()}") + + Truth.assertThat(actual.isLeft()).isTrue() + Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) + Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + } + @Test fun `fetch yields balances failure if stakingIdFactory createForDefault returns null`() = runTest { val params = YieldBalanceFetcherParams.Single( @@ -90,11 +158,13 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns null val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(userWalletId = userWalletId, currencyId = ton.id, network = ton.network) } @@ -121,6 +191,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId coEvery { yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) @@ -132,6 +203,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) stakingYieldsStore.getSyncWithTimeout() @@ -158,6 +230,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId coEvery { yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) @@ -169,6 +242,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) stakingYieldsStore.getSyncWithTimeout() @@ -195,6 +269,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId coEvery { yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) @@ -207,6 +282,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) stakingYieldsStore.getSyncWithTimeout() @@ -239,6 +315,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId coEvery { yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) @@ -251,6 +328,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) stakingYieldsStore.getSyncWithTimeout() @@ -283,6 +361,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { network = ton.network, ) + coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId coEvery { yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) @@ -302,6 +381,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { val actual = fetcher(params) coVerify { + userWalletsStore.getSyncOrNull(params.userWalletId) stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId)) stakingYieldsStore.getSyncWithTimeout() @@ -353,6 +433,7 @@ internal class DefaultSingleYieldBalanceFetcherTest { private companion object { val userWalletId = UserWalletId("011") + val userWallet = MockUserWalletFactory.create() val mocks = MockCryptoCurrencyFactory() 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 fed0750cf9..c2d41790de 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 @@ -91,6 +91,7 @@ abstract class BaseCurrencyStatusOperations( userWalletId: UserWalletId, currency: CryptoCurrency, includeQuotes: Boolean = true, + subscribeOnYieldBalance: Boolean = true, ): Flow> { val rawCurrencyId = currency.id.rawCurrencyId @@ -117,13 +118,24 @@ abstract class BaseCurrencyStatusOperations( val yieldBalanceFlow = getYieldBalance(userWalletId = userWalletId, cryptoCurrency = currency) - return combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance -> - currencyStatusProxyCreator.createCurrencyStatus( - currency = currency, - maybeQuote = maybeQuote, - maybeNetworkStatus = maybeNetworkStatus, - maybeYieldBalance = maybeYieldBalance, - ) + return if (subscribeOnYieldBalance) { + combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance -> + currencyStatusProxyCreator.createCurrencyStatus( + currency = currency, + maybeQuote = maybeQuote, + maybeNetworkStatus = maybeNetworkStatus, + maybeYieldBalance = maybeYieldBalance, + ) + } + } else { + combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus -> + currencyStatusProxyCreator.createCurrencyStatus( + currency = currency, + maybeQuote = maybeQuote, + maybeNetworkStatus = maybeNetworkStatus, + maybeYieldBalance = null, + ) + } } } @@ -242,7 +254,13 @@ abstract class BaseCurrencyStatusOperations( recover = { return flowOf(it.left()) }, ) - return getCurrencyStatusFlow(userWalletId, currency, includeQuotes) + return getCurrencyStatusFlow( + userWalletId = userWalletId, + currency = currency, + includeQuotes = includeQuotes, + // If toggle is off, then subscribe on yield balance. If toggle is on, then don't + subscribeOnYieldBalance = !tokensFeatureToggles.isStakingLoadingRefactoringEnabled, + ) } suspend fun getCurrenciesStatusesSync(userWalletId: UserWalletId): Either> {