diff --git a/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt b/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt index 6af667ae08..b818478a45 100644 --- a/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt +++ b/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt @@ -1,8 +1,14 @@ package com.tangem.utils.converter -interface Converter { +interface Converter { + fun convert(value: I): O - fun convertList(input: List): List { - return input.map { convert(it) } + + fun convertList(input: Collection): List { + return input.map(::convert) + } + + fun convertSet(input: Collection): Set { + return input.mapTo(hashSetOf(), ::convert) } } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt b/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt index 444e4048ab..50da29ecd0 100644 --- a/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt +++ b/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt @@ -1,8 +1,10 @@ package com.tangem.utils.converter -interface TwoWayConverter : Converter { +interface TwoWayConverter : Converter { + fun convertBack(value: O): I - fun convertListBack(input: List): List { + + fun convertListBack(input: Collection): List { return input.map { convertBack(it) } } } \ No newline at end of file diff --git a/data/tokens/build.gradle.kts b/data/tokens/build.gradle.kts index 5b3c4c0e7a..c908cd02dd 100644 --- a/data/tokens/build.gradle.kts +++ b/data/tokens/build.gradle.kts @@ -25,7 +25,6 @@ dependencies { /** Project - Utils */ implementation(projects.core.utils) - // FIXME: For blockchain extensions, remove after refactoring implementation(projects.domain.legacy) /** Tangem SDKs */ diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index 2b512574c4..25f7f9defd 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -1,8 +1,8 @@ package com.tangem.data.tokens.di import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.repository.DefaultNetworksRepository import com.tangem.data.tokens.repository.DefaultTokensRepository -import com.tangem.data.tokens.repository.MockNetworksRepository import com.tangem.data.tokens.repository.MockQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.token.UserTokensStore @@ -10,6 +10,7 @@ import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.repository.TokensRepository +import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -39,5 +40,19 @@ internal object TokensDataModule { @Provides @Singleton - fun provideNetworksRepository(): NetworksRepository = MockNetworksRepository() + fun provideNetworksRepository( + walletManagersFacade: WalletManagersFacade, + userWalletsStore: UserWalletsStore, + userTokensStore: UserTokensStore, + cacheRegistry: CacheRegistry, + dispatchers: CoroutineDispatcherProvider, + ): NetworksRepository { + return DefaultNetworksRepository( + walletManagersFacade, + userWalletsStore, + userTokensStore, + cacheRegistry, + dispatchers, + ) + } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt deleted file mode 100644 index 8278cce392..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.tangem.data.tokens.mock - -import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.models.Network -import java.math.BigDecimal - -@Suppress("MemberVisibilityCanBePrivate") -internal object MockNetworks { - - val network1 = Network( - id = Network.ID("network1"), - name = "Network One", - ) - - val network2 = Network( - id = Network.ID("network2"), - name = "Network Two", - ) - - val network3 = Network( - id = Network.ID("network3"), - name = "Network Three", - ) - - val networks = setOf(network1, network2, network3) - - val networkStatus1 = NetworkStatus( - networkId = network1.id, - value = NetworkStatus.Verified( - amounts = mapOf( - MockTokens.token1.id to BigDecimal("123.1234556789"), - MockTokens.token2.id to BigDecimal("42.2"), - MockTokens.token3.id to BigDecimal("1000000000.5"), - ), - hasTransactionsInProgress = false, - ), - ) - - val networkStatus2 = NetworkStatus( - networkId = network2.id, - value = NetworkStatus.MissedDerivation, - ) - - val networkStatus3 = NetworkStatus( - networkId = network3.id, - value = NetworkStatus.Verified( - amounts = mapOf( - MockTokens.token7.id to BigDecimal.ZERO, - MockTokens.token8.id to BigDecimal.TEN, - MockTokens.token9.id to BigDecimal.TEN, - MockTokens.token10.id to BigDecimal.TEN, - ), - hasTransactionsInProgress = false, - ), - ) - - val networksStatuses = setOf(networkStatus1, networkStatus2, networkStatus3) -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt deleted file mode 100644 index f89c208a52..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.tangem.data.tokens.mock - -import com.tangem.domain.tokens.model.Quote -import java.math.BigDecimal - -@Suppress("MemberVisibilityCanBePrivate") -internal object MockQuotes { - - val quote1 = Quote( - currencyId = MockTokens.token1.id, - fiatRate = BigDecimal("1.23"), - priceChange = BigDecimal("0.01"), - ) - - val quote2 = Quote( - currencyId = MockTokens.token2.id, - fiatRate = BigDecimal("2.34"), - priceChange = BigDecimal("-0.02"), - ) - - val quote3 = Quote( - currencyId = MockTokens.token3.id, - fiatRate = BigDecimal("3.45"), - priceChange = BigDecimal("0.03"), - ) - - val quote4 = Quote( - currencyId = MockTokens.token4.id, - fiatRate = BigDecimal("4.56"), - priceChange = BigDecimal("-0.04"), - ) - - val quote5 = Quote( - currencyId = MockTokens.token5.id, - fiatRate = BigDecimal("5.67"), - priceChange = BigDecimal("0.05"), - ) - - val quote6 = Quote( - currencyId = MockTokens.token6.id, - fiatRate = BigDecimal("6.78"), - priceChange = BigDecimal("-0.06"), - ) - - val quote7 = Quote( - currencyId = MockTokens.token7.id, - fiatRate = BigDecimal("7.89"), - priceChange = BigDecimal("0.07"), - ) - - val quote8 = Quote( - currencyId = MockTokens.token8.id, - fiatRate = BigDecimal("8.90"), - priceChange = BigDecimal("-0.08"), - ) - - val quote9 = Quote( - currencyId = MockTokens.token9.id, - fiatRate = BigDecimal("9.01"), - priceChange = BigDecimal("0.09"), - ) - - val quote10 = Quote( - currencyId = MockTokens.token10.id, - fiatRate = BigDecimal("10.12"), - priceChange = BigDecimal("-0.10"), - ) - - val quotes = setOf(quote1, quote2, quote3, quote4, quote5, quote6, quote7, quote8, quote9, quote10) -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt deleted file mode 100644 index e18e4c126d..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.tangem.data.tokens.mock - -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWalletId - -internal object MockTokens { - - val token1 - get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token1"), - networkId = MockNetworks.network1.id, - name = "Token 1", - symbol = "T1", - decimals = 8, - iconUrl = null, - derivationPath = null, - ) - val token2 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token2"), - networkId = MockNetworks.network1.id, - name = "Token 2", - symbol = "T2", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token3 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token3"), - networkId = MockNetworks.network1.id, - name = "Token 3", - symbol = "T3", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token4 - get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token4"), - networkId = MockNetworks.network2.id, - name = "Token 4", - symbol = "T4", - decimals = 8, - iconUrl = null, - derivationPath = null, - ) - val token5 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token5"), - networkId = MockNetworks.network2.id, - name = "Token 5", - symbol = "T5", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token6 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token6"), - networkId = MockNetworks.network2.id, - name = "Token 6", - symbol = "T6", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token7 - get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token7"), - networkId = MockNetworks.network3.id, - name = "Token 7", - symbol = "T7", - decimals = 8, - iconUrl = null, - derivationPath = null, - ) - val token8 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token8"), - networkId = MockNetworks.network3.id, - name = "Token 8", - symbol = "T8", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token9 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token9"), - networkId = MockNetworks.network3.id, - name = "Token 9", - symbol = "T9", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token10 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token10"), - networkId = MockNetworks.network3.id, - name = "Token 10", - symbol = "T10", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - - val tokens - get() = mapOf( - UserWalletId(stringValue = "123") to setOf( - token1, token2, token3, token4, token5, - token6, token7, token8, token9, token10, - ), - UserWalletId(stringValue = "321") to setOf(token1, token2, token3), - UserWalletId(stringValue = "42") to setOf(token7, token8, token9, token10), - UserWalletId(stringValue = "24") to setOf(token4, token5, token6), - ) -} \ No newline at end of file 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 new file mode 100644 index 0000000000..dbde700089 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -0,0 +1,106 @@ +package com.tangem.data.tokens.repository + +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.utils.NetworkConverter +import com.tangem.data.tokens.utils.NetworkStatusFactory +import com.tangem.data.tokens.utils.ResponseCurrenciesFactory +import com.tangem.datasource.local.token.UserTokensStore +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.demo.DemoConfig +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.addOrReplace +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch + +internal class DefaultNetworksRepository( + private val walletManagersFacade: WalletManagersFacade, + private val userWalletsStore: UserWalletsStore, + private val userTokensStore: UserTokensStore, + private val cacheRegistry: CacheRegistry, + private val dispatchers: CoroutineDispatcherProvider, +) : NetworksRepository { + + private val networkConverter by lazy { NetworkConverter() } + private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(DemoConfig()) } + private val networkStatusFactory by lazy { NetworkStatusFactory() } + + private val networksStatuses: MutableStateFlow> = MutableStateFlow(hashSetOf()) + + override fun getNetworks(networksIds: Set): Set { + return networkConverter.convertSet(networksIds) + } + + override fun getNetworkStatuses( + userWalletId: UserWalletId, + networks: Set, + refresh: Boolean, + ): Flow> = channelFlow { + networksStatuses.collectLatest(::send) + + launch(dispatchers.io) { + fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh) + } + } + + private suspend fun fetchNetworksStatusesIfCacheExpired( + userWalletId: UserWalletId, + networks: Set, + refresh: Boolean, + ) { + cacheRegistry.invokeOnExpire( + key = getNetworksStatusesCacheKey(userWalletId), + skipCache = refresh, + block = { fetchNetworksStatuses(userWalletId, networks) }, + ) + } + + private suspend fun fetchNetworksStatuses(userWalletId: UserWalletId, networks: Set) { + coroutineScope { + networks + .map { networkId -> + async { + fetchNetworkStatus(userWalletId, networkId) + } + } + .awaitAll() + } + } + + private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, networkId: Network.ID) { + val currencies = getCurrencies(userWalletId) + val result = walletManagersFacade.update( + userWalletId = userWalletId, + networkId = networkId, + extraTokens = currencies.filterIsInstanceTo(hashSetOf()), + ) + val networkStatus = networkStatusFactory.createNetworkStatus(networkId, result, currencies) + + networksStatuses.update { statuses -> + statuses.apply { + addOrReplace(networkStatus) { it.networkId == networkStatus.networkId } + } + } + } + + private suspend fun getCurrencies(userWalletId: UserWalletId): Set { + val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "Unable to find user wallet with provided ID: $userWalletId" + } + val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { + "Unable to find tokens response for user wallet with provided ID: $userWalletId" + } + + return responseCurrenciesFactory.createTokens(response, userWallet.scanResponse.card) + } + + private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId): String = "network_status_$userWalletId" +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt deleted file mode 100644 index 3455d66764..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.data.tokens.repository - -import com.tangem.data.tokens.mock.MockNetworks -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.models.Network -import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.wallets.models.UserWalletId -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -internal class MockNetworksRepository : NetworksRepository { - - override fun getNetworks(networksIds: Set): Set { - return MockNetworks.networks - .filter { it.id in networksIds } - .toSet() - } - - override fun getNetworkStatuses( - userWalletId: UserWalletId, - networks: Map>, - refresh: Boolean, - ): Flow> { - return flowOf( - MockNetworks.networksStatuses - .filter { it.networkId in networks.keys } - .toSet(), - ) - } -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt index 345f17e6bc..841ce3207f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt @@ -1,19 +1,23 @@ package com.tangem.data.tokens.repository -import com.tangem.data.tokens.mock.MockQuotes import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.repository.QuotesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +import java.math.BigDecimal internal class MockQuotesRepository : QuotesRepository { - override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { return flowOf( - MockQuotes.quotes - .filter { it.currencyId in tokensIds } - .toSet(), + currenciesIds.map { + Quote( + currencyId = it, + fiatRate = BigDecimal.ZERO, + priceChange = BigDecimal.ZERO, + ) + }.toSet(), ) } } \ 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 new file mode 100644 index 0000000000..23e27fe466 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt @@ -0,0 +1,31 @@ +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 +import timber.log.Timber + +internal class NetworkConverter : Converter { + + override fun convert(value: Network.ID): Network? { + val blockchain = Blockchain.fromId(value.value) + + if (blockchain == Blockchain.Unknown) { + Timber.e("Unable to convert Unknown blockchain to the domain network model") + return null + } + + return Network( + id = value, + name = blockchain.fullName, + ) + } + + 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/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt new file mode 100644 index 0000000000..d27da42e10 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -0,0 +1,57 @@ +package com.tangem.data.tokens.utils + +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount +import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult +import timber.log.Timber +import java.math.BigDecimal + +internal class NetworkStatusFactory { + + fun createNetworkStatus( + networkId: Network.ID, + result: UpdateWalletManagerResult, + currencies: Set, + ): NetworkStatus { + return NetworkStatus( + networkId = networkId, + value = when (result) { + is UpdateWalletManagerResult.MissedDerivation -> NetworkStatus.MissedDerivation + is UpdateWalletManagerResult.Unreachable -> NetworkStatus.Unreachable + is UpdateWalletManagerResult.NoAccount -> NetworkStatus.NoAccount(result.amountToCreateAccount) + is UpdateWalletManagerResult.Verified -> NetworkStatus.Verified( + amounts = formatAmounts(result.tokensAmounts, currencies), + hasTransactionsInProgress = result.hasTransactionsInProgress, + ) + }, + ) + } + + private fun formatAmounts( + amounts: Set, + currencies: Set, + ): Map { + val formattedAmounts = hashMapOf() + + currencies.forEach { currency -> + val amount = when (currency) { + is CryptoCurrency.Coin -> amounts.singleOrNull { it is CryptoCurrencyAmount.Coin } + is CryptoCurrency.Token -> amounts.singleOrNull { + it is CryptoCurrencyAmount.Token && + it.id == getTokenIdString(currency) && + it.tokenContractAddress == currency.contractAddress + } + }?.value + + if (amount == null) { + Timber.e("Unable to find a token amount for: ${currency.name}") + } else { + formattedAmounts[currency.id] = amount + } + } + + return formattedAmounts + } +} \ 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 2d0b915285..1ff2268f53 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 @@ -49,7 +49,7 @@ internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency return getTokenOrCoinId(blockchain, token) } -internal fun getResponseTokenId(currency: CryptoCurrency): String? { +internal fun getTokenIdString(currency: CryptoCurrency): String? { return currency.id.value.substringAfter(TOKEN_ID_DELIMITER) .takeUnless { currency is CryptoCurrency.Token && currency.isCustom } } 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 ffb4fc9c10..898c62c8b3 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 @@ -30,7 +30,7 @@ internal class UserTokensResponseFactory { val blockchain = getBlockchain(currency.networkId) return UserTokensResponse.Token( - id = getResponseTokenId(currency), + id = getTokenIdString(currency), networkId = blockchain.toNetworkId(), derivationPath = currency.derivationPath, name = currency.name, 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 09bc5ac1dd..1b942ab4ce 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 @@ -86,7 +86,7 @@ class DefaultWalletManagersFacade( return try { if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) { - updateDemoWalletManager(walletManager, extraTokens) + updateDemoWalletManager(walletManager) } else { updateWalletManager(walletManager) } @@ -95,14 +95,11 @@ class DefaultWalletManagersFacade( } } - private fun updateDemoWalletManager( - walletManager: WalletManager, - tokens: Set, - ): UpdateWalletManagerResult { + private fun updateDemoWalletManager(walletManager: WalletManager): UpdateWalletManagerResult { val amount = demoConfig.getBalance(walletManager.wallet.blockchain) walletManager.wallet.setAmount(amount) - return resultFactory.getDemoResult(amount, tokens) + return resultFactory.getDemoResult(walletManager, amount) } private suspend fun updateWalletManager(walletManager: WalletManager): UpdateWalletManagerResult { @@ -149,7 +146,7 @@ class DefaultWalletManagersFacade( if (tokens.isEmpty()) return val tokensToAdd = sdkTokenConverter - .convertList(tokens.toList()) + .convertList(tokens) .filter { it !in walletManager.cardTokens } walletManager.addTokens(tokensToAdd) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt index 192be587e5..ae72f37ec4 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt @@ -9,6 +9,7 @@ sealed class CryptoCurrencyAmount { data class Coin(override val value: BigDecimal) : CryptoCurrencyAmount() data class Token( + val id: String?, val tokenContractAddress: String, override val value: BigDecimal, ) : CryptoCurrencyAmount() diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt index bdf864f4fe..df5737de85 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt @@ -1,11 +1,7 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.TransactionStatus -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.domain.common.extensions.amountToCreateAccount -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult import timber.log.Timber @@ -26,9 +22,9 @@ internal class UpdateWalletManagerResultFactory { ) } - fun getDemoResult(demoAmount: Amount, tokens: Set): UpdateWalletManagerResult.Verified { + fun getDemoResult(walletManager: WalletManager, demoAmount: Amount): UpdateWalletManagerResult.Verified { return UpdateWalletManagerResult.Verified( - tokensAmounts = getDemoTokensAmounts(demoAmount, tokens), + tokensAmounts = getDemoTokensAmounts(demoAmount, walletManager.cardTokens), hasTransactionsInProgress = false, ) } @@ -53,18 +49,19 @@ internal class UpdateWalletManagerResultFactory { return amounts.mapNotNullTo(mutableAmounts, ::getTokenAmount) } - private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set): Set { + private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set): Set { val amountValue = demoAmount.value ?: BigDecimal.ZERO val demoAmounts = hashSetOf(CryptoCurrencyAmount.Coin(amountValue)) return tokens.mapTo(demoAmounts) { token -> - CryptoCurrencyAmount.Token(token.contractAddress, amountValue) + CryptoCurrencyAmount.Token(token.id, token.contractAddress, amountValue) } } private fun getTokenAmount(amount: Amount): CryptoCurrencyAmount? { return when (val type = amount.type) { is AmountType.Token -> CryptoCurrencyAmount.Token( + id = type.token.id, tokenContractAddress = type.token.contractAddress, value = getAmountValue(amount) ?: return null, ) 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 6ce5fc8b60..7b49295f4a 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 @@ -46,36 +46,43 @@ internal class CurrenciesStatusesOperations( fun getMultiCurrencyWalletStatusesFlow(): Flow> { return getMultiCurrencyWalletCurrencies().flatMapConcat { - val tokens = it.toNonEmptySetOrNull() + val currencies = it.toNonEmptySetOrNull() - if (tokens == null) { + if (currencies == null) { flowOf(emptySet()) } else { - val tokensIds = tokens.map { token -> token.id }.toNonEmptySet() - val groupedTokens = groupTokens(tokens) + val currencyIdToNetworkId = currencies.associate { currency -> + currency.id to currency.networkId + } + val currenciesIds = requireNotNull(currencyIdToNetworkId.keys.toNonEmptySetOrNull()) { + "Currencies IDs cannot be empty" + } + val networksIds = requireNotNull(currencyIdToNetworkId.values.toNonEmptySetOrNull()) { + "Networks IDs cannot be empty" + } - combine(getQuotes(tokensIds), getNetworksStatues(groupedTokens)) { quotes, networksStatuses -> - createTokensStatuses(tokens, quotes, networksStatuses) + combine(getQuotes(currenciesIds), getNetworksStatues(networksIds)) { quotes, networksStatuses -> + createTokensStatuses(currencies, quotes, networksStatuses) } } } } suspend fun getPrimaryCurrencyStatusFlow(): Flow { - val token = getPrimaryCurrency() + val currency = getPrimaryCurrency() - val quoteFlow = getQuotes(nonEmptySetOf(token.id)) + val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) .map { quotes -> - quotes.singleOrNull { it.currencyId == token.id } + quotes.singleOrNull { it.currencyId == currency.id } } - val statusFlow = getNetworksStatues(groupTokens(nonEmptySetOf(token))) + val statusFlow = getNetworksStatues(nonEmptySetOf(currency.networkId)) .map { statuses -> - statuses.singleOrNull { it.networkId == token.networkId } + statuses.singleOrNull { it.networkId == currency.networkId } } return combine(quoteFlow, statusFlow) { quote, networkStatus -> - createStatus(token, quote, networkStatus) + createStatus(currency, quote, networkStatus) } } @@ -132,30 +139,13 @@ internal class CurrenciesStatusesOperations( .flowOn(dispatchers.io) } - private fun getNetworksStatues( - groupedTokens: Map>, - ): Flow> { - return networksRepository.getNetworkStatuses(userWalletId, groupedTokens, refresh) + private fun getNetworksStatues(networks: NonEmptySet): Flow> { + return networksRepository.getNetworkStatuses(userWalletId, networks, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyNetworksStatuses) } .flowOn(dispatchers.io) } - private suspend fun groupTokens( - tokens: NonEmptySet, - ): Map> { - return withContext(dispatchers.default) { - tokens - .groupBy { it.networkId } - .mapValues { (_, tokens) -> - // Can not be empty - tokens.toNonEmptySetOrNull()!! - .map { it.id } - .toNonEmptySet() - } - } - } - sealed class Error { object EmptyCurrencies : Error() 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 5acf293b11..6dcec4843b 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 @@ -1,6 +1,5 @@ package com.tangem.domain.tokens.repository -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.models.Network import com.tangem.domain.wallets.models.UserWalletId @@ -23,13 +22,13 @@ interface NetworksRepository { * Retrieves the statuses of specified blockchain networks for a specific user wallet. * * @param userWalletId The unique identifier of the user wallet. - * @param networks A map of network IDs to sets of cryptocurrency IDs, representing the networks for which statuses are to be retrieved. + * @param networks A set of network IDs 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. */ fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Set, refresh: Boolean, ): Flow> } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt index 1c783ce342..c56cb3d7d9 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt @@ -12,9 +12,9 @@ interface QuotesRepository { /** * Retrieves the quotes for a set of specified cryptocurrencies, identified by their unique IDs. * - * @param tokensIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved. + * @param currenciesIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved. * @param refresh A boolean flag indicating whether the data should be refreshed. * @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies. */ - fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> + fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> } \ No newline at end of file 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 ced27621b7..5ac5c7c8fa 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 @@ -3,7 +3,6 @@ package com.tangem.domain.tokens.repository import arrow.core.Either import arrow.core.getOrElse import com.tangem.domain.core.error.DataError -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.models.Network import com.tangem.domain.wallets.models.UserWalletId @@ -21,7 +20,7 @@ internal class MockNetworksRepository( override fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Set, refresh: Boolean, ): Flow> { return statuses.map { it.getOrElse { e -> throw e } } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt index 9ec660b113..a94b8a72df 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt @@ -12,7 +12,7 @@ internal class MockQuotesRepository( private val quotes: Flow>>, ) : QuotesRepository { - override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { return quotes.map { it.getOrElse { e -> throw e } } } } \ No newline at end of file