Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-25 16:06:15 +04:00
parent 4bb8d20301
commit 3fd8b110ef
4 changed files with 168 additions and 16 deletions

View file

@ -24,17 +24,18 @@ dependencies {
// endregion
// region Project - Domain
implementation(projects.domain.account)
implementation(projects.domain.card)
implementation(projects.domain.core)
implementation(projects.domain.demo)
implementation(projects.domain.legacy)
implementation(projects.domain.walletManager)
implementation(projects.domain.card)
implementation(projects.domain.models)
implementation(projects.domain.staking)
implementation(projects.domain.staking.models)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.walletManager)
implementation(projects.domain.wallets.models)
// endregion

View file

@ -0,0 +1,35 @@
package com.tangem.data.tokens
import arrow.core.Either
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.core.utils.catchOn
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher.Params
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
/**
* Implementation of [MultiWalletCryptoCurrenciesFetcher] that fetches crypto currencies of all accounts
*
* @property userWalletsStore [UserWallet]'s store
* @property walletAccountsFetcher instance of [WalletAccountsFetcher] to fetch accounts for a multi wallet
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
internal class AccountListCryptoCurrenciesFetcher(
private val userWalletsStore: UserWalletsStore,
private val walletAccountsFetcher: WalletAccountsFetcher,
private val dispatchers: CoroutineDispatcherProvider,
) : MultiWalletCryptoCurrenciesFetcher {
override suspend fun invoke(params: Params) = Either.catchOn(dispatchers.default) {
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
if (!userWallet.isMultiCurrency) error("${this::class.simpleName} supports only multi-currency wallet")
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
}
}

View file

@ -1,13 +1,16 @@
package com.tangem.data.tokens.di
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.data.tokens.AccountListCryptoCurrenciesFetcher
import com.tangem.data.tokens.DefaultMultiWalletCryptoCurrenciesFetcher
import com.tangem.data.tokens.utils.CustomTokensMerger
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -24,28 +27,38 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
@Singleton
@Provides
fun provideMultiWalletCryptoCurrenciesFetcher(
accountsFeatureToggles: AccountsFeatureToggles,
tangemTechApi: TangemTechApi,
userWalletsStore: UserWalletsStore,
userTokensResponseStore: UserTokensResponseStore,
userTokensSaver: UserTokensSaver,
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
expressServiceLoader: ExpressServiceLoader,
walletAccountsFetcher: WalletAccountsFetcher,
dispatchers: CoroutineDispatcherProvider,
): MultiWalletCryptoCurrenciesFetcher {
return DefaultMultiWalletCryptoCurrenciesFetcher(
demoConfig = DemoConfig(),
userWalletsStore = userWalletsStore,
tangemTechApi = tangemTechApi,
customTokensMerger = CustomTokensMerger(
tangemTechApi = tangemTechApi,
userTokensSaver = userTokensSaver,
return if (accountsFeatureToggles.isFeatureEnabled) {
AccountListCryptoCurrenciesFetcher(
userWalletsStore = userWalletsStore,
walletAccountsFetcher = walletAccountsFetcher,
dispatchers = dispatchers,
),
userTokensResponseStore = userTokensResponseStore,
userTokensSaver = userTokensSaver,
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
expressServiceLoader = expressServiceLoader,
dispatchers = dispatchers,
)
)
} else {
DefaultMultiWalletCryptoCurrenciesFetcher(
demoConfig = DemoConfig(),
userWalletsStore = userWalletsStore,
tangemTechApi = tangemTechApi,
customTokensMerger = CustomTokensMerger(
tangemTechApi = tangemTechApi,
userTokensSaver = userTokensSaver,
dispatchers = dispatchers,
),
userTokensResponseStore = userTokensResponseStore,
userTokensSaver = userTokensSaver,
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
expressServiceLoader = expressServiceLoader,
dispatchers = dispatchers,
)
}
}
}

View file

@ -0,0 +1,103 @@
package com.tangem.data.tokens
import arrow.core.left
import com.tangem.common.test.utils.assertEither
import com.tangem.common.test.utils.assertEitherRight
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class AccountListCryptoCurrenciesFetcherTest {
private val userWalletsStore: UserWalletsStore = mockk(relaxUnitFun = true)
private val walletAccountsFetcher: WalletAccountsFetcher = mockk(relaxUnitFun = true)
private val dispatchers = TestingCoroutineDispatcherProvider()
private val fetcher = AccountListCryptoCurrenciesFetcher(
userWalletsStore = userWalletsStore,
walletAccountsFetcher = walletAccountsFetcher,
dispatchers = dispatchers,
)
@BeforeEach
fun resetMocks() {
clearMocks(userWalletsStore, walletAccountsFetcher)
}
@Test
fun `returns failure if wallet is not multi-currency`() = runTest {
// Arrange
val params = MultiWalletCryptoCurrenciesFetcher.Params(userWalletId = userWalletId)
val mockUserWallet = mockk<UserWallet> { every { isMultiCurrency } returns false }
every { userWalletsStore.getSyncStrict(key = params.userWalletId) } returns mockUserWallet
// Act
val actual = fetcher(params)
// Assert
val expected = IllegalStateException(
"${AccountListCryptoCurrenciesFetcher::class.simpleName} supports only multi-currency wallet",
).left()
assertEither(actual, expected)
verify { userWalletsStore.getSyncStrict(key = params.userWalletId) }
coVerify(inverse = true) { walletAccountsFetcher.fetch(any()) }
}
@Test
fun `returns accounts if wallet is multi-currency`() = runTest {
// Arrange
val params = MultiWalletCryptoCurrenciesFetcher.Params(userWalletId = userWalletId)
val mockUserWallet = mockk<UserWallet> { every { isMultiCurrency } returns true }
every { userWalletsStore.getSyncStrict(key = params.userWalletId) } returns mockUserWallet
// Act
val actual = fetcher(params)
// Assert
assertEitherRight(actual)
coVerify(ordering = Ordering.SEQUENCE) {
userWalletsStore.getSyncStrict(key = params.userWalletId)
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
}
}
@Test
fun `returns error if walletAccountsFetcher returns error`() = runTest {
// Arrange
val params = MultiWalletCryptoCurrenciesFetcher.Params(userWalletId = userWalletId)
val mockUserWallet = mockk<UserWallet> { every { isMultiCurrency } returns true }
val error = RuntimeException("fetch error")
every { userWalletsStore.getSyncStrict(key = params.userWalletId) } returns mockUserWallet
coEvery { walletAccountsFetcher.fetch(userWalletId = params.userWalletId) } throws error
// Act
val actual = fetcher(params)
// Assert
val expected = error.left()
assertEither(actual, expected)
coVerify(ordering = Ordering.SEQUENCE) {
userWalletsStore.getSyncStrict(key = params.userWalletId)
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
}
}
private companion object {
val userWalletId = UserWalletId("012")
}
}