Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-12 18:09:27 +03:00
commit c1cc66fea8
162 changed files with 3408 additions and 1585 deletions

View file

@ -1,27 +1,14 @@
package com.tangem.data.networks.single
import arrow.core.Either
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.networks.store.NetworksStatusesStoreV2
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
import com.tangem.data.tokens.utils.NetworkStatusFactory
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.core.utils.catchOn
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -30,27 +17,20 @@ import javax.inject.Inject
/**
* Default implementation of [SingleNetworkStatusFetcher]
*
* @param excludedBlockchains excluded blockchains
* @property walletManagersFacade wallet managers facade
* @property networksStatusesStore networks statuses store
* @property userWalletsStore user wallets store
* @property appPreferencesStore app preferences store
* @property dispatchers dispatchers
* @property walletManagersFacade wallet managers facade
* @property networksStatusesStore networks statuses store
* @property cardCryptoCurrencyFactory card crypto currency factory
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
excludedBlockchains: ExcludedBlockchains,
private val walletManagersFacade: WalletManagersFacade,
private val networksStatusesStore: NetworksStatusesStoreV2,
private val userWalletsStore: UserWalletsStore,
private val appPreferencesStore: AppPreferencesStore,
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
private val dispatchers: CoroutineDispatcherProvider,
) : SingleNetworkStatusFetcher {
private val demoConfig = DemoConfig()
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig, excludedBlockchains)
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
private val networkStatusFactory = NetworkStatusFactory()
override suspend fun invoke(params: SingleNetworkStatusFetcher.Params) = Either.catchOn(dispatchers.default) {
@ -59,9 +39,10 @@ internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
is SingleNetworkStatusFetcher.Params.Simple -> {
networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network)
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
createCurrencies(userWallet = userWallet, network = params.network)
cardCryptoCurrencyFactory.create(
userWalletId = params.userWalletId,
network = params.network,
)
}
}
@ -105,36 +86,4 @@ internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
Timber.e("Failed to fetch network status for $params: $it")
networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network)
}
private suspend fun createCurrencies(userWallet: UserWallet, network: Network): List<CryptoCurrency> {
val blockchain = Blockchain.fromNetworkId(networkId = network.backendId)
// multi-currency wallet
if (userWallet.isMultiCurrency) return getMultiWalletCurrencies(userWallet = userWallet, network = network)
// check if the blockchain of single-currency wallet is the same as network
val cardBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
if (cardBlockchain != blockchain) return emptyList()
// single-currency wallet with token (NODL)
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
return cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
}
// single-currency wallet
return cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse).let(::listOf)
}
private suspend fun getMultiWalletCurrencies(userWallet: UserWallet, network: Network): List<CryptoCurrency> {
val response = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
key = PreferencesKeys.getUserTokensKey(userWallet.walletId.stringValue),
) ?: return emptyList()
return responseCurrenciesFactory.createCurrencies(
tokens = response.tokens.filter {
it.networkId == network.backendId && it.derivationPath == network.derivationPath.value
},
scanResponse = userWallet.scanResponse,
)
}
}

View file

@ -2,8 +2,8 @@ package com.tangem.data.networks.single
import com.google.common.truth.Truth
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.networks.store.NetworksStatusesStoreV2
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.walletmanager.WalletManagersFacade
@ -22,35 +22,35 @@ import org.junit.Test
*/
internal class DefaultSingleNetworkStatusFetcherTest {
private val walletManagersFacade: WalletManagersFacade = mockk(relaxed = true)
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxed = true)
private val userWalletsStore: UserWalletsStore = mockk(relaxed = true)
private val walletManagersFacade: WalletManagersFacade = mockk(relaxUnitFun = true)
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxUnitFun = true)
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
private val fetcher = DefaultSingleNetworkStatusFetcher(
excludedBlockchains = mockk(relaxed = true),
walletManagersFacade = walletManagersFacade,
networksStatusesStore = networksStatusesStore,
userWalletsStore = userWalletsStore,
appPreferencesStore = mockk(relaxed = true),
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@Test
fun `fetch network status successfully`() = runTest {
val params = SingleNetworkStatusFetcher.Params.Simple(userWalletId = userWalletId, network = network)
val params = createParams()
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } returns listOf(ethereum)
val result = UpdateWalletManagerResult.MissedDerivation
coEvery { walletManagersFacade.update(userWalletId, network, emptySet()) } returns result
coEvery { walletManagersFacade.update(params.userWalletId, params.network, emptySet()) } returns result
val actual = fetcher(params)
coVerifyOrder {
networksStatusesStore.refresh(userWalletId = userWalletId, network = network)
userWalletsStore.getSyncStrict(key = userWalletId)
walletManagersFacade.update(userWalletId, network, emptySet())
networksStatusesStore.refresh(params.userWalletId, params.network)
cardCryptoCurrencyFactory.create(params.userWalletId, params.network)
walletManagersFacade.update(params.userWalletId, params.network, emptySet())
networksStatusesStore.storeSuccess(
userWalletId = userWalletId,
value = NetworkStatus(network, NetworkStatus.MissedDerivation),
userWalletId = params.userWalletId,
value = NetworkStatus(params.network, NetworkStatus.MissedDerivation),
)
}
@ -59,17 +59,17 @@ internal class DefaultSingleNetworkStatusFetcherTest {
@Test
fun `fetch network status failure`() = runTest {
val params = SingleNetworkStatusFetcher.Params.Simple(userWalletId = userWalletId, network = network)
val params = createParams()
val exception = IllegalStateException()
coEvery { userWalletsStore.getSyncStrict(key = userWalletId) } throws exception
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } throws exception
val actual = fetcher(params)
coVerifyOrder {
networksStatusesStore.refresh(userWalletId = userWalletId, network = network)
userWalletsStore.getSyncStrict(key = userWalletId)
networksStatusesStore.storeError(userWalletId = userWalletId, network = network)
networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network)
cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network)
networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network)
}
coVerify(inverse = true) {
@ -81,8 +81,15 @@ internal class DefaultSingleNetworkStatusFetcherTest {
Truth.assertThat(actual.leftOrNull()).isEqualTo(exception)
}
private fun createParams(): SingleNetworkStatusFetcher.Params {
return SingleNetworkStatusFetcher.Params.Simple(
userWalletId = UserWalletId("011"),
network = ethereum.network,
)
}
private companion object {
val userWalletId = UserWalletId("011")
val network = MockCryptoCurrencyFactory().ethereum.network
val ethereum = MockCryptoCurrencyFactory().ethereum
}
}