From 5ed381c26378ec95b8e37dabe776352f76c58a31 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Sep 2023 14:19:25 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../viewmodels/TokensListMigration.kt | 11 +-- .../tokens/legacy/redux/TokensMiddleware.kt | 4 +- .../converters/CryptoCurrencyConverter.kt | 6 +- .../middlewares/TradeCryptoMiddleware.kt | 13 +-- .../java/com/tangem/utils/extensions/List.kt | 5 +- .../java/com/tangem/utils/extensions/Set.kt | 39 +++++++++ .../repository/DefaultCurrenciesRepository.kt | 27 +++--- .../repository/DefaultNetworksRepository.kt | 86 +++++++++---------- .../utils/CardCryptoCurrenciesFactory.kt | 61 +++++++++++++ .../tokens/utils/CardCurrenciesFactory.kt | 48 ----------- .../tokens/utils/CryptoCurrencyFactory.kt | 23 +++-- .../data/tokens/utils/NetworkConverter.kt | 22 ----- .../data/tokens/utils/NetworkOperations.kt | 31 ++++++- .../data/tokens/utils/NetworkStatusFactory.kt | 4 +- ....kt => ResponseCryptoCurrenciesFactory.kt} | 58 +++++++++---- .../data/tokens/utils/TokensOperations.kt | 45 +++++----- .../tokens/utils/UserTokensResponseFactory.kt | 2 +- .../repository/DefaultTxHistoryRepository.kt | 14 +-- .../DefaultWalletManagersFacade.kt | 74 +++++++++------- .../walletmanager/WalletManagersFacade.kt | 34 ++++---- .../domain/tokens/models/CryptoCurrency.kt | 72 ++++++++++++---- .../tangem/domain/tokens/models/Network.kt | 35 ++++++++ .../tokens/FetchCurrencyStatusUseCase.kt | 6 +- .../domain/tokens/FetchTokenListUseCase.kt | 6 +- .../domain/tokens/RemoveCurrencyUseCase.kt | 14 ++- .../tokens/model/CryptoCurrencyStatus.kt | 47 +++++----- .../domain/tokens/model/NetworkStatus.kt | 4 +- .../CurrenciesStatusesOperations.kt | 34 ++++---- .../operations/CurrencyStatusOperations.kt | 2 +- .../TokenListFiatBalanceOperations.kt | 1 + .../tokens/repository/NetworksRepository.kt | 17 +--- ...PrimaryCurrencyStatusUpdatesUseCaseTest.kt | 2 +- .../domain/tokens/GetTokenListUseCaseTest.kt | 2 +- .../tangem/domain/tokens/mock/MockNetworks.kt | 11 +-- .../tangem/domain/tokens/mock/MockTokens.kt | 73 +++++++++++----- .../domain/tokens/mock/MockTokensStates.kt | 4 +- .../repository/MockNetworksRepository.kt | 9 +- .../repository/TxHistoryRepository.kt | 10 +-- .../usecase/GetTxHistoryItemsCountUseCase.kt | 4 +- .../usecase/GetTxHistoryItemsUseCase.kt | 7 +- .../wallets/usecase/GetExploreUrlUseCase.kt | 9 +- .../TokenDetailsLoadedBalanceConverter.kt | 2 + .../viewmodels/TokenDetailsViewModel.kt | 8 +- .../presentation/common/WalletPreviewData.kt | 6 +- .../organizetokens/model/DraggableItem.kt | 6 +- .../utils/common/IdsOperations.kt | 2 +- .../CryptoCurrencyToDraggableItemConverter.kt | 2 +- .../NetworkGroupToDraggableItemsConverter.kt | 2 +- .../utils/dnd/DraggableGroupsOperations.kt | 2 +- .../state/components/WalletTokensListState.kt | 15 +++- ...letSingleCurrencyLoadedBalanceConverter.kt | 2 + .../multicurrency/MultiCurrencyContent.kt | 7 +- .../multicurrency/MultiCurrencyContentItem.kt | 2 +- ...ryptoCurrencyStatusToTokenItemConverter.kt | 1 + .../utils/TokenListToContentItemsConverter.kt | 9 +- .../wallet/viewmodels/WalletViewModel.kt | 44 ++++------ 56 files changed, 631 insertions(+), 455 deletions(-) create mode 100644 core/utils/src/main/java/com/tangem/utils/extensions/Set.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt rename data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/{ResponseCurrenciesFactory.kt => ResponseCryptoCurrenciesFactory.kt} (55%) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt index 8ce3e83f02..8340df9959 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt @@ -54,7 +54,6 @@ internal class TokensListMigration( } is Either.Right -> { currentUserWallet = selectedWalletEither.value - val derivationStyle = currentUserWallet.scanResponse.derivationStyleProvider.getDerivationStyle() when (val currenciesEither = getCurrenciesUseCase(userWalletId = selectedWalletEither.value.walletId)) { is Either.Left -> { @@ -65,7 +64,7 @@ internal class TokensListMigration( TokensListCryptoCurrencies( coins = currenciesEither.value .filterIsInstance() - .filterNot { it.isCustomCurrency(derivationStyle) } + .filterNot { it.isCustom } .also { currentNewCoins = it } .map { Blockchain.fromId(it.network.id.value) }, tokens = currenciesEither.value @@ -91,12 +90,6 @@ internal class TokensListMigration( } } - private fun CryptoCurrency.Coin.isCustomCurrency(derivationStyle: DerivationStyle?): Boolean { - if (derivationPath == null || derivationStyle == null) return false - - return derivationPath != Blockchain.fromId(network.id.value).derivationPath(derivationStyle)?.rawPath - } - private fun getLegacyCryptoCurrencies(): TokensListCryptoCurrencies { val wallets = store.state.walletState.walletsDataFromStores val derivationStyle = store.state.globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle() @@ -157,12 +150,14 @@ internal class TokensListMigration( cryptoCurrencyFactory.createToken( sdkToken = it.token, blockchain = it.blockchain, + extraDerivationPath = null, derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider, ) }, changedCoins = changedBlockchainList.mapNotNull { cryptoCurrencyFactory.createCoin( blockchain = it, + extraDerivationPath = null, derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider, ) }, diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt index 012bdf1618..aeecc533ac 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt @@ -298,7 +298,7 @@ object TokensMiddleware { val customTokensCandidates = currencyList .filter { Blockchain.fromId(it.network.id.value).getSupportedCurves().contains(curve) } - .mapNotNull(CryptoCurrency::derivationPath) + .mapNotNull { it.network.derivationPath.value } .map(::DerivationPath) val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList() @@ -306,7 +306,7 @@ object TokensMiddleware { currencyList.find { it is CryptoCurrency.Coin && Blockchain.fromId(it.network.id.value) == Blockchain.Cardano } ?.let { currency -> - currency.derivationPath?.let { + currency.network.derivationPath.value?.let { bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt b/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt index 832de0bda1..8506d82036 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt @@ -18,6 +18,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter requireNotNull( cryptoCurrencyFactory.createCoin( blockchain = value.blockchain, + extraDerivationPath = value.derivationPath, derivationStyleProvider = requireNotNull( store.state.globalState .userWalletsListManager @@ -31,6 +32,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter Currency.Blockchain( blockchain = blockchain, - derivationPath = value.derivationPath, + derivationPath = value.network.derivationPath.value, ) is CryptoCurrency.Token -> Currency.Token( token = Token( @@ -60,7 +62,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter MutableList.replaceBy(item: T, predicate: (T) -> Boolean): Boo /** * Adds the specified element to the list or replaces an existing element. - * The predicate defines the condition to replace the existing element. + * + * !!!This function is not thread-safe!!! * * @param item The element to be added or replace the existing one. * @param predicate The condition to replace an existing element. diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/Set.kt b/core/utils/src/main/java/com/tangem/utils/extensions/Set.kt new file mode 100644 index 0000000000..f5c0cf1eb7 --- /dev/null +++ b/core/utils/src/main/java/com/tangem/utils/extensions/Set.kt @@ -0,0 +1,39 @@ +package com.tangem.utils.extensions + +/** + * Replaces an element in the set with the provided item based on the predicate. + * + * !!!This function is not thread-safe!!! + * + * @param item The element to replace the existing one. + * @param predicate The condition to replace an existing element. + * @return [Boolean] indicating whether an element was replaced. + */ +inline fun MutableSet.replaceBy(item: T, predicate: (T) -> Boolean): Boolean { + val foundItem = firstOrNull(predicate) ?: return false + + remove(foundItem) + add(item) + + return true +} + +/** + * Adds the specified element to the set or replaces an existing element. + * + * !!!This function is not thread-safe!!! + * + * @param item The element to be added or replace the existing one. + * @param predicate The condition to replace an existing element. + * @return The modified [Set] after adding or replacing the element. + */ +inline fun Set.addOrReplace(item: T, predicate: (T) -> Boolean): Set { + val mutableList = this.toMutableSet() + val isReplaced = mutableList.replaceBy(item, predicate) + + if (!isReplaced) { + mutableList.add(item) + } + + return mutableList +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 0e83c656d4..5dda8b7e59 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -8,6 +8,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.token.UserMarketCoinsStore import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.core.error.DataError @@ -35,8 +36,8 @@ internal class DefaultCurrenciesRepository( ) : CurrenciesRepository { private val demoConfig = DemoConfig() - private val responseCurrenciesFactory = ResponseCurrenciesFactory(demoConfig) - private val cardCurrenciesFactory = CardCurrenciesFactory(demoConfig) + private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(demoConfig) + private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig) private val userTokensResponseFactory = UserTokensResponseFactory() override suspend fun saveTokens( @@ -90,6 +91,7 @@ internal class DefaultCurrenciesRepository( .mapNotNull { CryptoCurrencyFactory().createCoin( blockchain = getBlockchain(networkId = it.network.id), + extraDerivationPath = it.network.derivationPath.value, derivationStyleProvider = getUserWallet(userWalletId).scanResponse.derivationStyleProvider, ) } @@ -99,7 +101,7 @@ internal class DefaultCurrenciesRepository( return any { val blockchain = getBlockchain(networkId = token.network.id) - it.id == getCoinId(blockchain).rawCurrencyId + it.id == blockchain.toCoinId() } } @@ -173,10 +175,7 @@ internal class DefaultCurrenciesRepository( "Unable to find tokens response for user wallet with provided ID: $userWalletId" } - return responseCurrenciesFactory.createCurrencies( - response = storedTokens, - card = userWallet.scanResponse.card, - ) + return responseCurrenciesFactory.createCurrencies(storedTokens, userWallet.scanResponse) } override suspend fun getMultiCurrencyWalletCurrency( @@ -190,7 +189,7 @@ internal class DefaultCurrenciesRepository( "Unable to find tokens response for user wallet with provided ID: $userWalletId" } - responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse.card) + responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse) } override suspend fun getNetworkCoin(userWalletId: UserWalletId, networkId: Network.ID): CryptoCurrency.Coin { @@ -206,10 +205,7 @@ internal class DefaultCurrenciesRepository( val storedCoin = storedTokens.tokens.find { it.networkId == Blockchain.fromId(networkId.value).toNetworkId() } ?: error("Coin in this network $networkId not found") - val coin = responseCurrenciesFactory.createCurrency( - responseToken = storedCoin, - card = userWallet.scanResponse.card, - ) + val coin = responseCurrenciesFactory.createCurrency(storedCoin, userWallet.scanResponse) return coin as? CryptoCurrency.Coin ?: error("Unable to create currency") } @@ -242,7 +238,7 @@ internal class DefaultCurrenciesRepository( return userTokensStore.get(userWallet.walletId).map { storedTokens -> responseCurrenciesFactory.createCurrencies( response = storedTokens, - card = userWallet.scanResponse.card, + scanResponse = userWallet.scanResponse, ) } } @@ -288,10 +284,7 @@ internal class DefaultCurrenciesRepository( if (NOT_FOUND_HTTP_CODE in errorMessage) { val response = userTokensStore.getSyncOrNull(userWallet.walletId) ?: userTokensResponseFactory.createUserTokensResponse( - currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard( - card = userWallet.scanResponse.card, - derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, - ), + currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse), isGroupedByNetwork = false, isSortedByBalance = false, ) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index b8861a9533..667bf5c512 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -1,10 +1,9 @@ package com.tangem.data.tokens.repository import com.tangem.data.common.cache.CacheRegistry -import com.tangem.data.tokens.utils.CardCurrenciesFactory -import com.tangem.data.tokens.utils.NetworkConverter +import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory import com.tangem.data.tokens.utils.NetworkStatusFactory -import com.tangem.data.tokens.utils.ResponseCurrenciesFactory +import com.tangem.data.tokens.utils.ResponseCryptoCurrenciesFactory import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.demo.DemoConfig @@ -13,7 +12,6 @@ import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.addOrReplace @@ -29,25 +27,18 @@ internal class DefaultNetworksRepository( ) : NetworksRepository { private val demoConfig by lazy { DemoConfig() } - private val networkConverter by lazy { NetworkConverter() } - private val cardCurrenciesFactory by lazy { CardCurrenciesFactory(demoConfig) } - private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(demoConfig) } + private val cardCurrenciesFactory by lazy { CardCryptoCurrenciesFactory(demoConfig) } + private val responseCurrenciesFactory by lazy { ResponseCryptoCurrenciesFactory(demoConfig) } private val networkStatusFactory by lazy { NetworkStatusFactory() } - private val networksStatuses: MutableStateFlow> = MutableStateFlow(emptyList()) - - override fun getNetworks(networksIds: Set): Set { - return networkConverter.convertSet(networksIds) - } + private val networksStatuses: MutableStateFlow> = MutableStateFlow(hashSetOf()) override fun getNetworkStatusesUpdates( userWalletId: UserWalletId, - networks: Set, + networks: Set, ): Flow> = channelFlow { launch(dispatchers.io) { - networksStatuses.collect { - send(it.toSet()) - } + networksStatuses.collect(::send) } launch(dispatchers.io) { @@ -57,7 +48,7 @@ internal class DefaultNetworksRepository( override suspend fun getNetworkStatusesSync( userWalletId: UserWalletId, - networks: Set, + networks: Set, refresh: Boolean, ): Set = withContext(dispatchers.io) { fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh) @@ -66,14 +57,14 @@ internal class DefaultNetworksRepository( private suspend fun fetchNetworksStatusesIfCacheExpired( userWalletId: UserWalletId, - networks: Set, + networks: Set, refresh: Boolean, ) { coroutineScope { networks - .map { networkId -> + .map { network -> async { - fetchNetworkStatusIfCacheExpired(userWalletId, networkId, refresh) + fetchNetworkStatusIfCacheExpired(userWalletId, network, refresh) } } .awaitAll() @@ -82,67 +73,70 @@ internal class DefaultNetworksRepository( private suspend fun fetchNetworkStatusIfCacheExpired( userWalletId: UserWalletId, - networkId: Network.ID, + network: Network, refresh: Boolean, ) { cacheRegistry.invokeOnExpire( - key = getNetworksStatusesCacheKey(userWalletId, networkId), + key = getNetworksStatusesCacheKey(userWalletId, network), skipCache = refresh, - block = { fetchNetworkStatus(userWalletId, networkId) }, + block = { fetchNetworkStatus(userWalletId, network) }, ) } - private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, networkId: Network.ID) { - val currencies = getCurrencies(userWalletId) - .asSequence() - .filter { it.network.id == networkId } + private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, network: Network) { + val currencies = getCurrencies(userWalletId, network) val result = walletManagersFacade.update( userWalletId = userWalletId, - networkId = networkId, + network = network, extraTokens = currencies.filterIsInstance().toSet(), ) - // Invalidate cache key if wallet manager update failed - when (result) { - is UpdateWalletManagerResult.Verified, - is UpdateWalletManagerResult.NoAccount, - -> Unit - is UpdateWalletManagerResult.Unreachable, - is UpdateWalletManagerResult.MissedDerivation, - -> cacheRegistry.invalidate(getNetworksStatusesCacheKey(userWalletId, networkId)) - } - val networkStatus = networkStatusFactory.createNetworkStatus( - networkId = networkId, + network = network, result = result, currencies = currencies.toSet(), ) networksStatuses.update { statuses -> - statuses.addOrReplace(networkStatus) { it.networkId == networkStatus.networkId } + statuses.addOrReplace(networkStatus) { it.network == networkStatus.network } } + + invalidateCacheKeyIfNeeded(userWalletId, networkStatus) } - private suspend fun getCurrencies(userWalletId: UserWalletId): List { + private suspend fun getCurrencies(userWalletId: UserWalletId, network: Network): Sequence { val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find user wallet with provided ID: $userWalletId" } - return if (userWallet.isMultiCurrency) { + val currencies = if (userWallet.isMultiCurrency) { val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { "Unable to find tokens response for user wallet with provided ID: $userWalletId" } - responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse.card) + responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse).asSequence() } else { val currency = cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) - listOf(currency) + sequenceOf(currency) + } + + return currencies.filter { it.network == network } + } + + private suspend fun invalidateCacheKeyIfNeeded(userWalletId: UserWalletId, networkStatus: NetworkStatus) { + when (networkStatus.value) { + is NetworkStatus.Verified, + is NetworkStatus.NoAccount, + -> Unit + is NetworkStatus.Unreachable, + is NetworkStatus.MissedDerivation, + -> cacheRegistry.invalidate(getNetworksStatusesCacheKey(userWalletId, networkStatus.network)) } } - private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId, nerworkId: Network.ID): String { - return "network_status_${userWalletId}_${nerworkId.value}" + private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId, network: Network): String { + return "network_status_${userWalletId}_${network.id}_${network.derivationPath.value}" } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt new file mode 100644 index 0000000000..aa96c7a9b5 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt @@ -0,0 +1,61 @@ +package com.tangem.data.tokens.utils + +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.derivationStyleProvider +import com.tangem.domain.demo.DemoConfig +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.tokens.models.CryptoCurrency + +internal class CardCryptoCurrenciesFactory(private val demoConfig: DemoConfig) { + + private val cryptoCurrencyFactory = CryptoCurrencyFactory() + + fun createDefaultCoinsForMultiCurrencyCard(scanResponse: ScanResponse): List { + val cardDerivationStyleProvider = scanResponse.derivationStyleProvider + val card = scanResponse.card + + var blockchains = if (demoConfig.isDemoCardId(card.cardId)) { + demoConfig.demoBlockchains + } else { + listOf(Blockchain.Bitcoin, Blockchain.Ethereum) + } + + if (card.isTestCard) { + blockchains = blockchains.mapNotNull { it.getTestnetVersion() } + } + + return blockchains.mapNotNull { + cryptoCurrencyFactory.createCoin( + blockchain = it, + extraDerivationPath = null, + derivationStyleProvider = cardDerivationStyleProvider, + ) + } + } + + fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency { + val cardDerivationStyleProvider = scanResponse.derivationStyleProvider + val resolver = scanResponse.cardTypesResolver + val blockchain = resolver.getBlockchain() + + val coin = cryptoCurrencyFactory.createCoin( + blockchain = blockchain, + extraDerivationPath = null, + derivationStyleProvider = cardDerivationStyleProvider, + ) + requireNotNull(coin) { "Coin for the single currency card cannot be null" } + + val primaryToken = resolver.getPrimaryToken()?.let { token -> + cryptoCurrencyFactory.createToken( + sdkToken = token, + blockchain = blockchain, + extraDerivationPath = null, + derivationStyleProvider = cardDerivationStyleProvider, + ) + } + + return primaryToken ?: coin + } +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt deleted file mode 100644 index 269c65e9f7..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.tangem.data.tokens.utils - -import com.tangem.blockchain.common.Blockchain -import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.demo.DemoConfig -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.models.CryptoCurrency - -internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { - - private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() } - - fun createDefaultCoinsForMultiCurrencyCard( - card: CardDTO, - derivationStyleProvider: DerivationStyleProvider, - ): List { - var blockchains = if (demoConfig.isDemoCardId(card.cardId)) { - demoConfig.demoBlockchains - } else { - listOf(Blockchain.Bitcoin, Blockchain.Ethereum) - } - - if (card.isTestCard) { - blockchains = blockchains.mapNotNull { it.getTestnetVersion() } - } - - return blockchains.mapNotNull { cryptoCurrencyFactory.createCoin(it, derivationStyleProvider) } - } - - fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency { - val derivationStyleProvider = scanResponse.derivationStyleProvider - val resolver = scanResponse.cardTypesResolver - val blockchain = resolver.getBlockchain() - - val coin = requireNotNull(cryptoCurrencyFactory.createCoin(blockchain, derivationStyleProvider)) { - "Coin for the single currency card cannot be null" - } - val primaryToken = resolver.getPrimaryToken()?.let { token -> - cryptoCurrencyFactory.createToken(token, blockchain, derivationStyleProvider) - } - - return primaryToken ?: coin - } -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt index e6fff3176f..e12c65ff37 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt @@ -2,6 +2,7 @@ package com.tangem.data.tokens.utils import com.tangem.blockchain.common.Blockchain import com.tangem.domain.common.DerivationStyleProvider +import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.tokens.models.CryptoCurrency import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken @@ -12,6 +13,7 @@ class CryptoCurrencyFactory { fun createToken( sdkToken: SdkToken, blockchain: Blockchain, + extraDerivationPath: String?, derivationStyleProvider: DerivationStyleProvider, ): CryptoCurrency.Token? { if (blockchain == Blockchain.Unknown) { @@ -19,35 +21,40 @@ class CryptoCurrencyFactory { return null } - val id = getTokenId(blockchain, sdkToken) + val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null + val id = getTokenId(network, sdkToken) return CryptoCurrency.Token( id = id, - network = getNetwork(blockchain) ?: return null, + network = network, name = sdkToken.name, symbol = sdkToken.symbol, iconUrl = getTokenIconUrl(blockchain, sdkToken), decimals = sdkToken.decimals, - isCustom = isCustomToken(id), + isCustom = isCustomToken(id, network), contractAddress = sdkToken.contractAddress, - derivationPath = getDerivationPath(blockchain, derivationStyleProvider), ) } - fun createCoin(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): CryptoCurrency.Coin? { + fun createCoin( + blockchain: Blockchain, + extraDerivationPath: String?, + derivationStyleProvider: DerivationStyleProvider, + ): CryptoCurrency.Coin? { if (blockchain == Blockchain.Unknown) { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null } + val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null return CryptoCurrency.Coin( - id = getCoinId(blockchain), - network = getNetwork(blockchain) ?: return null, + id = getCoinId(network, blockchain.toCoinId()), + network = network, name = blockchain.fullName, symbol = blockchain.currency, iconUrl = getCoinIconUrl(blockchain), decimals = blockchain.decimals(), - derivationPath = getDerivationPath(blockchain, derivationStyleProvider), + isCustom = isCustomCoin(network), ) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt deleted file mode 100644 index 6c57906a38..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.tangem.data.tokens.utils - -import com.tangem.blockchain.common.Blockchain -import com.tangem.domain.tokens.models.Network -import com.tangem.utils.converter.Converter - -internal class NetworkConverter : Converter { - - override fun convert(value: Network.ID): Network? { - val blockchain = Blockchain.fromId(value.value) - - return getNetwork(blockchain) - } - - override fun convertList(input: Collection): List { - return input.mapNotNull(::convert) - } - - override fun convertSet(input: Collection): Set { - return input.mapNotNullTo(hashSetOf(), ::convert) - } -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkOperations.kt index 3a536a46ed..e70b7a4060 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkOperations.kt @@ -1,10 +1,19 @@ package com.tangem.data.tokens.utils import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.tokens.models.Network import timber.log.Timber -internal fun getNetwork(blockchain: Blockchain): Network? { +internal fun getBlockchain(networkId: Network.ID): Blockchain { + return Blockchain.fromId(networkId.value) +} + +internal fun getNetwork( + blockchain: Blockchain, + extraDerivationPath: String?, + derivationStyleProvider: DerivationStyleProvider, +): Network? { if (blockchain == Blockchain.Unknown) { Timber.e("Unable to convert Unknown blockchain to the domain network model") return null @@ -14,10 +23,26 @@ internal fun getNetwork(blockchain: Blockchain): Network? { id = Network.ID(blockchain.id), name = blockchain.fullName, isTestnet = blockchain.isTestnet(), + derivationPath = getDerivationPath(blockchain, extraDerivationPath, derivationStyleProvider), standardType = getNetworkStandardType(blockchain), ) } +private fun getDerivationPath( + blockchain: Blockchain, + extraDerivationPath: String?, + derivationStyleProvider: DerivationStyleProvider, +): Network.DerivationPath { + val cardDerivationPath = getCardDerivationPath(blockchain, derivationStyleProvider) + + return when { + cardDerivationPath.isNullOrBlank() -> Network.DerivationPath.None + extraDerivationPath == cardDerivationPath -> Network.DerivationPath.Card(extraDerivationPath) + !extraDerivationPath.isNullOrBlank() -> Network.DerivationPath.Custom(extraDerivationPath) + else -> Network.DerivationPath.None + } +} + private fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType { return when (blockchain) { Blockchain.Ethereum, Blockchain.EthereumTestnet -> Network.StandardType.ERC20 @@ -26,4 +51,8 @@ private fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType Blockchain.Tron, Blockchain.TronTestnet -> Network.StandardType.TRC20 else -> Network.StandardType.Unspecified(blockchain.name) } +} + +private fun getCardDerivationPath(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): String? { + return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index faed2bda5c..76f28d82a3 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -14,12 +14,12 @@ import java.math.BigDecimal internal class NetworkStatusFactory { fun createNetworkStatus( - networkId: Network.ID, + network: Network, result: UpdateWalletManagerResult, currencies: Set, ): NetworkStatus { return NetworkStatus( - networkId = networkId, + network = network, value = when (result) { is UpdateWalletManagerResult.MissedDerivation -> NetworkStatus.MissedDerivation is UpdateWalletManagerResult.Unreachable -> NetworkStatus.Unreachable diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCryptoCurrenciesFactory.kt similarity index 55% rename from data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt rename to data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCryptoCurrenciesFactory.kt index 514ef69b10..3a7208bcde 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCryptoCurrenciesFactory.kt @@ -3,47 +3,57 @@ package com.tangem.data.tokens.utils import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.datasource.api.tangemTech.models.UserTokensResponse +import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.common.extensions.toCoinId +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.demo.DemoConfig -import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.models.CryptoCurrency import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken -internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { +internal class ResponseCryptoCurrenciesFactory(private val demoConfig: DemoConfig) { - fun createCurrency(currencyId: CryptoCurrency.ID, response: UserTokensResponse, card: CardDTO): CryptoCurrency { + fun createCurrency( + currencyId: CryptoCurrency.ID, + response: UserTokensResponse, + scanResponse: ScanResponse, + ): CryptoCurrency { val responseTokenId = currencyId.rawCurrencyId val token = requireNotNull(response.tokens.firstOrNull { it.id == responseTokenId }) { "Unable find a token with provided ID: $responseTokenId" } - return requireNotNull(createCurrency(token, card)) { + return requireNotNull(createCurrency(token, scanResponse)) { "Unable to create a currency with provided ID: $currencyId" } } - fun createCurrencies(response: UserTokensResponse, card: CardDTO): List { - return response.tokens.mapNotNull { createCurrency(it, card) } + fun createCurrencies(response: UserTokensResponse, scanResponse: ScanResponse): List { + return response.tokens.mapNotNull { createCurrency(it, scanResponse) } } - fun createCurrency(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? { + fun createCurrency(responseToken: UserTokensResponse.Token, scanResponse: ScanResponse): CryptoCurrency? { var blockchain = Blockchain.fromNetworkId(responseToken.networkId) if (blockchain == null || blockchain == Blockchain.Unknown) { Timber.e("Unable to find a blockchain with the network ID: ${responseToken.networkId}") return null } + val cardDerivationStyleProvider = scanResponse.derivationStyleProvider + val card = scanResponse.card + if (demoConfig.isDemoCardId(card.cardId)) { blockchain = blockchain.getTestnetVersion() ?: blockchain } val sdkToken = createSdkToken(responseToken) return if (sdkToken == null) { - createCoin(blockchain, responseToken) + createCoin(blockchain, responseToken, cardDerivationStyleProvider) } else { - createToken(blockchain, sdkToken, responseToken.derivationPath) + createToken(blockchain, sdkToken, responseToken.derivationPath, cardDerivationStyleProvider) } } @@ -59,31 +69,43 @@ internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { } } - private fun createCoin(blockchain: Blockchain, responseToken: UserTokensResponse.Token): CryptoCurrency.Coin? { + private fun createCoin( + blockchain: Blockchain, + responseToken: UserTokensResponse.Token, + derivationStyleProvider: DerivationStyleProvider, + ): CryptoCurrency.Coin? { + val network = getNetwork(blockchain, responseToken.derivationPath, derivationStyleProvider) ?: return null + return CryptoCurrency.Coin( - id = getCoinId(blockchain), - network = getNetwork(blockchain) ?: return null, + id = getCoinId(network, blockchain.toCoinId()), + network = network, name = responseToken.name, symbol = responseToken.symbol, decimals = responseToken.decimals, - derivationPath = responseToken.derivationPath, iconUrl = getCoinIconUrl(blockchain), + isCustom = isCustomCoin(network), ) } - private fun createToken(blockchain: Blockchain, sdkToken: Token, derivationPath: String?): CryptoCurrency.Token? { - val id = getTokenId(blockchain, sdkToken) + private fun createToken( + blockchain: Blockchain, + sdkToken: Token, + responseDerivationPath: String?, + derivationStyleProvider: DerivationStyleProvider, + ): CryptoCurrency.Token? { + val network = getNetwork(blockchain, responseDerivationPath, derivationStyleProvider) + ?: return null + val id = getTokenId(network, sdkToken) return CryptoCurrency.Token( id = id, - network = getNetwork(blockchain) ?: return null, + network = network, name = sdkToken.name, symbol = sdkToken.symbol, decimals = sdkToken.decimals, - derivationPath = derivationPath, iconUrl = getTokenIconUrl(blockchain, sdkToken), contractAddress = sdkToken.contractAddress, - isCustom = isCustomToken(id), + isCustom = isCustomToken(id, network), ) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index a95d6efa08..5f5e666c48 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -2,14 +2,13 @@ package com.tangem.data.tokens.utils import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.IconsUtil -import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.tokens.models.CryptoCurrency.ID import com.tangem.domain.tokens.models.Network import com.tangem.blockchain.common.Token as SdkToken +import com.tangem.domain.tokens.models.CryptoCurrency.ID.Body as CurrencyIdBody import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.COIN_PREFIX as COIN_ID_PREFIX -import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.CUSTOM_TOKEN_PREFIX as CUSTOM_TOKEN_ID_PREFIX import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.TOKEN_PREFIX as TOKEN_ID_PREFIX import com.tangem.domain.tokens.models.CryptoCurrency.ID.Suffix.ContractAddress as CustomCurrencyIdSuffix import com.tangem.domain.tokens.models.CryptoCurrency.ID.Suffix.RawID as CurrencyIdSuffix @@ -18,24 +17,27 @@ private const val DEFAULT_TOKENS_ICONS_HOST = "https://s3.eu-central-1.amazonaws private const val TOKEN_ICON_SIZE = "large" private const val TOKEN_ICON_EXT = "png" -internal fun isCustomToken(tokenId: ID): Boolean { - return tokenId.rawCurrencyId == null +internal fun isCustomToken(tokenId: ID, network: Network): Boolean { + return network.derivationPath is Network.DerivationPath.Custom || tokenId.rawCurrencyId == null } -internal fun getDerivationPath(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): String? { - return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath +internal fun isCustomCoin(network: Network): Boolean { + return network.derivationPath is Network.DerivationPath.Custom } -internal fun getBlockchain(networkId: Network.ID): Blockchain { - return Blockchain.fromId(networkId.value) +internal fun getCoinId(network: Network, coinId: String): ID { + return ID(COIN_ID_PREFIX, getCurrencyIdBody(network), CurrencyIdSuffix(rawId = coinId)) } -internal fun getCoinId(blockchain: Blockchain): ID { - return getTokenOrCoinId(blockchain, token = null) -} +internal fun getTokenId(network: Network, sdkToken: SdkToken): ID { + val sdkTokenId = sdkToken.id + val suffix = if (sdkTokenId == null) { + CustomCurrencyIdSuffix(contractAddress = sdkToken.contractAddress) + } else { + CurrencyIdSuffix(rawId = sdkTokenId) + } -internal fun getTokenId(blockchain: Blockchain, token: SdkToken): ID { - return getTokenOrCoinId(blockchain, token) + return ID(TOKEN_ID_PREFIX, getCurrencyIdBody(network), suffix) } internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? { @@ -58,15 +60,16 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? { return coinId?.let(::getTokenIconUrlFromDefaultHost) } -private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): ID { - val sdkTokenId = token?.id - val (prefix, suffix) = when { - token == null -> COIN_ID_PREFIX to CurrencyIdSuffix(rawId = blockchain.toCoinId()) - sdkTokenId == null -> CUSTOM_TOKEN_ID_PREFIX to CustomCurrencyIdSuffix(contractAddress = token.contractAddress) - else -> TOKEN_ID_PREFIX to CurrencyIdSuffix(rawId = sdkTokenId) +private fun getCurrencyIdBody(network: Network): CurrencyIdBody { + return when (val path = network.derivationPath) { + is Network.DerivationPath.Custom -> CurrencyIdBody.NetworkIdWithDerivationPath( + rawId = network.id.value, + derivationPath = path.value, + ) + is Network.DerivationPath.Card, + is Network.DerivationPath.None, + -> CurrencyIdBody.NetworkId(network.id.value) } - - return ID(prefix, Network.ID(blockchain.id), suffix) } private fun getTokenIconUrlFromDefaultHost(tokenId: String): String { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index c6cbc7f858..c8cb48b178 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -32,7 +32,7 @@ internal class UserTokensResponseFactory { return UserTokensResponse.Token( id = currency.id.rawCurrencyId, networkId = blockchain.toNetworkId(), - derivationPath = currency.derivationPath, + derivationPath = currency.network.derivationPath.value, name = currency.name, symbol = currency.symbol, decimals = currency.decimals, diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt index a042400894..7aa4add858 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt @@ -19,12 +19,11 @@ class DefaultTxHistoryRepository( private val userWalletsStore: UserWalletsStore, ) : TxHistoryRepository { - override suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int { + override suspend fun getTxHistoryItemsCount(network: Network): Int { val userWallet = getUserWallet() val state = walletManagersFacade.getTxHistoryState( userWalletId = userWallet.walletId, - networkId = networkId, - rawDerivationPath = derivationPath, + network = network, ) return when (state) { is TxHistoryState.Failed.FetchError -> throw TxHistoryStateError.DataError(state.exception) @@ -34,11 +33,7 @@ class DefaultTxHistoryRepository( } } - override fun getTxHistoryItems( - networkId: Network.ID, - derivationPath: String?, - pageSize: Int, - ): Flow> { + override fun getTxHistoryItems(network: Network, pageSize: Int): Flow> { val userWallet = getUserWallet() return Pager( config = PagingConfig( @@ -49,8 +44,7 @@ class DefaultTxHistoryRepository( loadPage = { page: Int, pageSize: Int -> walletManagersFacade.getTxHistoryItems( userWalletId = userWallet.walletId, - networkId = networkId, - rawDerivationPath = derivationPath, + network = network, page = page, pageSize = pageSize, ) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index deb238337d..dd1cf7a25b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -8,7 +8,6 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore -import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.models.CryptoCurrency @@ -39,42 +38,46 @@ class DefaultWalletManagersFacade( override suspend fun update( userWalletId: UserWalletId, - networkId: Network.ID, + network: Network, extraTokens: Set, ): UpdateWalletManagerResult { val userWallet = getUserWallet(userWalletId) - val blockchain = Blockchain.fromId(networkId.value) + val blockchain = Blockchain.fromId(network.id.value) + val derivationPath = network.derivationPath.value - return getAndUpdateWalletManager(userWallet, blockchain, extraTokens) + return getAndUpdateWalletManager(userWallet, blockchain, derivationPath, extraTokens) } - override suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String { + override suspend fun getExploreUrl(userWalletId: UserWalletId, network: Network): String { val userWallet = getUserWallet(userWalletId) + val blockchain = Blockchain.fromId(network.id.value) - val blockchain = Blockchain.fromId(networkId.value) - - return getOrCreateWalletManager( + val walletManager = getOrCreateWalletManager( userWallet = userWallet, blockchain = blockchain, - derivationPath = blockchain - .derivationPath(userWallet.scanResponse.derivationStyleProvider.getDerivationStyle()), + derivationPath = network.derivationPath.value, ) - ?.wallet - ?.getExploreUrl() - .orEmpty() - } - override suspend fun getTxHistoryState( - userWalletId: UserWalletId, - networkId: Network.ID, - rawDerivationPath: String?, - ): TxHistoryState { - val userWallet = getUserWallet(userWalletId) - val blockchain = Blockchain.fromId(networkId.value) - val derivationPath = rawDerivationPath?.let(::DerivationPath) - val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) { + requireNotNull(walletManager) { "Unable to get a wallet manager for blockchain: $blockchain" } + + return walletManager.wallet.getExploreUrl() + } + + override suspend fun getTxHistoryState(userWalletId: UserWalletId, network: Network): TxHistoryState { + val userWallet = getUserWallet(userWalletId) + val blockchain = Blockchain.fromId(network.id.value) + val walletManager = getOrCreateWalletManager( + userWallet = userWallet, + blockchain = blockchain, + derivationPath = network.derivationPath.value, + ) + + requireNotNull(walletManager) { + "Unable to get a wallet manager for blockchain: $blockchain" + } + return walletManager .getTransactionHistoryState(walletManager.wallet.address) .let(txHistoryStateConverter::convert) @@ -82,17 +85,22 @@ class DefaultWalletManagersFacade( override suspend fun getTxHistoryItems( userWalletId: UserWalletId, - networkId: Network.ID, - rawDerivationPath: String?, + network: Network, page: Int, pageSize: Int, ): PaginationWrapper { val userWallet = getUserWallet(userWalletId) - val blockchain = Blockchain.fromId(networkId.value) - val derivationPath = rawDerivationPath?.let(::DerivationPath) - val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) { + val blockchain = Blockchain.fromId(network.id.value) + val walletManager = getOrCreateWalletManager( + userWallet = userWallet, + blockchain = blockchain, + derivationPath = network.derivationPath.value, + ) + + requireNotNull(walletManager) { "Unable to get a wallet manager for blockchain: $blockchain" } + val itemsResult = walletManager.getTransactionsHistory( address = walletManager.wallet.address, page = page, @@ -118,12 +126,12 @@ class DefaultWalletManagersFacade( private suspend fun getAndUpdateWalletManager( userWallet: UserWallet, blockchain: Blockchain, + derivationPath: String?, extraTokens: Set, ): UpdateWalletManagerResult { val scanResponse = userWallet.scanResponse - val derivationPath = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath.rawPath)) { + if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath)) { Timber.e("Derivation missed for: $blockchain") return UpdateWalletManagerResult.MissedDerivation } @@ -171,21 +179,21 @@ class DefaultWalletManagersFacade( override suspend fun getOrCreateWalletManager( userWallet: UserWallet, blockchain: Blockchain, - derivationPath: DerivationPath?, + derivationPath: String?, ): WalletManager? { val userWalletId = userWallet.walletId var walletManager = walletManagersStore.getSyncOrNull( userWalletId = userWalletId, blockchain = blockchain, - derivationPath = derivationPath?.rawPath, + derivationPath = derivationPath, ) if (walletManager == null) { walletManager = walletManagerFactory.createWalletManager( scanResponse = userWallet.scanResponse, blockchain = blockchain, - derivationPath = derivationPath, + derivationPath = derivationPath?.let { DerivationPath(rawPath = it) }, ) ?: return null walletManagersStore.store(userWalletId, walletManager) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 4c296b01e4..bcb4409033 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -2,7 +2,6 @@ package com.tangem.domain.walletmanager import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager -import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.txhistory.models.PaginationWrapper @@ -22,52 +21,53 @@ interface WalletManagersFacade { * Updates the wallet manager associated with a user's wallet and network. * * @param userWalletId The ID of the user's wallet. - * @param networkId The network ID. + * @param network The network. * @param extraTokens Additional tokens. * @return The result of updating the wallet manager. */ suspend fun update( userWalletId: UserWalletId, - networkId: Network.ID, + network: Network, extraTokens: Set, ): UpdateWalletManagerResult - suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String + /** + * Returns network explorer URL of the wallet manager associated with a user's wallet and network. + * + * @param userWalletId The ID of the user's wallet. + * @param network The network. + * + * @return The network explorer URL, maybe empty if the wallet manager was not found. + * */ + suspend fun getExploreUrl(userWalletId: UserWalletId, network: Network): String /** * Returns transactions count * * @param userWalletId The ID of the user's wallet. - * @param networkId The network ID. - * @param rawDerivationPath Derivation path in raw form. - + * @param network The network. */ - suspend fun getTxHistoryState( - userWalletId: UserWalletId, - networkId: Network.ID, - rawDerivationPath: String?, - ): TxHistoryState + suspend fun getTxHistoryState(userWalletId: UserWalletId, network: Network): TxHistoryState /** * Returns transaction history items wrapped to pagination * * @param userWalletId The ID of the user's wallet. - * @param networkId The network ID. - * @param rawDerivationPath Derivation path in raw form. + * @param network The network. * @param page Pagination page. * @param pageSize Pagination size. */ suspend fun getTxHistoryItems( userWalletId: UserWalletId, - networkId: Network.ID, - rawDerivationPath: String?, + network: Network, page: Int, pageSize: Int, ): PaginationWrapper + // TODO: Remove after refactoring suspend fun getOrCreateWalletManager( userWallet: UserWallet, blockchain: Blockchain, - derivationPath: DerivationPath?, + derivationPath: String?, ): WalletManager? } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt index 562625c73d..94d8104980 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt @@ -11,8 +11,7 @@ import java.io.Serializable * @property symbol Symbol of the cryptocurrency. * @property decimals Number of decimal places used by the cryptocurrency. * @property iconUrl Optional URL of the cryptocurrency icon. `null` if not found. - * @property derivationPath Optional path used for key derivation. `null` if the wallet does not support the - * [HD Wallet](https://coinsutra.com/hd-wallets-deterministic-wallet/) feature. + * @property isCustom Indicates whether the currency is a custom user-added currency or not. */ // FIXME: Remove serialization [REDACTED_JIRA] sealed class CryptoCurrency : Serializable { @@ -23,7 +22,7 @@ sealed class CryptoCurrency : Serializable { abstract val symbol: String abstract val decimals: Int abstract val iconUrl: String? - abstract val derivationPath: String? + abstract val isCustom: Boolean /** * Represents a native coin in the blockchain network. @@ -35,7 +34,7 @@ sealed class CryptoCurrency : Serializable { override val symbol: String, override val decimals: Int, override val iconUrl: String?, - override val derivationPath: String?, + override val isCustom: Boolean, ) : CryptoCurrency() { init { @@ -47,7 +46,6 @@ sealed class CryptoCurrency : Serializable { * Represents a token in the blockchain network, typically a non-native asset. * * @property contractAddress Address of the contract managing the token. - * @property isCustom Indicates whether the token is a custom user-added token or not. */ data class Token( override val id: ID, @@ -56,9 +54,8 @@ sealed class CryptoCurrency : Serializable { override val symbol: String, override val decimals: Int, override val iconUrl: String?, - override val derivationPath: String?, + override val isCustom: Boolean, val contractAddress: String, - val isCustom: Boolean, ) : CryptoCurrency() { init { @@ -80,34 +77,70 @@ sealed class CryptoCurrency : Serializable { // FIXME: Remove serialization [REDACTED_JIRA] data class ID( private val prefix: Prefix, - private val networkId: Network.ID, + private val body: Body, private val suffix: Suffix, ) : Serializable { val value: String = buildString { append(prefix.value) - append(networkId.value) - append(DELIMITER) + append(PREFIX_DELIMITER) + append(body.value) + append(SUFFIX_DELIMITER) append(suffix.value) } + /** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null`. */ val rawCurrencyId: String? = (suffix as? Suffix.RawID)?.rawId - val rawNetworkId: String = networkId.value + /** Represents a raw cryptocurrency's network ID. */ + val rawNetworkId: String = when (body) { + is Body.NetworkId -> body.rawId + is Body.NetworkIdWithDerivationPath -> body.rawId + } /** * Represents the different types of prefixes that can be associated with a cryptocurrency ID. + * * These prefixes can help in quickly categorizing the type of cryptocurrency. */ enum class Prefix(val value: String) { /** Prefix for standard coins. */ - COIN_PREFIX(value = "coin_"), + COIN_PREFIX(value = "coin"), /** Prefix for standard tokens. */ - TOKEN_PREFIX(value = "token_"), + TOKEN_PREFIX(value = "token"), + } - /** Prefix for custom tokens. */ - CUSTOM_TOKEN_PREFIX(value = "custom_"), + /** + * Represents the body part of the cryptocurrency ID. + * + * The body can be either a raw network ID or a raw network ID with a network derivation path. + */ + sealed class Body { + + /** The value of the body. */ + abstract val value: String + + /** Represents a raw network ID. */ + data class NetworkId(val rawId: String) : Body() { + override val value: String = rawId + } + + /** + * Represents a raw network ID with a network derivation path. + * + * Should be used for a cryptocurrencies with custom derivation path. + * */ + data class NetworkIdWithDerivationPath( + val rawId: String, + val derivationPath: String, + ) : Body() { + override val value: String = buildString { + append(rawId) + append(DERIVATION_PATH_DELIMITER) + append(derivationPath.hashCode()) + } + } } /** @@ -132,8 +165,14 @@ sealed class CryptoCurrency : Serializable { } } + override fun toString(): String { + return "ID(value='$value')" + } + private companion object { - const val DELIMITER = '#' + const val PREFIX_DELIMITER = '_' + const val SUFFIX_DELIMITER = '#' + const val DERIVATION_PATH_DELIMITER = 'd' } } @@ -142,6 +181,5 @@ sealed class CryptoCurrency : Serializable { require(symbol.isNotBlank()) { "Crypto currency symbol must not be blank" } require(iconUrl?.isNotBlank() ?: true) { "Crypto currency icon URL must not be blank" } require(decimals >= 0) { "Crypto currency decimal must not be less then zero, but it is: $decimals" } - require(derivationPath?.isNotBlank() ?: true) { "Crypto currency derivation path must not be blank" } } } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Network.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Network.kt index aaa1cb39d8..9c1091ac3e 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Network.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Network.kt @@ -11,6 +11,7 @@ import java.io.Serializable * * @property id The unique identifier of the network. * @property name The human-readable name of the network, such as "Ethereum" or "Bitcoin". + * @property derivationPath The path used to derive keys for this network. * @property isTestnet Indicates whether the network is a test network or a main network. * @property standardType The type of blockchain standard the network adheres to. */ @@ -18,6 +19,7 @@ import java.io.Serializable data class Network( val id: ID, val name: String, + val derivationPath: DerivationPath, val isTestnet: Boolean, val standardType: StandardType, ) : Serializable { @@ -39,6 +41,39 @@ data class Network( } } + /** + * Represents a path used to derive cryptographic keys for a blockchain network. + * + * This class represents such paths in a generic manner, allowing for predefined card-based paths, + * custom paths, or even no derivation path at all. + */ + sealed class DerivationPath { + + /** The actual derivation path value, if any. */ + abstract val value: String? + + /** + * Represents a predefined card-based derivation path. + * + * @property value The derivation path string. + */ + data class Card(override val value: String) : DerivationPath() + + /** + * Represents a custom derivation path specified by the user. + * + * @property value The derivation path string. + */ + data class Custom(override val value: String) : DerivationPath() + + /** + * Represents a lack of derivation path. + */ + object None : DerivationPath() { + override val value: String? = null + } + } + /** * Represents the type of blockchain standard that a network adheres to. * diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt index b10e3633c9..91f7e713a4 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt @@ -75,7 +75,7 @@ class FetchCurrencyStatusUseCase( refresh: Boolean, ) = coroutineScope { val fetchStatus = async { - fetchNetworkStatus(userWalletId, currency.network.id, refresh) + fetchNetworkStatus(userWalletId, currency.network, refresh) } val fetchQuote = async { fetchQuote(currency.id, refresh) @@ -101,11 +101,11 @@ class FetchCurrencyStatusUseCase( private suspend fun Raise.fetchNetworkStatus( userWalletId: UserWalletId, - networkId: Network.ID, + network: Network, refresh: Boolean, ) { catch( - block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(networkId), refresh) }, + block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(network), refresh) }, ) { raise(CurrencyStatusError.DataError(it)) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt index 7e379288b9..9dc971195a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt @@ -48,7 +48,7 @@ class FetchTokenListUseCase( val fetchStatuses = async { fetchNetworksStatuses( userWalletId, - currencies.mapTo(hashSetOf()) { it.network.id }, + currencies.mapTo(hashSetOf()) { it.network }, refresh, ) } @@ -81,11 +81,11 @@ class FetchTokenListUseCase( private suspend fun Raise.fetchNetworksStatuses( userWalletId: UserWalletId, - networksIds: Set, + networks: Set, refresh: Boolean, ) { catch( - block = { networksRepository.getNetworkStatusesSync(userWalletId, networksIds, refresh) }, + block = { networksRepository.getNetworkStatusesSync(userWalletId, networks, refresh) }, ) { raise(TokenListError.DataError(it)) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RemoveCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RemoveCurrencyUseCase.kt index dba5642239..e2a908df3b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RemoveCurrencyUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RemoveCurrencyUseCase.kt @@ -27,10 +27,16 @@ class RemoveCurrencyUseCase( } suspend fun hasLinkedTokens(userWalletId: UserWalletId, currency: CryptoCurrency): Boolean { - val walletCurrencies = currenciesRepository - .getMultiCurrencyWalletCurrenciesSync(userWalletId = userWalletId, refresh = false) + return when (currency) { + is CryptoCurrency.Coin -> { + val walletCurrencies = currenciesRepository.getMultiCurrencyWalletCurrenciesSync( + userWalletId = userWalletId, + refresh = false, + ) - return currency is CryptoCurrency.Coin && - walletCurrencies.any { it != currency && it.network.id == currency.network.id } + walletCurrencies.any { it is CryptoCurrency.Token && it.network == currency.network } + } + is CryptoCurrency.Token -> false + } } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt index 5f4f6aa6d0..603ffb73f7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt @@ -7,7 +7,7 @@ import java.math.BigDecimal /** * Represents the status of a cryptocurrency asset within a network. * - * This class encapsulates the details of a specific cryptocurrency, either a coin or token, + * This class encapsulates the details of a specific cryptocurrency, either a coin or cryptocurrency, * along with its current status within the blockchain network. The status can include various states * like Loading, Unreachable, Loaded, etc. * @@ -20,22 +20,22 @@ data class CryptoCurrencyStatus( ) { /** - * Represents the various states a token can have, encapsulating different information based on the state. + * Represents the various states a cryptocurrency can have, encapsulating different information based on the state. * * @property isError Indicates whether this status represents an error status. */ sealed class Status(val isError: Boolean) { - /** The amount of the token. */ + /** The amount of the cryptocurrency. */ open val amount: BigDecimal? = null - /** The fiat equivalent of the token's amount. */ + /** The fiat equivalent of the cryptocurrency's amount. */ open val fiatAmount: BigDecimal? = null - /** The exchange rate used for converting the token amount to fiat. */ + /** The exchange rate used for converting the cryptocurrency amount to fiat. */ open val fiatRate: BigDecimal? = null - /** The change in price of the token. */ + /** The change in price of the cryptocurrency. */ open val priceChange: BigDecimal? = null /** Indicates if there are any transactions in progress related to the cryptocurrency network. */ @@ -48,25 +48,28 @@ data class CryptoCurrencyStatus( open val networkAddress: NetworkAddress? = null } - /** Represents the Loading state of a token, typically while fetching its details. */ + /** Represents the Loading state of a cryptocurrency, typically while fetching its details. */ object Loading : Status(isError = false) - /** Represents a state where the token is not reachable. */ + /** Represents a state where the cryptocurrency is not reachable. */ object Unreachable : Status(isError = true) - /** Represents a state where the token's derivation is missed. */ + /** Represents a state where the cryptocurrency's network amount not found. */ + object NoAmount : Status(isError = true) + + /** Represents a state where the cryptocurrency's derivation is missed. */ object MissedDerivation : Status(isError = true) - /** Represents a state where there is no account associated with the token. */ + /** Represents a state where there is no account associated with the cryptocurrency. */ object NoAccount : Status(isError = false) /** - * Represents a Loaded state of a token with complete information. + * Represents a Loaded state of a cryptocurrency with complete information. * - * @property amount The amount of the token. - * @property fiatAmount The fiat equivalent of the token's amount. - * @property fiatRate The exchange rate used for converting the token amount to fiat. - * @property priceChange The change in price of the token. + * @property amount The amount of the cryptocurrency. + * @property fiatAmount The fiat equivalent of the cryptocurrency's amount. + * @property fiatRate The exchange rate used for converting the cryptocurrency amount to fiat. + * @property priceChange The change in price of the cryptocurrency. * @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the * cryptocurrency network. * @property pendingTransactions The current cryptocurrency transactions. @@ -82,12 +85,12 @@ data class CryptoCurrencyStatus( ) : Status(isError = false) /** - * Represents a Custom state of a token, typically used for user-defined tokens. + * Represents a Custom state of a cryptocurrency, typically used for user-defined tokens. * - * @property amount The amount of the token. - * @property fiatAmount The fiat equivalent of the token's amount (optional). - * @property fiatRate The exchange rate used for converting the token amount to fiat (optional). - * @property priceChange The change in price of the token (optional). + * @property amount The amount of the cryptocurrency. + * @property fiatAmount The fiat equivalent of the cryptocurrency's amount (optional). + * @property fiatRate The exchange rate used for converting the cryptocurrency amount to fiat (optional). + * @property priceChange The change in price of the cryptocurrency (optional). * @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the * cryptocurrency network. * @property pendingTransactions The current cryptocurrency transactions. @@ -103,9 +106,9 @@ data class CryptoCurrencyStatus( ) : Status(isError = false) /** - * Represents a state where the token is available, but there is no current quote available for it. + * Represents a state where the cryptocurrency is available, but there is no current quote available for it. * - * @property amount The amount of the token. + * @property amount The amount of the cryptocurrency. * @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the * cryptocurrency network. * @property pendingTransactions The current cryptocurrency transactions. diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt index 87be054645..ef1c54b04f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt @@ -8,11 +8,11 @@ import java.math.BigDecimal /** * Represents the status of a specific blockchain network. * - * @property networkId The unique identifier of the network for which the status is provided. + * @property network The network for which the status is provided. * @property value The specific status value, represented as a sealed class to encapsulate the various possible states of the network. */ data class NetworkStatus( - val networkId: Network.ID, + val network: Network, val value: Status, ) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index e72a1769bb..07a72dbee4 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -58,11 +58,11 @@ internal class CurrenciesStatusesOperations( emit(maybeLoadingCurrenciesStatuses) - val (networksIds, currenciesIds) = getIds(nonEmptyCurrencies) + val (networks, currenciesIds) = getIds(nonEmptyCurrencies) val currenciesFlow = combine( getQuotes(currenciesIds), - getNetworksStatuses(networksIds), + getNetworksStatuses(networks), ) { maybeQuotes, maybeNetworksStatuses -> createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses) } @@ -99,7 +99,7 @@ internal class CurrenciesStatusesOperations( } private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow> { - val (networksIds, currenciesIds) = getIds(nonEmptyListOf(currency)) + val (networks, currenciesIds) = getIds(nonEmptyListOf(currency)) val quoteFlow = getQuotes(currenciesIds) .map { maybeQuotes -> @@ -108,15 +108,15 @@ internal class CurrenciesStatusesOperations( } } - val statusFlow = getNetworksStatuses(networksIds) + val statusFlow = getNetworksStatuses(networks) .map { maybeStatuses -> maybeStatuses.map { statuses -> - statuses.singleOrNull { it.networkId == currency.network.id } + statuses.singleOrNull { it.network == currency.network } } } return combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus -> - createStatus(currency, maybeQuote, maybeNetworkStatus) + createCurrencyStatus(currency, maybeQuote, maybeNetworkStatus) } } @@ -135,13 +135,13 @@ internal class CurrenciesStatusesOperations( currencies.map { currency -> val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } - val networkStatus = networksStatuses?.firstOrNull { it.networkId == currency.network.id } + val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network } - createStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed) + createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed) } } - private fun createStatus( + private fun createCurrencyStatus( currency: CryptoCurrency, maybeQuote: Either, maybeNetworkStatus: Either, @@ -154,10 +154,10 @@ internal class CurrenciesStatusesOperations( null } - createStatus(currency, quote, networkStatus, ignoreQuote = quoteRetrievingFailed) + createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quoteRetrievingFailed) } - private fun createStatus( + private fun createCurrencyStatus( currency: CryptoCurrency, quote: Quote?, networkStatus: NetworkStatus?, @@ -206,7 +206,7 @@ internal class CurrenciesStatusesOperations( .onEmpty { emit(Error.EmptyQuotes.left()) } } - private fun getNetworksStatuses(networks: NonEmptySet): Flow>> { + private fun getNetworksStatuses(networks: NonEmptySet): Flow>> { return networksRepository.getNetworkStatusesUpdates(userWalletId, networks) .map, Either>> { it.right() } .catch { emit(Error.DataError(it).left()) } @@ -215,17 +215,17 @@ internal class CurrenciesStatusesOperations( private fun getIds( currencies: NonEmptyList, - ): Pair, NonEmptySet> { + ): Pair, NonEmptySet> { val currencyIdToNetworkId = currencies.associate { currency -> - currency.id to currency.network.id + currency.id to currency.network } val currenciesIds = currencyIdToNetworkId.keys.toNonEmptySetOrNull() - val networksIds = currencyIdToNetworkId.values.toNonEmptySetOrNull() + val networks = currencyIdToNetworkId.values.toNonEmptySetOrNull() requireNotNull(currenciesIds) { "Currencies IDs cannot be empty" } - requireNotNull(networksIds) { "Networks IDs cannot be empty" } + requireNotNull(networks) { "Networks IDs cannot be empty" } - return networksIds to currenciesIds + return networks to currenciesIds } sealed class Error { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index 2082010b8d..4e043eaa52 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -26,7 +26,7 @@ internal class CurrencyStatusOperations( } private fun createStatus(status: NetworkStatus.Verified): CryptoCurrencyStatus.Status { - val amount = status.amounts[currency.id] ?: return CryptoCurrencyStatus.Unreachable + val amount = status.amounts[currency.id] ?: return CryptoCurrencyStatus.NoAmount val hasCurrentNetworkTransactions = status.pendingTransactions.isNotEmpty() val currentTransactions = status.pendingTransactions.getOrElse(currency.id, ::emptySet) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt index ec63a9fa0c..326fceb11f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt @@ -22,6 +22,7 @@ internal class TokenListFiatBalanceOperations( } is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, -> { fiatBalance = TokenList.FiatBalance.Failed break diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt index b019c7f3f5..a69acb7bec 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt @@ -9,25 +9,16 @@ import kotlinx.coroutines.flow.Flow * Repository for everything related to the blockchain networks * */ interface NetworksRepository { - - /** - * Retrieves the details of the specified blockchain networks, identified by their unique IDs. - * - * @param networksIds The unique identifiers of the networks to be retrieved. - * @return A set of [Network] objects corresponding to the specified network IDs. - */ - fun getNetworks(networksIds: Set): Set - /** * Retrieves updates of network statuses of specified blockchain networks for a specific user wallet. * * Loads remote network statuses if they have expired. * * @param userWalletId The unique identifier of the user wallet. - * @param networks A set of network IDs which statuses are to be retrieved. + * @param networks A set of network which statuses are to be retrieved. * @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks. */ - fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set): Flow> + fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set): Flow> /** * Retrieves network statuses of specified blockchain networks for a specific user wallet. @@ -35,13 +26,13 @@ interface NetworksRepository { * Loads remote network statuses if they have expired or if [refresh] is `true`. * * @param userWalletId The unique identifier of the user wallet. - * @param networks A set of network IDs which statuses are to be retrieved. + * @param networks A set of network which statuses are to be retrieved. * @param refresh A boolean flag indicating whether the data should be refreshed. * @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks. */ suspend fun getNetworkStatusesSync( userWalletId: UserWalletId, - networks: Set, + networks: Set, refresh: Boolean, ): Set } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt index 41b1dda67b..a1f9e62bf9 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt @@ -164,6 +164,6 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest { isSortedByBalance = flowOf(), ), quotesRepository = MockQuotesRepository(quotes), - networksRepository = MockNetworksRepository(MockNetworks.networks.right(), statuses), + networksRepository = MockNetworksRepository(statuses), ) } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index b3a5e61a2e..0affccbec6 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -314,6 +314,6 @@ internal class GetTokenListUseCaseTest { isSortedByBalance = isSortedByBalance, ), quotesRepository = MockQuotesRepository(quotes), - networksRepository = MockNetworksRepository(MockNetworks.networks.right(), statuses), + networksRepository = MockNetworksRepository(statuses), ) } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt index 831d0c308b..f29e8b3a74 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt @@ -17,6 +17,7 @@ internal object MockNetworks { name = "Network One", isTestnet = false, standardType = Network.StandardType.ERC20, + derivationPath = Network.DerivationPath.None, ) val network2 = Network( @@ -24,6 +25,7 @@ internal object MockNetworks { name = "Network Two", isTestnet = false, standardType = Network.StandardType.ERC20, + derivationPath = Network.DerivationPath.None, ) val network3 = Network( @@ -31,22 +33,21 @@ internal object MockNetworks { name = "Network Three", isTestnet = false, standardType = Network.StandardType.ERC20, + derivationPath = Network.DerivationPath.None, ) - val networks = nonEmptySetOf(network1, network2, network3) - val networkStatus1 = NetworkStatus( - networkId = network1.id, + network = network1, value = NetworkStatus.Unreachable, ) val networkStatus2 = NetworkStatus( - networkId = network2.id, + network = network2, value = NetworkStatus.MissedDerivation, ) val networkStatus3 = NetworkStatus( - networkId = network3.id, + network = network3, value = NetworkStatus.NoAccount( amountToCreateAccount = amountToCreateAccount, address = NetworkAddress.Single(defaultAddress = "mock"), diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt index fda72a1b06..7a045b5662 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt @@ -7,17 +7,25 @@ internal object MockTokens { val token1 get() = CryptoCurrency.Coin( - id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token1")), + id = ID( + ID.Prefix.COIN_PREFIX, + ID.Body.NetworkId(MockNetworks.network1.id.value), + ID.Suffix.RawID("token1"), + ), network = MockNetworks.network1, name = "Token 1", symbol = "T1", decimals = 8, iconUrl = null, - derivationPath = null, + isCustom = false, ) val token2 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token2")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network1.id.value), + ID.Suffix.RawID("token2"), + ), network = MockNetworks.network1, name = "Token 2", symbol = "T2", @@ -25,11 +33,14 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val token3 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token3")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network1.id.value), + ID.Suffix.RawID("token3"), + ), network = MockNetworks.network1, name = "Token 3", symbol = "T3", @@ -37,21 +48,28 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val token4 get() = CryptoCurrency.Coin( - id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token4")), + id = ID( + ID.Prefix.COIN_PREFIX, + ID.Body.NetworkId(MockNetworks.network2.id.value), + ID.Suffix.RawID("token4"), + ), network = MockNetworks.network2, name = "Token 4", symbol = "T4", decimals = 8, iconUrl = null, - derivationPath = null, + isCustom = false, ) val token5 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token5")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network2.id.value), + ID.Suffix.RawID("token5"), + ), network = MockNetworks.network2, name = "Token 5", symbol = "T5", @@ -59,11 +77,14 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val token6 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token6")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network2.id.value), + ID.Suffix.RawID("token6"), + ), network = MockNetworks.network2, name = "Token 6", symbol = "T6", @@ -71,21 +92,28 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val token7 get() = CryptoCurrency.Coin( - id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token7")), + id = ID( + ID.Prefix.COIN_PREFIX, + ID.Body.NetworkId(MockNetworks.network3.id.value), + ID.Suffix.RawID("token7"), + ), network = MockNetworks.network3, name = "Token 7", symbol = "T7", decimals = 8, iconUrl = null, - derivationPath = null, + isCustom = false, ) val token8 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token8")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network3.id.value), + ID.Suffix.RawID("token8"), + ), network = MockNetworks.network3, name = "Token 8", symbol = "T8", @@ -93,11 +121,14 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val token9 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token9")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network3.id.value), + ID.Suffix.RawID("token9"), + ), network = MockNetworks.network3, name = "Token 9", symbol = "T9", @@ -105,11 +136,14 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val token10 get() = CryptoCurrency.Token( - id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token10")), + id = ID( + ID.Prefix.TOKEN_PREFIX, + ID.Body.NetworkId(MockNetworks.network3.id.value), + ID.Suffix.RawID("token10"), + ), network = MockNetworks.network3, name = "Token 10", symbol = "T10", @@ -117,7 +151,6 @@ internal object MockTokens { decimals = 8, iconUrl = null, contractAddress = "address", - derivationPath = null, ) val tokens = listOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt index d01254b1e7..4dd52cf2a3 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt @@ -72,7 +72,7 @@ internal object MockTokensStates { val loadedTokensStates = failedTokenStates.map { status -> val networkStatus = MockNetworks.verifiedNetworksStatuses - .first { it.networkId == status.currency.network.id } + .first { it.network == status.currency.network } val amount = (networkStatus.value as NetworkStatus.Verified).amounts[status.currency.id]!! val quote = MockQuotes.quotes.first { it.rawCurrencyId == status.currency.id.rawCurrencyId } val fiatAmount = amount * quote.fiatRate @@ -98,7 +98,7 @@ internal object MockTokensStates { hasCurrentNetworkTransactions = false, networkAddress = requireNotNull( value = MockNetworks.verifiedNetworksStatuses - .first { it.networkId == status.currency.network.id } + .first { it.network == status.currency.network } .value as? NetworkStatus.Verified, ).address, ), diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt index 0209a5e953..6d5026250c 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt @@ -11,24 +11,19 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map internal class MockNetworksRepository( - private val networks: Either>, private val statuses: Flow>>, ) : NetworksRepository { - override fun getNetworks(networksIds: Set): Set { - return networks.getOrElse { throw it } - } - override fun getNetworkStatusesUpdates( userWalletId: UserWalletId, - networks: Set, + networks: Set, ): Flow> { return statuses.map { it.getOrElse { e -> throw e } } } override suspend fun getNetworkStatusesSync( userWalletId: UserWalletId, - networks: Set, + networks: Set, refresh: Boolean, ): Set { return getNetworkStatusesUpdates(userWalletId, networks).first() diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt index 8ff4c05cff..b912b2111e 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt @@ -2,20 +2,16 @@ package com.tangem.domain.txhistory.repository import androidx.paging.PagingData import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError -import com.tangem.domain.txhistory.models.TxHistoryItem import kotlinx.coroutines.flow.Flow interface TxHistoryRepository { @Throws(TxHistoryStateError::class) - suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int + suspend fun getTxHistoryItemsCount(network: Network): Int @Throws(TxHistoryListError::class) - fun getTxHistoryItems( - networkId: Network.ID, - derivationPath: String?, - pageSize: Int, - ): Flow> + fun getTxHistoryItems(network: Network, pageSize: Int): Flow> } \ No newline at end of file diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt index 172c8c2b3f..df9a8013cc 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt @@ -9,10 +9,10 @@ import com.tangem.domain.txhistory.repository.TxHistoryRepository class GetTxHistoryItemsCountUseCase(private val repository: TxHistoryRepository) { - suspend operator fun invoke(networkId: Network.ID, derivationPath: String?): Either { + suspend operator fun invoke(network: Network): Either { return either { catch( - block = { repository.getTxHistoryItemsCount(networkId, derivationPath) }, + block = { repository.getTxHistoryItemsCount(network) }, catch = { throwable -> raise( when (throwable) { diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt index 38fbf4b87e..77ef74e98a 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt @@ -4,8 +4,8 @@ import androidx.paging.PagingData import arrow.core.Either import arrow.core.raise.either import com.tangem.domain.tokens.models.Network -import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.repository.TxHistoryRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch @@ -15,13 +15,12 @@ private const val DEFAULT_PAGE_SIZE = 20 class GetTxHistoryItemsUseCase(private val repository: TxHistoryRepository) { operator fun invoke( - networkId: Network.ID, - derivationPath: String?, + network: Network, pageSize: Int = DEFAULT_PAGE_SIZE, ): Either>> { return either { repository - .getTxHistoryItems(networkId = networkId, derivationPath = derivationPath, pageSize = pageSize) + .getTxHistoryItems(network = network, pageSize = pageSize) .catch { raise(TxHistoryListError.DataError(it)) } } } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExploreUrlUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExploreUrlUseCase.kt index 564192c4f8..f255563e7f 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExploreUrlUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExploreUrlUseCase.kt @@ -1,12 +1,17 @@ package com.tangem.domain.wallets.usecase +import arrow.core.raise.catch import com.tangem.domain.tokens.models.Network import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId +// TODO: Add tests class GetExploreUrlUseCase(private val walletsManagersFacade: WalletManagersFacade) { - suspend operator fun invoke(userWalletId: UserWalletId, networkId: Network.ID): String { - return walletsManagersFacade.getExploreUrl(userWalletId, networkId) + // FIXME: Handle error + suspend operator fun invoke(userWalletId: UserWalletId, network: Network): String { + return catch({ walletsManagersFacade.getExploreUrl(userWalletId, network) }) { + "" + } } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 1d09bb6fae..2199b485c3 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -65,6 +65,7 @@ internal class TokenDetailsLoadedBalanceConverter( is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.NoAccount, is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.NoAmount, // TODO: [REDACTED_JIRA] is CryptoCurrencyStatus.Unreachable, -> { @@ -89,6 +90,7 @@ internal class TokenDetailsLoadedBalanceConverter( is CryptoCurrencyStatus.Custom, is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.NoAccount, + is CryptoCurrencyStatus.NoAmount, is CryptoCurrencyStatus.Unreachable, -> MarketPriceBlockState.Error(currencyName) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 0af0c850bd..599c54b7c1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -118,8 +118,7 @@ internal class TokenDetailsViewModel @Inject constructor( private fun updateTxHistory(refresh: Boolean = false) { viewModelScope.launch(dispatchers.io) { val txHistoryItemsCountEither = txHistoryItemsCountUseCase( - networkId = cryptoCurrency.network.id, - derivationPath = cryptoCurrency.derivationPath, + network = cryptoCurrency.network, ) if (!refresh) { @@ -129,8 +128,7 @@ internal class TokenDetailsViewModel @Inject constructor( txHistoryItemsCountEither.onRight { uiState = stateFactory.getLoadedTxHistoryState( txHistoryEither = txHistoryItemsUseCase( - networkId = cryptoCurrency.network.id, - derivationPath = cryptoCurrency.derivationPath, + network = cryptoCurrency.network, ).map { it.cachedIn(viewModelScope) }, @@ -254,7 +252,7 @@ internal class TokenDetailsViewModel @Inject constructor( router.openUrl( url = getExploreUrlUseCase( userWalletId = wallet.walletId, - networkId = cryptoCurrency.network.id, + network = cryptoCurrency.network, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 307db0c04c..91cc615b96 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -206,7 +206,7 @@ internal object WalletPreviewData { val networkNumber = index + 1 val group = DraggableItem.GroupHeader( - id = "group_$networkNumber", + id = networkNumber, networkName = "$networkNumber", roundingMode = when (index) { 0 -> DraggableItem.RoundingMode.Top() @@ -328,7 +328,7 @@ internal object WalletPreviewData { walletsListConfig = walletListConfig, tokensListState = WalletTokensListState.Content( persistentListOf( - TokensListItemState.NetworkGroupTitle(TextReference.Str("Bitcoin")), + TokensListItemState.NetworkGroupTitle(id = 0, stringReference("Bitcoin")), TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_1", @@ -357,7 +357,7 @@ internal object WalletPreviewData { amount = "1,89340821 ETH", ), ), - TokensListItemState.NetworkGroupTitle(TextReference.Str("Ethereum")), + TokensListItemState.NetworkGroupTitle(id = 1, stringReference("Ethereum")), TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_5", diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt index 3424a965b6..f7c411a377 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt @@ -13,7 +13,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem * */ @Immutable internal sealed class DraggableItem { - abstract val id: String + abstract val id: Any abstract val roundingMode: RoundingMode abstract val showShadow: Boolean @@ -26,7 +26,7 @@ internal sealed class DraggableItem { * @property showShadow if true then item should be elevated * */ data class GroupHeader( - override val id: String, + override val id: Int, val networkName: String, override val roundingMode: RoundingMode = RoundingMode.None, override val showShadow: Boolean = false, @@ -43,7 +43,7 @@ internal sealed class DraggableItem { * */ data class Token( val tokenItemState: TokenItemState.Draggable, - val groupId: String, + val groupId: Int, override val showShadow: Boolean = false, override val roundingMode: RoundingMode = RoundingMode.None, ) : DraggableItem() { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt index e28e32c3a8..5cf4e81e8e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt @@ -5,4 +5,4 @@ import com.tangem.domain.tokens.models.Network internal fun getTokenItemId(currencyId: CryptoCurrency.ID): String = currencyId.value -internal fun getGroupHeaderId(networkId: Network.ID): String = networkId.value \ No newline at end of file +internal fun getGroupHeaderId(network: Network): Int = network.hashCode() \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt index 8dad69b926..a6c36995b0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -36,7 +36,7 @@ internal class CryptoCurrencyToDraggableItemConverter( ): DraggableItem.Token { return DraggableItem.Token( tokenItemState = createTokenItemState(currencyStatus, appCurrency), - groupId = getGroupHeaderId(currencyStatus.currency.network.id), + groupId = getGroupHeaderId(currencyStatus.currency.network), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt index e57076e83f..0205df476a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt @@ -31,7 +31,7 @@ internal class NetworkGroupToDraggableItemsConverter( } private fun createGroupHeader(group: NetworkGroup) = DraggableItem.GroupHeader( - id = getGroupHeaderId(group.network.id), + id = getGroupHeaderId(group.network), networkName = group.network.name, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/dnd/DraggableGroupsOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/dnd/DraggableGroupsOperations.kt index 3018d416a5..3891fdea66 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/dnd/DraggableGroupsOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/dnd/DraggableGroupsOperations.kt @@ -7,7 +7,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteI internal class DraggableGroupsOperations { - private var groupIdToTokens: Map>? = null + private var groupIdToTokens: Map>? = null fun collapseGroup(items: List, movingGroup: DraggableItem.GroupHeader): List { if (!groupIdToTokens.isNullOrEmpty()) return items diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index 0cb0188b10..b81f513cc0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -53,7 +53,7 @@ internal sealed class WalletTokensListState { /** Locked content state */ object Locked : ContentState( items = persistentListOf( - TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), + TokensListItemState.NetworkGroupTitle(id = 42, name = TextReference.Res(id = R.string.main_tokens)), TokensListItemState.Token(state = TokenItemState.Locked(id = LOCKED_TOKEN_ID)), ), organizeTokensButton = OrganizeTokensButtonState.Hidden, @@ -84,19 +84,26 @@ internal sealed class WalletTokensListState { @Immutable sealed interface TokensListItemState { + val id: Any + /** * Network group title item * - * @property value network name + * @property name network name */ - data class NetworkGroupTitle(val value: TextReference) : TokensListItemState + data class NetworkGroupTitle( + override val id: Int, + val name: TextReference, + ) : TokensListItemState /** * Token item * * @property state token item state */ - data class Token(val state: TokenItemState) : TokensListItemState + data class Token(val state: TokenItemState) : TokensListItemState { + override val id: String = state.id + } } private companion object { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index b67b7cedb8..6028b4aff8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -71,6 +71,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.NoAccount, is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, -> MarketPriceBlockState.Error(currencyName) } } @@ -112,6 +113,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( is CryptoCurrencyStatus.NoAccount, is CryptoCurrencyStatus.Custom, is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, -> { WalletCardState.Error( id = selectedWallet.id, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 350cb95ecf..1330515bf4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -44,12 +44,7 @@ private fun LazyListScope.contentItems( ) { itemsIndexed( items = items, - key = { _, item -> - when (item) { - is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> item.value.hashCode() - is WalletTokensListState.TokensListItemState.Token -> item.state.id - } - }, + key = { _, item -> item.id }, contentType = { _, item -> item::class.java }, itemContent = { index, item -> MultiCurrencyContentItem( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt index a9ac43d4ab..882548c609 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt @@ -19,7 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletToke internal fun MultiCurrencyContentItem(state: WalletTokensListState.TokensListItemState, modifier: Modifier = Modifier) { when (state) { is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> { - NetworkGroupItem(networkName = state.value.resolveReference(), modifier = modifier) + NetworkGroupItem(networkName = state.name.resolveReference(), modifier = modifier) } is WalletTokensListState.TokensListItemState.Token -> { TokenItem(state = state.state, modifier = modifier) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index 1e073b5ffd..ebd6ad5e7e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -30,6 +30,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter( is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.NoAccount, is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, -> value.mapToUnreachableTokenItemState() } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index cb11331df1..8fb50ac24e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.common.Provider -import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup @@ -68,7 +68,12 @@ internal class TokenListToContentItemsConverter( } private fun MutableList.addGroup(group: NetworkGroup): List { - this.add(TokensListItemState.NetworkGroupTitle(TextReference.Str(group.network.name))) + val groupTitle = TokensListItemState.NetworkGroupTitle( + id = group.network.hashCode(), + name = stringReference(group.network.name), + ) + + this.add(groupTitle) group.currencies.forEach { token -> this.addToken(token) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 6efc2b0461..1c8e0dd7f9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -3,8 +3,6 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels import androidx.lifecycle.* import androidx.paging.cachedIn import arrow.core.getOrElse -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess @@ -15,7 +13,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.* import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.CanUseBiometryUseCase @@ -461,14 +458,18 @@ internal class WalletViewModel @Inject constructor( val wallet = getWallet( index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, ) - router.openTxHistoryWebsite( - url = getExploreUrlUseCase( - userWalletId = wallet.walletId, - networkId = Network.ID( - value = wallet.scanResponse.cardTypesResolver.getBlockchain().id, + val currencyStatus = getPrimaryCurrencyStatusUpdatesUseCase(wallet.walletId) + .firstOrNull() + ?.getOrNull() + + if (currencyStatus != null) { + router.openTxHistoryWebsite( + url = getExploreUrlUseCase( + userWalletId = wallet.walletId, + network = currencyStatus.currency.network, ), - ), - ) + ) + } } } @@ -582,31 +583,20 @@ internal class WalletViewModel @Inject constructor( private fun getSingleCurrencyContent(index: Int) { val wallet = getWallet(index) - val blockchain = getCardTypeResolver(index).getBlockchain() - updateTxHistory( - blockchain = blockchain, - derivationStyle = wallet.scanResponse.derivationStyleProvider.getDerivationStyle(), - ) - updateMarketPrice(userWalletId = wallet.walletId) + updatePrimaryCurrencyStatus(userWalletId = wallet.walletId) updateNotifications(index) } - private fun updateTxHistory(blockchain: Blockchain, derivationStyle: DerivationStyle?) { + private fun updateTxHistory(network: Network) { viewModelScope.launch(dispatchers.io) { - val derivationPath = blockchain.derivationPath(style = derivationStyle)?.rawPath - - val txHistoryItemsCountEither = txHistoryItemsCountUseCase( - networkId = Network.ID(blockchain.id), - derivationPath = derivationPath, - ) + val txHistoryItemsCountEither = txHistoryItemsCountUseCase(network) uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither) txHistoryItemsCountEither.onRight { uiState = stateFactory.getLoadedTxHistoryState( txHistoryEither = txHistoryItemsUseCase( - networkId = Network.ID(blockchain.id), - derivationPath = derivationPath, + network, ).map { it.cachedIn(viewModelScope) }, @@ -615,8 +605,7 @@ internal class WalletViewModel @Inject constructor( } } - // It also update wallet balance - private fun updateMarketPrice(userWalletId: UserWalletId) { + private fun updatePrimaryCurrencyStatus(userWalletId: UserWalletId) { getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId) .distinctUntilChanged() .onEach { maybeCryptoCurrencyStatus -> @@ -625,6 +614,7 @@ internal class WalletViewModel @Inject constructor( maybeCryptoCurrencyStatus.onRight { status -> singleWalletCryptoCurrencyStatus = status updateButtons(userWalletId = userWalletId, currency = status.currency) + updateTxHistory(status.currency.network) } } .flowOn(dispatchers.io)