From 81a16c8bb145717630e476b3a67c943e85021577 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Aug 2023 13:56:39 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 6 +- .../com/tangem/data/tokens/mock/MockQuotes.kt | 20 +- .../com/tangem/data/tokens/mock/MockTokens.kt | 48 ++-- .../repository/DefaultTokensRepository.kt | 34 +-- .../repository/MockNetworksRepository.kt | 4 +- .../tokens/repository/MockQuotesRepository.kt | 6 +- ...ensFactory.kt => CardCurrenciesFactory.kt} | 18 +- .../tokens/utils/ResponseCurrenciesFactory.kt | 77 ++++++ .../tokens/utils/ResponseTokensFactory.kt | 60 ----- .../data/tokens/utils/TokensOperations.kt | 18 +- .../tokens/utils/UserTokensResponseFactory.kt | 22 +- .../tokens/ApplyTokenListSortingUseCase.kt | 20 +- ...seCase.kt => GetPrimaryCurrencyUseCase.kt} | 22 +- .../domain/tokens/GetTokenListUseCase.kt | 14 +- .../tokens/ToggleTokenListGroupingUseCase.kt | 4 +- .../tokens/ToggleTokenListSortingUseCase.kt | 2 +- .../mapper/GetWalletTokenErrorMappers.kt | 12 +- .../error/mapper/TokenListErrorMappers.kt | 12 +- .../domain/tokens/model/CryptoCurrency.kt | 61 +++++ ...TokenStatus.kt => CryptoCurrencyStatus.kt} | 10 +- .../domain/tokens/model/NetworkGroup.kt | 2 +- .../domain/tokens/model/NetworkStatus.kt | 6 +- .../com/tangem/domain/tokens/model/Quote.kt | 2 +- .../com/tangem/domain/tokens/model/Token.kt | 31 --- .../tangem/domain/tokens/model/TokenList.kt | 2 +- ...ons.kt => CurrenciesStatusesOperations.kt} | 57 +++-- ...rations.kt => CurrencyStatusOperations.kt} | 40 ++-- .../TokenListFiatBalanceOperations.kt | 22 +- .../tokens/operations/TokenListOperations.kt | 24 +- .../operations/TokenListSortingOperations.kt | 30 +-- .../tokens/repository/NetworksRepository.kt | 4 +- .../tokens/repository/QuotesRepository.kt | 4 +- .../tokens/repository/TokensRepository.kt | 8 +- .../ApplyTokenListSortingUseCaseTest.kt | 4 +- .../domain/tokens/GetTokenListUseCaseTest.kt | 6 +- .../domain/tokens/mock/MockNetworksGroups.kt | 20 +- .../tangem/domain/tokens/mock/MockQuotes.kt | 20 +- .../domain/tokens/mock/MockTokenLists.kt | 18 +- .../tangem/domain/tokens/mock/MockTokens.kt | 226 +++++++++--------- .../domain/tokens/mock/MockTokensStates.kt | 135 +++-------- .../repository/MockNetworksRepository.kt | 4 +- .../tokens/repository/MockQuotesRepository.kt | 4 +- .../tokens/repository/MockTokensRepository.kt | 17 +- ...yptoCurrencyStatusToTokenItemConverter.kt} | 51 ++-- .../utils/TokenListToContentItemsConverter.kt | 10 +- .../WalletNotificationsListFactory.kt | 8 +- 46 files changed, 610 insertions(+), 615 deletions(-) rename data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/{CardTokensFactory.kt => CardCurrenciesFactory.kt} (84%) create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseTokensFactory.kt rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/{GetTokenUseCase.kt => GetPrimaryCurrencyUseCase.kt} (70%) create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/{TokenStatus.kt => CryptoCurrencyStatus.kt} (84%) delete mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Token.kt rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/{TokensStatusesOperations.kt => CurrenciesStatusesOperations.kt} (69%) rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/{TokenStatusOperations.kt => CurrencyStatusOperations.kt} (64%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/{TokenStatusToTokenItemConverter.kt => CryptoCurrencyStatusToTokenItemConverter.kt} (63%) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index ddf863762d..8fc08144df 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -28,13 +28,13 @@ internal object TokensDomainModule { @Provides @ViewModelScoped - fun provideGetTokenUseCase( + fun provideGetPrimaryCurrencyUseCase( tokensRepository: TokensRepository, quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, - ): GetTokenUseCase { - return GetTokenUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers) + ): GetPrimaryCurrencyUseCase { + return GetPrimaryCurrencyUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers) } @Provides 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 index 86f7a6d76c..f89c208a52 100644 --- 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 @@ -7,61 +7,61 @@ import java.math.BigDecimal internal object MockQuotes { val quote1 = Quote( - tokenId = MockTokens.token1.id, + currencyId = MockTokens.token1.id, fiatRate = BigDecimal("1.23"), priceChange = BigDecimal("0.01"), ) val quote2 = Quote( - tokenId = MockTokens.token2.id, + currencyId = MockTokens.token2.id, fiatRate = BigDecimal("2.34"), priceChange = BigDecimal("-0.02"), ) val quote3 = Quote( - tokenId = MockTokens.token3.id, + currencyId = MockTokens.token3.id, fiatRate = BigDecimal("3.45"), priceChange = BigDecimal("0.03"), ) val quote4 = Quote( - tokenId = MockTokens.token4.id, + currencyId = MockTokens.token4.id, fiatRate = BigDecimal("4.56"), priceChange = BigDecimal("-0.04"), ) val quote5 = Quote( - tokenId = MockTokens.token5.id, + currencyId = MockTokens.token5.id, fiatRate = BigDecimal("5.67"), priceChange = BigDecimal("0.05"), ) val quote6 = Quote( - tokenId = MockTokens.token6.id, + currencyId = MockTokens.token6.id, fiatRate = BigDecimal("6.78"), priceChange = BigDecimal("-0.06"), ) val quote7 = Quote( - tokenId = MockTokens.token7.id, + currencyId = MockTokens.token7.id, fiatRate = BigDecimal("7.89"), priceChange = BigDecimal("0.07"), ) val quote8 = Quote( - tokenId = MockTokens.token8.id, + currencyId = MockTokens.token8.id, fiatRate = BigDecimal("8.90"), priceChange = BigDecimal("-0.08"), ) val quote9 = Quote( - tokenId = MockTokens.token9.id, + currencyId = MockTokens.token9.id, fiatRate = BigDecimal("9.01"), priceChange = BigDecimal("0.09"), ) val quote10 = Quote( - tokenId = MockTokens.token10.id, + currencyId = MockTokens.token10.id, fiatRate = BigDecimal("10.12"), priceChange = BigDecimal("-0.10"), ) 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 index 97384de272..e18e4c126d 100644 --- 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 @@ -1,25 +1,23 @@ package com.tangem.data.tokens.mock -import com.tangem.domain.tokens.model.Token +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId internal object MockTokens { val token1 - get() = Token( - id = Token.ID("token1"), + get() = CryptoCurrency.Coin( + id = CryptoCurrency.ID("token1"), networkId = MockNetworks.network1.id, name = "Token 1", symbol = "T1", - isCustom = false, decimals = 8, iconUrl = null, - contractAddress = null, derivationPath = null, ) val token2 - get() = Token( - id = Token.ID("token2"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token2"), networkId = MockNetworks.network1.id, name = "Token 2", symbol = "T2", @@ -30,8 +28,8 @@ internal object MockTokens { derivationPath = null, ) val token3 - get() = Token( - id = Token.ID("token3"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token3"), networkId = MockNetworks.network1.id, name = "Token 3", symbol = "T3", @@ -42,20 +40,18 @@ internal object MockTokens { derivationPath = null, ) val token4 - get() = Token( - id = Token.ID("token4"), + get() = CryptoCurrency.Coin( + id = CryptoCurrency.ID("token4"), networkId = MockNetworks.network2.id, name = "Token 4", symbol = "T4", - isCustom = false, decimals = 8, iconUrl = null, - contractAddress = null, derivationPath = null, ) val token5 - get() = Token( - id = Token.ID("token5"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token5"), networkId = MockNetworks.network2.id, name = "Token 5", symbol = "T5", @@ -66,8 +62,8 @@ internal object MockTokens { derivationPath = null, ) val token6 - get() = Token( - id = Token.ID("token6"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token6"), networkId = MockNetworks.network2.id, name = "Token 6", symbol = "T6", @@ -78,20 +74,18 @@ internal object MockTokens { derivationPath = null, ) val token7 - get() = Token( - id = Token.ID("token7"), + get() = CryptoCurrency.Coin( + id = CryptoCurrency.ID("token7"), networkId = MockNetworks.network3.id, name = "Token 7", symbol = "T7", - isCustom = false, decimals = 8, iconUrl = null, - contractAddress = null, derivationPath = null, ) val token8 - get() = Token( - id = Token.ID("token8"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token8"), networkId = MockNetworks.network3.id, name = "Token 8", symbol = "T8", @@ -102,8 +96,8 @@ internal object MockTokens { derivationPath = null, ) val token9 - get() = Token( - id = Token.ID("token9"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token9"), networkId = MockNetworks.network3.id, name = "Token 9", symbol = "T9", @@ -114,8 +108,8 @@ internal object MockTokens { derivationPath = null, ) val token10 - get() = Token( - id = Token.ID("token10"), + get() = CryptoCurrency.Token( + id = CryptoCurrency.ID("token10"), networkId = MockNetworks.network3.id, name = "Token 10", symbol = "T10", diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt index f38aae9394..704e25fa68 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt @@ -1,15 +1,15 @@ package com.tangem.data.tokens.repository import com.tangem.data.common.cache.CacheRegistry -import com.tangem.data.tokens.utils.CardTokensFactory -import com.tangem.data.tokens.utils.ResponseTokensFactory +import com.tangem.data.tokens.utils.CardCurrenciesFactory +import com.tangem.data.tokens.utils.ResponseCurrenciesFactory import com.tangem.data.tokens.utils.UserTokensResponseFactory import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.UserTokensResponse 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.Token +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -27,18 +27,18 @@ internal class DefaultTokensRepository( ) : TokensRepository { private val demoConfig = DemoConfig() - private val responseTokensFactory = ResponseTokensFactory(demoConfig) - private val cardTokensFactory = CardTokensFactory(demoConfig) + private val responseCurrenciesFactory = ResponseCurrenciesFactory(demoConfig) + private val cardCurrenciesFactory = CardCurrenciesFactory(demoConfig) private val userTokensResponseFactory = UserTokensResponseFactory() override suspend fun saveTokens( userWalletId: UserWalletId, - tokens: Set, + currencies: Set, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { val response = userTokensResponseFactory.createUserTokensResponse( - tokens = tokens, + currencies = currencies, isGroupedByNetwork = isGroupedByNetwork, isSortedByBalance = isSortedByBalance, ) @@ -46,7 +46,7 @@ internal class DefaultTokensRepository( storeAndPushTokens(userWalletId, response) } - override suspend fun getSingleCurrencyWalletToken(userWalletId: UserWalletId): Token { + override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { val userWallet = withContext(dispatchers.io) { requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find a user wallet with provided ID: $userWalletId" @@ -56,11 +56,13 @@ internal class DefaultTokensRepository( "Single currency wallet excepted, but multi currency wallet was found: $userWalletId" } - return cardTokensFactory - .createPrimaryTokenForSingleCurrencyCard(userWallet.scanResponse) + return cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) } - override fun getMultiCurrencyWalletTokens(userWalletId: UserWalletId, refresh: Boolean): Flow> { + override fun getMultiCurrencyWalletCurrencies( + userWalletId: UserWalletId, + refresh: Boolean, + ): Flow> { return channelFlow { val userWallet = withContext(dispatchers.io) { requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { @@ -72,7 +74,7 @@ internal class DefaultTokensRepository( } launch(dispatchers.io) { - getMultiCurrencyWalletTokens(userWallet).collectLatest(::send) + getMultiCurrencyWalletCurrencies(userWallet).collectLatest(::send) } launch(dispatchers.io) { @@ -93,9 +95,9 @@ internal class DefaultTokensRepository( .flowOn(dispatchers.io) } - private fun getMultiCurrencyWalletTokens(userWallet: UserWallet): Flow> { + private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow> { return userTokensStore.get(userWallet.walletId).map { storedTokens -> - responseTokensFactory.createTokens( + responseCurrenciesFactory.createTokens( response = storedTokens, card = userWallet.scanResponse.card, ) @@ -131,7 +133,9 @@ internal class DefaultTokensRepository( if (NOT_FOUND_HTTP_CODE in errorMessage) { val response = userTokensStore.getSyncOrNull(userWallet.walletId) ?: userTokensResponseFactory.createUserTokensResponse( - tokens = cardTokensFactory.createDefaultTokensForMultiCurrencyCard(userWallet.scanResponse.card), + currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard( + userWallet.scanResponse.card, + ), isGroupedByNetwork = false, isSortedByBalance = false, ) 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 index 26bc58aa70..c6919ea86b 100644 --- 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 @@ -1,9 +1,9 @@ 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.Network import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.Token import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow @@ -19,7 +19,7 @@ internal class MockNetworksRepository : NetworksRepository { override fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Map>, refresh: Boolean, ): Flow> { return flowOf( 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 2510893558..345f17e6bc 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,18 +1,18 @@ 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.model.Token import com.tangem.domain.tokens.repository.QuotesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf internal class MockQuotesRepository : QuotesRepository { - override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { + override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { return flowOf( MockQuotes.quotes - .filter { it.tokenId in tokensIds } + .filter { it.currencyId in tokensIds } .toSet(), ) } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardTokensFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt similarity index 84% rename from data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardTokensFactory.kt rename to data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt index b569162bd8..15fd96b0aa 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardTokensFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt @@ -6,13 +6,13 @@ import com.tangem.domain.common.util.cardTypesResolver 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.model.CryptoCurrency import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken -import com.tangem.domain.tokens.model.Token as DomainToken -internal class CardTokensFactory(private val demoConfig: DemoConfig) { +internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { - fun createDefaultTokensForMultiCurrencyCard(card: CardDTO): Set { + fun createDefaultCoinsForMultiCurrencyCard(card: CardDTO): Set { var blockchains = if (demoConfig.isDemoCardId(card.cardId)) { demoConfig.demoBlockchains } else { @@ -26,7 +26,7 @@ internal class CardTokensFactory(private val demoConfig: DemoConfig) { return blockchains.mapNotNull { createCoin(it, card) }.toSet() } - fun createPrimaryTokenForSingleCurrencyCard(scanResponse: ScanResponse): DomainToken { + fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency { val card = scanResponse.card val resolver = scanResponse.cardTypesResolver val blockchain = resolver.getBlockchain() @@ -41,13 +41,13 @@ internal class CardTokensFactory(private val demoConfig: DemoConfig) { return primaryToken ?: coin } - private fun createToken(sdkToken: SdkToken, blockchain: Blockchain, card: CardDTO): DomainToken? { + private fun createToken(sdkToken: SdkToken, blockchain: Blockchain, card: CardDTO): CryptoCurrency.Token? { if (blockchain != Blockchain.Unknown) { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null } - return DomainToken( + return CryptoCurrency.Token( id = getTokenId(blockchain, sdkToken), networkId = getNetworkId(blockchain), name = sdkToken.name, @@ -60,21 +60,19 @@ internal class CardTokensFactory(private val demoConfig: DemoConfig) { ) } - private fun createCoin(blockchain: Blockchain, card: CardDTO): DomainToken? { + private fun createCoin(blockchain: Blockchain, card: CardDTO): CryptoCurrency.Coin? { if (blockchain != Blockchain.Unknown) { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null } - return DomainToken( + return CryptoCurrency.Coin( id = getCoinId(blockchain), networkId = getNetworkId(blockchain), name = blockchain.fullName, symbol = blockchain.currency, iconUrl = getCoinIconUrl(blockchain), decimals = blockchain.decimals(), - isCustom = false, - contractAddress = null, derivationPath = getDerivationPath(blockchain, card), ) } 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/ResponseCurrenciesFactory.kt new file mode 100644 index 0000000000..0a6c78b0e5 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -0,0 +1,77 @@ +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.extensions.fromNetworkId +import com.tangem.domain.demo.DemoConfig +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.tokens.model.CryptoCurrency +import timber.log.Timber +import com.tangem.blockchain.common.Token as SdkToken + +internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { + + fun createTokens(response: UserTokensResponse, card: CardDTO): Set { + return response.tokens.mapNotNull { createToken(it, card) }.toSet() + } + + private fun createToken(responseToken: UserTokensResponse.Token, card: CardDTO): 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 + } + + if (demoConfig.isDemoCardId(card.cardId)) { + blockchain = blockchain.getTestnetVersion() ?: blockchain + } + + val sdkToken = createSdkToken(responseToken) + return if (sdkToken == null) { + createCoin(blockchain, responseToken) + } else { + createToken(blockchain, sdkToken, responseToken.derivationPath) + } + } + + private fun createSdkToken(token: UserTokensResponse.Token): SdkToken? { + return token.contractAddress?.let { contractAddress -> + SdkToken( + name = token.name, + symbol = token.symbol, + contractAddress = contractAddress, + decimals = token.decimals, + id = token.id, + ) + } + } + + private fun createCoin(blockchain: Blockchain, responseToken: UserTokensResponse.Token): CryptoCurrency.Coin { + return CryptoCurrency.Coin( + id = getCoinId(blockchain), + networkId = getNetworkId(blockchain), + name = responseToken.name, + symbol = responseToken.symbol, + decimals = responseToken.decimals, + derivationPath = responseToken.derivationPath, + iconUrl = getCoinIconUrl(blockchain), + ) + } + + private fun createToken(blockchain: Blockchain, sdkToken: Token, derivationPath: String?): CryptoCurrency.Token { + val id = getTokenId(blockchain, sdkToken) + + return CryptoCurrency.Token( + id = id, + networkId = getNetworkId(blockchain), + name = sdkToken.name, + symbol = sdkToken.symbol, + decimals = sdkToken.decimals, + derivationPath = derivationPath, + iconUrl = getTokenIconUrl(blockchain, sdkToken), + contractAddress = sdkToken.contractAddress, + isCustom = isCustomToken(id), + ) + } +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseTokensFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseTokensFactory.kt deleted file mode 100644 index 081b7ffa64..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseTokensFactory.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.data.tokens.utils - -import com.tangem.blockchain.common.Blockchain -import com.tangem.datasource.api.tangemTech.models.UserTokensResponse -import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.domain.demo.DemoConfig -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.tokens.model.Token -import timber.log.Timber -import com.tangem.blockchain.common.Token as SdkToken - -internal class ResponseTokensFactory(private val demoConfig: DemoConfig) { - - fun createTokens(response: UserTokensResponse, card: CardDTO): Set { - return response.tokens.mapNotNull { createToken(it, card) }.toSet() - } - - private fun createToken(token: UserTokensResponse.Token, card: CardDTO): Token? { - var blockchain = Blockchain.fromNetworkId(token.networkId) - if (blockchain == null) { - Timber.e("Unable to find a blockchain with the network ID: ${token.networkId}") - return null - } - - if (demoConfig.isDemoCardId(card.cardId)) { - blockchain = blockchain.getTestnetVersion() ?: blockchain - } - - val sdkToken = createSdkToken(token) - val (tokenId, iconUrl) = if (sdkToken == null) { - getCoinId(blockchain) to getCoinIconUrl(blockchain) - } else { - getTokenId(blockchain, sdkToken) to getTokenIconUrl(blockchain, sdkToken) - } - - return Token( - id = tokenId, - networkId = getNetworkId(blockchain), - name = token.name, - symbol = token.symbol, - decimals = token.decimals, - iconUrl = iconUrl, - contractAddress = token.contractAddress, - derivationPath = token.derivationPath, - isCustom = isCustomToken(tokenId), - ) - } - - private fun createSdkToken(token: UserTokensResponse.Token): SdkToken? { - return token.contractAddress?.let { contractAddress -> - SdkToken( - name = token.name, - symbol = token.symbol, - contractAddress = contractAddress, - decimals = token.decimals, - id = token.id, - ) - } - } -} \ 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 157df77386..627f30111d 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 @@ -6,9 +6,9 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.blockchain.common.Token as SdkToken -import com.tangem.domain.tokens.model.Token as DomainToken private const val DEFAULT_TOKENS_ICONS_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins" private const val TOKEN_ICON_SIZE = "large" @@ -19,7 +19,7 @@ private const val TOKEN_ID_PREFIX = "token_" private const val CUSTOM_TOKEN_ID_PREFIX = "custom_token_" private const val TOKEN_ID_DELIMITER = '#' -internal fun isCustomToken(tokenId: DomainToken.ID): Boolean { +internal fun isCustomToken(tokenId: CryptoCurrency.ID): Boolean { return tokenId.value.startsWith(CUSTOM_TOKEN_ID_PREFIX) } @@ -41,17 +41,17 @@ internal fun getNetworkId(blockchain: Blockchain): Network.ID { return Network.ID(value) } -internal fun getCoinId(blockchain: Blockchain): DomainToken.ID { +internal fun getCoinId(blockchain: Blockchain): CryptoCurrency.ID { return getTokenOrCoinId(blockchain, token = null) } -internal fun getTokenId(blockchain: Blockchain, token: SdkToken): DomainToken.ID { +internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency.ID { return getTokenOrCoinId(blockchain, token) } -internal fun getResponseTokenId(token: DomainToken): String? { - return token.id.value.substringAfter(TOKEN_ID_DELIMITER) - .takeUnless { token.isCustom } +internal fun getResponseTokenId(currency: CryptoCurrency): String? { + return currency.id.value.substringAfter(TOKEN_ID_DELIMITER) + .takeUnless { currency is CryptoCurrency.Token && currency.isCustom } } internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? { @@ -74,7 +74,7 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? { return coinId?.let(::getTokenIconUrlFromDefaultHost) } -private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): DomainToken.ID { +private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): CryptoCurrency.ID { val sdkTokenId = token?.id val (prefix, suffix) = when { token == null -> COIN_ID_PREFIX to blockchain.toCoinId() @@ -89,7 +89,7 @@ private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): DomainTo append(suffix.lowercase()) } - return DomainToken.ID(value) + return CryptoCurrency.ID(value) } 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 a7c85ba0d6..ffb4fc9c10 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 @@ -2,17 +2,17 @@ package com.tangem.data.tokens.utils import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.tokens.model.Token +import com.tangem.domain.tokens.model.CryptoCurrency internal class UserTokensResponseFactory { fun createUserTokensResponse( - tokens: Set, + currencies: Set, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ): UserTokensResponse { return UserTokensResponse( - tokens = tokens.map(::createResponseToken), + tokens = currencies.map(::createResponseToken), group = if (isGroupedByNetwork) { UserTokensResponse.GroupType.NETWORK } else { @@ -26,17 +26,17 @@ internal class UserTokensResponseFactory { ) } - private fun createResponseToken(domainToken: Token): UserTokensResponse.Token { - val blockchain = getBlockchain(domainToken.networkId) + private fun createResponseToken(currency: CryptoCurrency): UserTokensResponse.Token { + val blockchain = getBlockchain(currency.networkId) return UserTokensResponse.Token( - id = getResponseTokenId(domainToken), + id = getResponseTokenId(currency), networkId = blockchain.toNetworkId(), - derivationPath = domainToken.derivationPath, - name = domainToken.name, - symbol = domainToken.symbol, - decimals = domainToken.decimals, - contractAddress = domainToken.contractAddress, + derivationPath = currency.derivationPath, + name = currency.name, + symbol = currency.symbol, + decimals = currency.decimals, + contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress, ) } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 2004da059c..c1bad61702 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -7,8 +7,8 @@ import arrow.core.raise.either import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network -import com.tangem.domain.tokens.model.Token import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -22,7 +22,7 @@ class ApplyTokenListSortingUseCase( suspend operator fun invoke( userWalletId: UserWalletId, - sortedTokensIds: Set>, + sortedTokensIds: Set>, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ): Either { @@ -30,7 +30,7 @@ class ApplyTokenListSortingUseCase( either { applySorting( userWalletId = userWalletId, - tokens = sortTokens(sortedTokensIds, getTokens(userWalletId)), + tokens = sortTokens(sortedTokensIds, getCurrencies(userWalletId)), isGrouped = isGroupedByNetwork, isSortedByBalance = isSortedByBalance, ) @@ -39,14 +39,14 @@ class ApplyTokenListSortingUseCase( } private suspend fun Raise.sortTokens( - sortedTokensIds: Set>, - unsortedTokens: Set, - ): Set = withContext(dispatchers.default) { + sortedTokensIds: Set>, + unsortedTokens: Set, + ): Set = withContext(dispatchers.default) { val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) { TokenListSortingError.TokenListIsEmpty } - val sortedTokens = sortedMapOf() + val sortedTokens = sortedMapOf() unsortedTokens.forEach { token -> val index = nonEmptySortedTokensIds.indexOfFirst { (networkId, tokenId) -> @@ -65,9 +65,9 @@ class ApplyTokenListSortingUseCase( } } - private suspend fun Raise.getTokens(userWalletId: UserWalletId): Set { + private suspend fun Raise.getCurrencies(userWalletId: UserWalletId): Set { val tokens = catch( - block = { tokensRepository.getMultiCurrencyWalletTokens(userWalletId, refresh = false).firstOrNull() }, + block = { tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() }, catch = { raise(TokenListSortingError.DataError(it)) }, ) @@ -78,7 +78,7 @@ class ApplyTokenListSortingUseCase( private suspend fun Raise.applySorting( userWalletId: UserWalletId, - tokens: Set, + tokens: Set, isGrouped: Boolean, isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt similarity index 70% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenUseCase.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt index fd3ea69f0b..bfc25b58da 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt @@ -7,8 +7,8 @@ import arrow.core.raise.recover import arrow.core.right import com.tangem.domain.tokens.error.TokenError import com.tangem.domain.tokens.error.mapper.mapToTokenError -import com.tangem.domain.tokens.model.TokenStatus -import com.tangem.domain.tokens.operations.TokensStatusesOperations +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.repository.TokensRepository @@ -18,14 +18,17 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest -class GetTokenUseCase( +class GetPrimaryCurrencyUseCase( private val tokensRepository: TokensRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val dispatchers: CoroutineDispatcherProvider, ) { - operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Flow> { + operator fun invoke( + userWalletId: UserWalletId, + refresh: Boolean = false, + ): Flow> { return channelFlow { recover( block = { @@ -40,8 +43,11 @@ class GetTokenUseCase( } } - private suspend fun Raise.getToken(userWalletId: UserWalletId, refresh: Boolean): Flow { - val operations = TokensStatusesOperations( + private suspend fun Raise.getToken( + userWalletId: UserWalletId, + refresh: Boolean, + ): Flow { + val operations = CurrenciesStatusesOperations( tokensRepository = tokensRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, @@ -49,9 +55,9 @@ class GetTokenUseCase( refresh = refresh, dispatchers = dispatchers, raise = this, - transformError = TokensStatusesOperations.Error::mapToTokenError, + transformError = CurrenciesStatusesOperations.Error::mapToTokenError, ) - return operations.getSingleCurrencyWalletTokenStatusFlow() + return operations.getPrimaryCurrencyStatusFlow() } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index 9f07999ccd..0a142e1241 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -7,10 +7,10 @@ import arrow.core.raise.recover import arrow.core.right import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.mapper.mapToTokenListError +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListOperations -import com.tangem.domain.tokens.operations.TokensStatusesOperations import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.repository.TokensRepository @@ -51,21 +51,21 @@ class GetTokenListUseCase( private fun Raise.getTokensStatuses( userWalletId: UserWalletId, refresh: Boolean, - ): Flow> { - val operations = TokensStatusesOperations( + ): Flow> { + val operations = CurrenciesStatusesOperations( userWalletId = userWalletId, refresh = refresh, useCase = this@GetTokenListUseCase, raise = this, - transformError = TokensStatusesOperations.Error::mapToTokenListError, + transformError = CurrenciesStatusesOperations.Error::mapToTokenListError, ) - return operations.getMultiCurrencyWalletTokensStatusesFlow() + return operations.getMultiCurrencyWalletStatusesFlow() } private fun Raise.createTokenList( userWalletId: UserWalletId, - tokens: Set, + tokens: Set, ): Flow { val operations = TokenListOperations( userWalletId = userWalletId, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt index 4d65f82422..cb53f81ec2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt @@ -41,7 +41,7 @@ class ToggleTokenListGroupingUseCase( val sortingOperations = getSortingOperations(tokenList) val tokens = sortingOperations.getTokens() - val networks = getNetworks(tokens.map { it.networkId }.toSet()) + val networks = getNetworks(tokens.map { it.currency.networkId }.toSet()) return TokenList.GroupedByNetwork( groups = sortingOperations.getGroupedTokens(networks), totalFiatBalance = tokenList.totalFiatBalance, @@ -55,7 +55,7 @@ class ToggleTokenListGroupingUseCase( val sortingOperations = getSortingOperations(tokenList) return TokenList.Ungrouped( - tokens = sortingOperations.getTokens(), + currencies = sortingOperations.getTokens(), totalFiatBalance = tokenList.totalFiatBalance, sortedBy = sortingOperations.getSortType(), ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt index 4b0b8664f4..b91ce9521a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt @@ -49,7 +49,7 @@ class ToggleTokenListSortingUseCase( val operations = getSortingOperations(tokenList) return tokenList.copy( - tokens = operations.getTokens(), + currencies = operations.getTokens(), sortedBy = operations.getSortType(), ) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt index 25f2bf46d3..1252251389 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt @@ -1,14 +1,14 @@ package com.tangem.domain.tokens.error.mapper import com.tangem.domain.tokens.error.TokenError -import com.tangem.domain.tokens.operations.TokensStatusesOperations +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -internal fun TokensStatusesOperations.Error.mapToTokenError(): TokenError { +internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): TokenError { return when (this) { - is TokensStatusesOperations.Error.DataError -> TokenError.DataError(this.cause) - is TokensStatusesOperations.Error.EmptyNetworksStatuses, - is TokensStatusesOperations.Error.EmptyQuotes, - is TokensStatusesOperations.Error.EmptyTokens, + is CurrenciesStatusesOperations.Error.DataError -> TokenError.DataError(this.cause) + is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses, + is CurrenciesStatusesOperations.Error.EmptyQuotes, + is CurrenciesStatusesOperations.Error.EmptyCurrencies, -> TokenError.UnableToCreateToken } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/TokenListErrorMappers.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/TokenListErrorMappers.kt index 2d0798a489..9b24b82737 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/TokenListErrorMappers.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/TokenListErrorMappers.kt @@ -1,15 +1,15 @@ package com.tangem.domain.tokens.error.mapper import com.tangem.domain.tokens.error.TokenListError +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListOperations -import com.tangem.domain.tokens.operations.TokensStatusesOperations -internal fun TokensStatusesOperations.Error.mapToTokenListError(): TokenListError { +internal fun CurrenciesStatusesOperations.Error.mapToTokenListError(): TokenListError { return when (this) { - is TokensStatusesOperations.Error.DataError -> TokenListError.DataError(this.cause) - is TokensStatusesOperations.Error.EmptyNetworksStatuses, - is TokensStatusesOperations.Error.EmptyQuotes, - is TokensStatusesOperations.Error.EmptyTokens, + is CurrenciesStatusesOperations.Error.DataError -> TokenListError.DataError(this.cause) + is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses, + is CurrenciesStatusesOperations.Error.EmptyQuotes, + is CurrenciesStatusesOperations.Error.EmptyCurrencies, -> TokenListError.EmptyTokens } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt new file mode 100644 index 0000000000..891d75986e --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt @@ -0,0 +1,61 @@ +package com.tangem.domain.tokens.model + +sealed class CryptoCurrency { + + abstract val id: ID + abstract val networkId: Network.ID + abstract val name: String + abstract val symbol: String + abstract val decimals: Int + abstract val iconUrl: String? + abstract val derivationPath: String? + + data class Coin( + override val id: ID, + override val networkId: Network.ID, + override val name: String, + override val symbol: String, + override val decimals: Int, + override val iconUrl: String?, + override val derivationPath: String?, + ) : CryptoCurrency() { + + init { + checkProperties() + } + } + + data class Token( + override val id: ID, + override val networkId: Network.ID, + override val name: String, + override val symbol: String, + override val decimals: Int, + override val iconUrl: String?, + override val derivationPath: String?, + val contractAddress: String, + val isCustom: Boolean, + ) : CryptoCurrency() { + + init { + checkProperties() + require(contractAddress.isNotBlank()) { "Token contract address must not be blank" } + } + } + + @JvmInline + value class ID(val value: String) { + + init { + require(value.isNotBlank()) { "Crypto currency ID must not be blank" } + } + } + + protected fun checkProperties() { + require(name.isNotBlank()) { "Crypto currency name must not be blank" } + 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/src/main/kotlin/com/tangem/domain/tokens/model/TokenStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt similarity index 84% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenStatus.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt index 9a217c2f35..f67fc23e5a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt @@ -2,14 +2,8 @@ package com.tangem.domain.tokens.model import java.math.BigDecimal -data class TokenStatus( - val id: Token.ID, - val networkId: Network.ID, - val name: String, - val symbol: String, - val decimals: Int, - val iconUrl: String?, - val isCoin: Boolean, +data class CryptoCurrencyStatus( + val currency: CryptoCurrency, val value: Status, ) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt index d1cc917d38..51d03a899e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt @@ -2,5 +2,5 @@ package com.tangem.domain.tokens.model data class NetworkGroup( val network: Network, - val tokens: Set, + val currencies: Set, ) \ No newline at end of file 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 36d1bc30e6..f76108f535 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,16 +8,16 @@ data class NetworkStatus( ) { sealed class Status { - open val amounts: Map? = null + open val amounts: Map? = null } object Unreachable : Status() object MissedDerivation : Status() - data class TransactionInProgress(override val amounts: Map) : Status() + data class TransactionInProgress(override val amounts: Map) : Status() - data class Verified(override val amounts: Map) : Status() + data class Verified(override val amounts: Map) : Status() data class NoAccount(val amountToCreateAccount: BigDecimal) : Status() } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt index fad3db3666..192c196537 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt @@ -3,7 +3,7 @@ package com.tangem.domain.tokens.model import java.math.BigDecimal data class Quote( - val tokenId: Token.ID, + val currencyId: CryptoCurrency.ID, val fiatRate: BigDecimal, val priceChange: BigDecimal, ) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Token.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Token.kt deleted file mode 100644 index 745acaa6b5..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Token.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.domain.tokens.model - -data class Token( - val id: ID, - val networkId: Network.ID, - val name: String, - val symbol: String, - val iconUrl: String?, - val decimals: Int, - val isCustom: Boolean, - val contractAddress: String?, - val derivationPath: String?, -) { - - init { - require(name.isNotBlank()) { "Token name must not be blank" } - require(symbol.isNotBlank()) { "Token symbol must not be blank" } - require(iconUrl?.isNotBlank() ?: true) { "Token icon URL must not be blank" } - require(decimals > 0) { "Token decimal must not be less then zero, but it is: $decimals" } - require(contractAddress?.isNotBlank() ?: true) { "Token contract address must not be blank" } - require(derivationPath?.isNotBlank() ?: true) { "Token derivation path must not be blank" } - } - - @JvmInline - value class ID(val value: String) { - - init { - require(value.isNotBlank()) { "Token ID value must not be blank" } - } - } -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt index 3f1ffaaf75..f19f35bef2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt @@ -13,7 +13,7 @@ sealed class TokenList { ) : TokenList() data class Ungrouped( - val tokens: Set, + val currencies: Set, override val totalFiatBalance: FiatBalance, override val sortedBy: SortType, ) : TokenList() diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokensStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt similarity index 69% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokensStatusesOperations.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 7b7eced70a..e64237f1a3 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokensStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.withContext @Suppress("LongParameterList") -internal class TokensStatusesOperations( +internal class CurrenciesStatusesOperations( private val tokensRepository: TokensRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, @@ -24,7 +24,7 @@ internal class TokensStatusesOperations( private val dispatchers: CoroutineDispatcherProvider, raise: Raise, transformError: (Error) -> E, -) : DelegatedRaise(raise, transformError) { +) : DelegatedRaise(raise, transformError) { constructor( userWalletId: UserWalletId, @@ -43,8 +43,8 @@ internal class TokensStatusesOperations( transformError = transformError, ) - fun getMultiCurrencyWalletTokensStatusesFlow(): Flow> { - return getMultiCurrencyWalletTokens().flatMapConcat { + fun getMultiCurrencyWalletStatusesFlow(): Flow> { + return getMultiCurrencyWalletCurrencies().flatMapConcat { val tokens = it.toNonEmptySetOrNull() if (tokens == null) { @@ -60,12 +60,12 @@ internal class TokensStatusesOperations( } } - suspend fun getSingleCurrencyWalletTokenStatusFlow(): Flow { - val token = getSingleCurrencyWalletToken() + suspend fun getPrimaryCurrencyStatusFlow(): Flow { + val token = getPrimaryCurrency() val quoteFlow = getQuotes(nonEmptySetOf(token.id)) .map { quotes -> - quotes.singleOrNull { it.tokenId == token.id } + quotes.singleOrNull { it.currencyId == token.id } } val statusFlow = getNetworksStatues(groupTokens(nonEmptySetOf(token))) @@ -79,61 +79,69 @@ internal class TokensStatusesOperations( } private suspend fun createTokensStatuses( - tokens: Set, + tokens: Set, quotes: Set, networkStatuses: Set, - ): Set = withContext(dispatchers.default) { + ): Set = withContext(dispatchers.default) { tokens.mapTo(hashSetOf()) { token -> - val quote = quotes.firstOrNull { it.tokenId == token.id } + val quote = quotes.firstOrNull { it.currencyId == token.id } val networkStatus = networkStatuses.firstOrNull { it.networkId == token.networkId } createStatus(token, quote, networkStatus) } } - private suspend fun createStatus(token: Token, quote: Quote?, networkStatus: NetworkStatus?): TokenStatus { - val tokenStatusOperations = TokenStatusOperations( - token = token, + private suspend fun createStatus( + token: CryptoCurrency, + quote: Quote?, + networkStatus: NetworkStatus?, + ): CryptoCurrencyStatus { + val currencyStatusOperations = CurrencyStatusOperations( + currency = token, quote = quote, networkStatus = networkStatus, dispatchers = dispatchers, ) - return tokenStatusOperations.createTokenStatus() + return currencyStatusOperations.createTokenStatus() } - private fun getMultiCurrencyWalletTokens(): Flow> { - return tokensRepository.getMultiCurrencyWalletTokens(userWalletId, refresh) + private fun getMultiCurrencyWalletCurrencies(): Flow> { + return tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) .catch { raise(Error.DataError(it)) } - .onEmpty { raise(Error.EmptyTokens) } + .onEmpty { raise(Error.EmptyCurrencies) } .flowOn(dispatchers.io) } - private suspend fun getSingleCurrencyWalletToken(): Token { + private suspend fun getPrimaryCurrency(): CryptoCurrency { return withContext(dispatchers.io) { catch( - block = { tokensRepository.getSingleCurrencyWalletToken(userWalletId) }, + block = { tokensRepository.getPrimaryCurrency(userWalletId) }, catch = { raise(Error.DataError(it)) }, ) } } - private fun getQuotes(tokensIds: NonEmptySet): Flow> { + private fun getQuotes(tokensIds: NonEmptySet): Flow> { return quotesRepository.getQuotes(tokensIds, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyQuotes) } .flowOn(dispatchers.io) } - private fun getNetworksStatues(groupedTokens: Map>): Flow> { + private fun getNetworksStatues( + groupedTokens: Map>, + ): Flow> { return networksRepository.getNetworkStatuses(userWalletId, groupedTokens, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyNetworksStatuses) } .flowOn(dispatchers.io) } - private suspend fun groupTokens(tokens: NonEmptySet): Map> = - withContext(dispatchers.default) { + private suspend fun groupTokens( + tokens: NonEmptySet, + ): Map> { + return withContext(dispatchers.default) { tokens .groupBy { it.networkId } .mapValues { (_, tokens) -> @@ -143,10 +151,11 @@ internal class TokensStatusesOperations( .toNonEmptySet() } } + } sealed class Error { - object EmptyTokens : Error() + object EmptyCurrencies : Error() object EmptyQuotes : Error() diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt similarity index 64% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenStatusOperations.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index 250ba9932f..a48964936b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -1,39 +1,33 @@ package com.tangem.domain.tokens.operations +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote -import com.tangem.domain.tokens.model.Token -import com.tangem.domain.tokens.model.TokenStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import java.math.BigDecimal -internal class TokenStatusOperations( - private val token: Token, +internal class CurrencyStatusOperations( + private val currency: CryptoCurrency, private val quote: Quote?, private val networkStatus: NetworkStatus?, private val dispatchers: CoroutineDispatcherProvider, ) { - suspend fun createTokenStatus(): TokenStatus = withContext(dispatchers.default) { - TokenStatus( - id = token.id, - networkId = token.networkId, - name = token.name, - symbol = token.symbol, - isCoin = token.contractAddress == null, - decimals = token.decimals, - iconUrl = token.iconUrl, + suspend fun createTokenStatus(): CryptoCurrencyStatus = withContext(dispatchers.default) { + CryptoCurrencyStatus( + currency = currency, value = createStatus(), ) } - private fun createStatus(): TokenStatus.Status { + private fun createStatus(): CryptoCurrencyStatus.Status { return when (val status = networkStatus?.value) { - null -> TokenStatus.Loading - is NetworkStatus.MissedDerivation -> TokenStatus.MissedDerivation - is NetworkStatus.Unreachable -> TokenStatus.Unreachable - is NetworkStatus.NoAccount -> TokenStatus.NoAccount + null -> CryptoCurrencyStatus.Loading + is NetworkStatus.MissedDerivation -> CryptoCurrencyStatus.MissedDerivation + is NetworkStatus.Unreachable -> CryptoCurrencyStatus.Unreachable + is NetworkStatus.NoAccount -> CryptoCurrencyStatus.NoAccount is NetworkStatus.TransactionInProgress, is NetworkStatus.Verified, -> createStatus( @@ -43,17 +37,17 @@ internal class TokenStatusOperations( } } - private fun createStatus(amount: BigDecimal, hasTransactionsInProgress: Boolean): TokenStatus.Status { + private fun createStatus(amount: BigDecimal, hasTransactionsInProgress: Boolean): CryptoCurrencyStatus.Status { return when { - token.isCustom -> TokenStatus.Custom( + currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom( amount = amount, fiatAmount = calculateFiatAmountOrNull(amount, quote?.fiatRate), fiatRate = quote?.fiatRate, priceChange = quote?.priceChange, hasTransactionsInProgress = hasTransactionsInProgress, ) - quote == null -> TokenStatus.Loading - else -> TokenStatus.Loaded( + quote == null -> CryptoCurrencyStatus.Loading + else -> CryptoCurrencyStatus.Loaded( amount = amount, fiatAmount = calculateFiatAmount(amount, quote.fiatRate), fiatRate = quote.fiatRate, @@ -64,7 +58,7 @@ internal class TokenStatusOperations( } private fun getTokenAmount(): BigDecimal { - val amount = networkStatus?.value?.amounts?.get(token.id) + val amount = networkStatus?.value?.amounts?.get(currency.id) return amount ?: error("Incorrect network status: $networkStatus") } 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 69378510f3..dd3cb367d5 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 @@ -1,14 +1,14 @@ package com.tangem.domain.tokens.operations import arrow.core.NonEmptySet +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import java.math.BigDecimal internal class TokenListFiatBalanceOperations( - private val tokens: NonEmptySet, + private val currencies: NonEmptySet, private val isAnyTokenLoading: Boolean, private val dispatcher: CoroutineDispatcherProvider, ) { @@ -18,25 +18,25 @@ internal class TokenListFiatBalanceOperations( var fiatBalance: TokenList.FiatBalance = TokenList.FiatBalance.Loading if (isAnyTokenLoading) return@withContext fiatBalance - for (token in tokens) { + for (token in currencies) { when (val status = token.value) { - is TokenStatus.Loading -> { + is CryptoCurrencyStatus.Loading -> { fiatBalance = TokenList.FiatBalance.Loading break } - is TokenStatus.MissedDerivation, - is TokenStatus.Unreachable, + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.Unreachable, -> { fiatBalance = TokenList.FiatBalance.Failed break } - is TokenStatus.NoAccount -> { + is CryptoCurrencyStatus.NoAccount -> { fiatBalance = recalculateBalanceForNoAccountStatus(fiatBalance) } - is TokenStatus.Loaded -> { + is CryptoCurrencyStatus.Loaded -> { fiatBalance = recalculateBalance(status, fiatBalance) } - is TokenStatus.Custom -> { + is CryptoCurrencyStatus.Custom -> { fiatBalance = recalculateBalance(status, fiatBalance) } } @@ -57,7 +57,7 @@ internal class TokenListFiatBalanceOperations( } private fun recalculateBalance( - status: TokenStatus.Loaded, + status: CryptoCurrencyStatus.Loaded, currentBalance: TokenList.FiatBalance, ): TokenList.FiatBalance { return with(currentBalance) { @@ -71,7 +71,7 @@ internal class TokenListFiatBalanceOperations( } private fun recalculateBalance( - status: TokenStatus.Custom, + status: CryptoCurrencyStatus.Custom, currentBalance: TokenList.FiatBalance, ): TokenList.FiatBalance { return with(currentBalance) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index 9283eacb78..a39720b230 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -7,9 +7,9 @@ import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.core.raise.DelegatedRaise import com.tangem.domain.tokens.GetTokenListUseCase +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId @@ -22,7 +22,7 @@ internal class TokenListOperations( private val tokensRepository: TokensRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, - private val tokens: Set, + private val tokens: Set, private val dispatchers: CoroutineDispatcherProvider, raise: Raise, transform: (Error) -> E, @@ -30,7 +30,7 @@ internal class TokenListOperations( constructor( userWalletId: UserWalletId, - tokens: Set, + tokens: Set, useCase: GetTokenListUseCase, raise: Raise, transform: (Error) -> E, @@ -55,7 +55,7 @@ internal class TokenListOperations( val tokensNes = tokens.toNonEmptySetOrNull() ?: return@withContext TokenList.NotInitialized - val isAnyTokenLoading = tokensNes.any { it.value is TokenStatus.Loading } + val isAnyTokenLoading = tokensNes.any { it.value is CryptoCurrencyStatus.Loading } val fiatBalanceOperations = TokenListFiatBalanceOperations(tokensNes, isAnyTokenLoading, dispatchers) createTokenList( @@ -69,14 +69,14 @@ internal class TokenListOperations( } private suspend fun createTokenList( - tokens: NonEmptySet, + tokens: NonEmptySet, fiatBalance: TokenList.FiatBalance, isAnyTokenLoading: Boolean, isGrouped: Boolean, isSortedByBalance: Boolean, ): TokenList { val sortingOperations = TokenListSortingOperations( - tokens = tokens, + currencies = tokens, isAnyTokenLoading = isAnyTokenLoading, sortByBalance = isSortedByBalance, dispatchers = dispatchers, @@ -90,7 +90,7 @@ internal class TokenListOperations( } private suspend fun createTokenList( - tokens: NonEmptySet, + tokens: NonEmptySet, sortingOperations: TokenListSortingOperations<*>, fiatBalance: TokenList.FiatBalance, isGrouped: Boolean, @@ -108,9 +108,9 @@ internal class TokenListOperations( } } - private suspend fun getNetworks(tokensNes: NonEmptySet): Set { + private suspend fun getNetworks(tokensNes: NonEmptySet): Set { return withContext(dispatchers.io) { - val networksIds = tokensNes.map { it.networkId }.toNonEmptySet() + val networksIds = tokensNes.map { it.currency.networkId }.toNonEmptySet() catch( block = { networksRepository.getNetworks(networksIds) }, catch = { raise(Error.DataError(it)) }, @@ -124,7 +124,7 @@ internal class TokenListOperations( ): TokenList.Ungrouped = TokenList.Ungrouped( sortedBy = sortingOperations.getSortType(), totalFiatBalance = fiatBalance, - tokens = sortingOperations.getTokens(), + currencies = sortingOperations.getTokens(), ) private suspend fun createGroupedTokenList( @@ -138,13 +138,13 @@ internal class TokenListOperations( ) private fun createUnsortedUngroupedTokenList( - tokens: NonEmptySet, + tokens: NonEmptySet, fiatBalance: TokenList.FiatBalance, ): TokenList.Ungrouped { return TokenList.Ungrouped( sortedBy = TokenList.SortType.NONE, totalFiatBalance = fiatBalance, - tokens = tokens, + currencies = tokens, ) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt index e40520ec97..debb451f54 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt @@ -6,16 +6,16 @@ import arrow.core.raise.ensure import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.core.raise.DelegatedRaise +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import java.math.BigDecimal internal class TokenListSortingOperations( - private val tokens: Set, + private val currencies: Set, private val isAnyTokenLoading: Boolean, private val sortByBalance: Boolean, private val dispatchers: CoroutineDispatcherProvider, @@ -31,9 +31,9 @@ internal class TokenListSortingOperations( sortByBalance: Boolean = tokenList.sortedBy == TokenList.SortType.BALANCE, isAnyTokenLoading: Boolean = tokenList.totalFiatBalance is TokenList.FiatBalance.Loading, ) : this( - tokens = when (tokenList) { - is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.tokens }.toSet() - is TokenList.Ungrouped -> tokenList.tokens + currencies = when (tokenList) { + is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.currencies }.toSet() + is TokenList.Ungrouped -> tokenList.currencies is TokenList.NotInitialized -> emptySet() }, isAnyTokenLoading = isAnyTokenLoading, @@ -45,7 +45,7 @@ internal class TokenListSortingOperations( suspend fun getGroupedTokens(networks: Set): NonEmptySet { return withContext(dispatchers.default) { - ensure(tokens.isNotEmpty()) { Error.EmptyTokens } + ensure(currencies.isNotEmpty()) { Error.EmptyTokens } val networksNes = ensureNotNull(networks.toNonEmptySetOrNull()) { Error.EmptyNetworks } @@ -58,9 +58,9 @@ internal class TokenListSortingOperations( } } - suspend fun getTokens(): NonEmptySet { + suspend fun getTokens(): NonEmptySet { return withContext(dispatchers.default) { - val tokensNes = ensureNotNull(tokens.toNonEmptySetOrNull()) { + val tokensNes = ensureNotNull(currencies.toNonEmptySetOrNull()) { Error.EmptyTokens } @@ -71,8 +71,8 @@ internal class TokenListSortingOperations( fun getSortType() = if (sortByBalance) TokenList.SortType.BALANCE else TokenList.SortType.NONE private fun groupTokens(networks: NonEmptySet): NonEmptySet { - val groupedTokens = tokens - .groupBy { it.networkId } + val groupedTokens = currencies + .groupBy { it.currency.networkId } .map { (networkId, tokens) -> val network = ensureNotNull(networks.firstOrNull { it.id == networkId }) { Error.NetworkNotFound(networkId) @@ -80,7 +80,7 @@ internal class TokenListSortingOperations( NetworkGroup( network = network, - tokens = ensureNotNull(tokens.toNonEmptySetOrNull()) { Error.EmptyTokens }, + currencies = ensureNotNull(tokens.toNonEmptySetOrNull()) { Error.EmptyTokens }, ) } .toNonEmptySetOrNull() @@ -91,9 +91,9 @@ internal class TokenListSortingOperations( private fun groupAndSortTokensByBalance(networks: NonEmptySet): NonEmptySet { val groupsWithSortedTokens = groupTokens(networks) .map { group -> - val tokens = group.tokens as? NonEmptySet + val tokens = group.currencies as? NonEmptySet ?: error("Tokens can not be empty here") - group.copy(tokens = sortTokensByBalance(tokens)) + group.copy(currencies = sortTokensByBalance(tokens)) } .toNonEmptySet() @@ -104,7 +104,7 @@ internal class TokenListSortingOperations( } } - private fun sortTokensByBalance(tokens: NonEmptySet): NonEmptySet { + private fun sortTokensByBalance(tokens: NonEmptySet): NonEmptySet { return if (isAnyTokenLoading) { tokens } else { @@ -117,7 +117,7 @@ internal class TokenListSortingOperations( private fun sortGroupsByBalance(groupsWithSortedTokens: NonEmptySet): NonEmptySet { return groupsWithSortedTokens .sortedByDescending { group -> - group.tokens.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO } + group.currencies.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO } } .toNonEmptySetOrNull() ?: error("Tokens can not be empty here") 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 97efc6c34a..36af396bbe 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,8 +1,8 @@ package com.tangem.domain.tokens.repository +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.Token import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow @@ -15,7 +15,7 @@ interface NetworksRepository { fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Map>, 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 fd3e10aaff..d1aaef5ae1 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 @@ -1,7 +1,7 @@ package com.tangem.domain.tokens.repository +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Quote -import com.tangem.domain.tokens.model.Token import kotlinx.coroutines.flow.Flow /** @@ -9,5 +9,5 @@ import kotlinx.coroutines.flow.Flow * */ interface QuotesRepository { - fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> + fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt index c019c0a34d..f999803031 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt @@ -1,6 +1,6 @@ package com.tangem.domain.tokens.repository -import com.tangem.domain.tokens.model.Token +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow @@ -11,14 +11,14 @@ interface TokensRepository { suspend fun saveTokens( userWalletId: UserWalletId, - tokens: Set, + currencies: Set, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) - suspend fun getSingleCurrencyWalletToken(userWalletId: UserWalletId): Token + suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency - fun getMultiCurrencyWalletTokens(userWalletId: UserWalletId, refresh: Boolean): Flow> + fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow> fun isTokensGrouped(userWalletId: UserWalletId): Flow diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index 6a666e453b..ab5b194a5b 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -6,7 +6,7 @@ import arrow.core.right import com.tangem.domain.core.error.DataError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.mock.MockTokens -import com.tangem.domain.tokens.model.Token +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.repository.MockTokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider @@ -191,7 +191,7 @@ internal class ApplyTokenListSortingUseCaseTest { private fun getTokensRepository( sortTokensResult: Either = Unit.right(), - tokens: Flow>> = flowOf(MockTokens.tokens.right()), + tokens: Flow>> = flowOf(MockTokens.tokens.right()), ): MockTokensRepository { return MockTokensRepository(sortTokensResult, tokens, emptyFlow(), emptyFlow()) } 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 8cb8237f21..a1ff4474c9 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 @@ -9,10 +9,10 @@ import com.tangem.domain.tokens.mock.MockNetworks import com.tangem.domain.tokens.mock.MockQuotes import com.tangem.domain.tokens.mock.MockTokenLists import com.tangem.domain.tokens.mock.MockTokens +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote -import com.tangem.domain.tokens.model.Token import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository import com.tangem.domain.tokens.repository.MockTokensRepository @@ -224,7 +224,7 @@ internal class GetTokenListUseCaseTest { fun `when tokens is empty then not initialized token list should be received`() = runTest { val expectedResult = MockTokenLists.notInitializedTokenList.right() - val useCase = getUseCase(tokens = flowOf(emptySet().right())) + val useCase = getUseCase(tokens = flowOf(emptySet().right())) // When val result = useCase(userWalletId).first() @@ -318,7 +318,7 @@ internal class GetTokenListUseCaseTest { } private fun getUseCase( - tokens: Flow>> = flowOf(MockTokens.tokens.right()), + tokens: Flow>> = flowOf(MockTokens.tokens.right()), quotes: Flow>> = flowOf(MockQuotes.quotes.right()), networks: Either> = MockNetworks.networks.right(), statuses: Flow>> = flowOf(MockNetworks.errorNetworksStatuses.right()), diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt index 2e0aab633f..ebda181faf 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt @@ -9,22 +9,22 @@ internal object MockNetworksGroups { val networkGroup1 = NetworkGroup( network = MockNetworks.network1, - tokens = MockTokensStates.failedTokenStates - .filter { it.networkId == MockNetworks.network1.id } + currencies = MockTokensStates.failedTokenStates + .filter { it.currency.networkId == MockNetworks.network1.id } .toNonEmptySetOrNull()!!, ) val networkGroup2 = NetworkGroup( network = MockNetworks.network2, - tokens = MockTokensStates.failedTokenStates - .filter { it.networkId == MockNetworks.network2.id } + currencies = MockTokensStates.failedTokenStates + .filter { it.currency.networkId == MockNetworks.network2.id } .toNonEmptySetOrNull()!!, ) val networkGroup3 = NetworkGroup( network = MockNetworks.network3, - tokens = MockTokensStates.failedTokenStates - .filter { it.networkId == MockNetworks.network3.id } + currencies = MockTokensStates.failedTokenStates + .filter { it.currency.networkId == MockNetworks.network3.id } .toNonEmptySetOrNull()!!, ) @@ -32,21 +32,21 @@ internal object MockNetworksGroups { val loadedNetworksGroups = failedNetworksGroups.map { group -> group.copy( - tokens = MockTokensStates.loadedTokensStates - .filter { it.networkId == group.network.id } + currencies = MockTokensStates.loadedTokensStates + .filter { it.currency.networkId == group.network.id } .toNonEmptySetOrNull()!!, ) }.toNonEmptySet() val sortedNetworksGroups = loadedNetworksGroups.map { group -> group.copy( - tokens = group.tokens + currencies = group.currencies .sortedByDescending { it.value.fiatAmount } .toNonEmptySetOrNull()!!, ) } .sortedByDescending { group -> - group.tokens.sumOf { it.value.fiatAmount!! } + group.currencies.sumOf { it.value.fiatAmount!! } } .toNonEmptySetOrNull()!! } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt index 767391423e..d54da733fd 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt @@ -8,61 +8,61 @@ import java.math.BigDecimal internal object MockQuotes { val quote1 = Quote( - tokenId = MockTokens.token1.id, + currencyId = MockTokens.token1.id, fiatRate = BigDecimal("1.23"), priceChange = BigDecimal("0.01"), ) val quote2 = Quote( - tokenId = MockTokens.token2.id, + currencyId = MockTokens.token2.id, fiatRate = BigDecimal("2.34"), priceChange = BigDecimal("-0.02"), ) val quote3 = Quote( - tokenId = MockTokens.token3.id, + currencyId = MockTokens.token3.id, fiatRate = BigDecimal("3.45"), priceChange = BigDecimal("0.03"), ) val quote4 = Quote( - tokenId = MockTokens.token4.id, + currencyId = MockTokens.token4.id, fiatRate = BigDecimal("4.56"), priceChange = BigDecimal("-0.04"), ) val quote5 = Quote( - tokenId = MockTokens.token5.id, + currencyId = MockTokens.token5.id, fiatRate = BigDecimal("5.67"), priceChange = BigDecimal("0.05"), ) val quote6 = Quote( - tokenId = MockTokens.token6.id, + currencyId = MockTokens.token6.id, fiatRate = BigDecimal("6.78"), priceChange = BigDecimal("-0.06"), ) val quote7 = Quote( - tokenId = MockTokens.token7.id, + currencyId = MockTokens.token7.id, fiatRate = BigDecimal("7.89"), priceChange = BigDecimal("0.07"), ) val quote8 = Quote( - tokenId = MockTokens.token8.id, + currencyId = MockTokens.token8.id, fiatRate = BigDecimal("8.90"), priceChange = BigDecimal("-0.08"), ) val quote9 = Quote( - tokenId = MockTokens.token9.id, + currencyId = MockTokens.token9.id, fiatRate = BigDecimal("9.01"), priceChange = BigDecimal("0.09"), ) val quote10 = Quote( - tokenId = MockTokens.token10.id, + currencyId = MockTokens.token10.id, fiatRate = BigDecimal("10.12"), priceChange = BigDecimal("-0.10"), ) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt index 604a2cbffc..403dcb5046 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt @@ -5,8 +5,8 @@ import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.mock.MockNetworksGroups.failedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.loadedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.sortedNetworksGroups +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus import java.math.BigDecimal @Suppress("MemberVisibilityCanBePrivate") @@ -24,7 +24,7 @@ internal object MockTokenLists { ) val emptyUngroupedTokenList = TokenList.Ungrouped( - tokens = emptySet(), + currencies = emptySet(), totalFiatBalance = TokenList.FiatBalance.Failed, sortedBy = TokenList.SortType.NONE, ) @@ -36,14 +36,14 @@ internal object MockTokenLists { ) val failedUngroupedTokenList = TokenList.Ungrouped( - tokens = MockTokensStates.failedTokenStates, + currencies = MockTokensStates.failedTokenStates, totalFiatBalance = TokenList.FiatBalance.Failed, sortedBy = TokenList.SortType.NONE, ) val loadingUngroupedTokenList = with(failedUngroupedTokenList) { copy( - tokens = tokens.map { it.copy(value = TokenStatus.Loading) }.toNonEmptySetOrNull()!!, + currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptySetOrNull()!!, totalFiatBalance = TokenList.FiatBalance.Loading, ) } @@ -53,8 +53,8 @@ internal object MockTokenLists { totalFiatBalance = TokenList.FiatBalance.Loading, groups = groups.map { group -> group.copy( - tokens = group.tokens - .map { it.copy(value = TokenStatus.Loading) } + currencies = group.currencies + .map { it.copy(value = CryptoCurrencyStatus.Loading) } .toNonEmptySetOrNull()!!, ) }.toNonEmptySetOrNull()!!, @@ -66,7 +66,7 @@ internal object MockTokenLists { val tokens = MockTokensStates.loadedTokensStates return failedUngroupedTokenList.copy( - tokens = tokens, + currencies = tokens, sortedBy = TokenList.SortType.NONE, totalFiatBalance = TokenList.FiatBalance.Loaded( amount = tokens.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }, @@ -84,7 +84,7 @@ internal object MockTokenLists { sortedBy = TokenList.SortType.NONE, totalFiatBalance = TokenList.FiatBalance.Loaded( amount = groups - .flatMap { it.tokens as NonEmptySet } + .flatMap { it.currencies as NonEmptySet } .sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }, isAllAmountsSummarized = true, ), @@ -98,7 +98,7 @@ internal object MockTokenLists { .toNonEmptySetOrNull()!! return unsortedUngroupedTokenList.copy( - tokens = tokens, + currencies = tokens, sortedBy = TokenList.SortType.BALANCE, ) } 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 53703985ec..b0af037c5e 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 @@ -1,119 +1,123 @@ package com.tangem.domain.tokens.mock -import com.tangem.domain.tokens.model.Token +import com.tangem.domain.tokens.model.CryptoCurrency internal object MockTokens { - val token1 = Token( - id = Token.ID("token1"), - networkId = MockNetworks.network1.id, - name = "Token 1", - symbol = "T1", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = null, - derivationPath = null, - ) - val token2 = Token( - id = Token.ID("token2"), - networkId = MockNetworks.network1.id, - name = "Token 2", - symbol = "T2", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token3 = Token( - id = Token.ID("token3"), - networkId = MockNetworks.network1.id, - name = "Token 3", - symbol = "T3", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token4 = Token( - id = Token.ID("token4"), - networkId = MockNetworks.network2.id, - name = "Token 4", - symbol = "T4", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = null, - derivationPath = null, - ) - val token5 = Token( - id = Token.ID("token5"), - networkId = MockNetworks.network2.id, - name = "Token 5", - symbol = "T5", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token6 = Token( - id = Token.ID("token6"), - networkId = MockNetworks.network2.id, - name = "Token 6", - symbol = "T6", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token7 = Token( - id = Token.ID("token7"), - networkId = MockNetworks.network3.id, - name = "Token 7", - symbol = "T7", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = null, - derivationPath = null, - ) - val token8 = Token( - id = Token.ID("token8"), - networkId = MockNetworks.network3.id, - name = "Token 8", - symbol = "T8", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token9 = Token( - id = Token.ID("token9"), - networkId = MockNetworks.network3.id, - name = "Token 9", - symbol = "T9", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token10 = Token( - id = Token.ID("token10"), - networkId = MockNetworks.network3.id, - name = "Token 10", - symbol = "T10", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) + 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 = setOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10) } \ No newline at end of file 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 6652b1bd69..4df0a5d525 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 @@ -1,120 +1,60 @@ package com.tangem.domain.tokens.mock import arrow.core.nonEmptySetOf +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.TokenStatus @Suppress("MemberVisibilityCanBePrivate") internal object MockTokensStates { - val tokenState1 = TokenStatus( - id = MockTokens.token1.id, - networkId = MockTokens.token1.networkId, - name = MockTokens.token1.name, - symbol = MockTokens.token1.symbol, - decimals = MockTokens.token1.decimals, - iconUrl = MockTokens.token1.iconUrl, - isCoin = MockTokens.token1.contractAddress == null, - value = TokenStatus.Unreachable, + val tokenState1 = CryptoCurrencyStatus( + currency = MockTokens.token1, + value = CryptoCurrencyStatus.Unreachable, ) - val tokenState2 = TokenStatus( - id = MockTokens.token2.id, - networkId = MockTokens.token2.networkId, - name = MockTokens.token2.name, - symbol = MockTokens.token2.symbol, - decimals = MockTokens.token2.decimals, - iconUrl = MockTokens.token2.iconUrl, - isCoin = MockTokens.token2.contractAddress == null, - value = TokenStatus.Unreachable, + val tokenState2 = CryptoCurrencyStatus( + currency = MockTokens.token2, + value = CryptoCurrencyStatus.Unreachable, ) - val tokenState3 = TokenStatus( - id = MockTokens.token3.id, - networkId = MockTokens.token3.networkId, - name = MockTokens.token3.name, - symbol = MockTokens.token3.symbol, - decimals = MockTokens.token3.decimals, - iconUrl = MockTokens.token3.iconUrl, - isCoin = MockTokens.token3.contractAddress == null, - value = TokenStatus.Unreachable, + val tokenState3 = CryptoCurrencyStatus( + currency = MockTokens.token3, + value = CryptoCurrencyStatus.Unreachable, ) - val tokenState4 = TokenStatus( - id = MockTokens.token4.id, - networkId = MockTokens.token4.networkId, - name = MockTokens.token4.name, - symbol = MockTokens.token4.symbol, - decimals = MockTokens.token4.decimals, - iconUrl = MockTokens.token4.iconUrl, - isCoin = MockTokens.token4.contractAddress == null, - value = TokenStatus.MissedDerivation, + val tokenState4 = CryptoCurrencyStatus( + currency = MockTokens.token4, + value = CryptoCurrencyStatus.MissedDerivation, ) - val tokenState5 = TokenStatus( - id = MockTokens.token5.id, - networkId = MockTokens.token5.networkId, - name = MockTokens.token5.name, - symbol = MockTokens.token5.symbol, - decimals = MockTokens.token5.decimals, - iconUrl = MockTokens.token5.iconUrl, - isCoin = MockTokens.token5.contractAddress == null, - value = TokenStatus.MissedDerivation, + val tokenState5 = CryptoCurrencyStatus( + currency = MockTokens.token5, + value = CryptoCurrencyStatus.MissedDerivation, ) - val tokenState6 = TokenStatus( - id = MockTokens.token6.id, - networkId = MockTokens.token6.networkId, - name = MockTokens.token6.name, - symbol = MockTokens.token6.symbol, - decimals = MockTokens.token6.decimals, - iconUrl = MockTokens.token6.iconUrl, - isCoin = MockTokens.token6.contractAddress == null, - value = TokenStatus.MissedDerivation, + val tokenState6 = CryptoCurrencyStatus( + currency = MockTokens.token6, + value = CryptoCurrencyStatus.MissedDerivation, ) - val tokenState7 = TokenStatus( - id = MockTokens.token7.id, - networkId = MockTokens.token7.networkId, - name = MockTokens.token7.name, - symbol = MockTokens.token7.symbol, - decimals = MockTokens.token7.decimals, - iconUrl = MockTokens.token7.iconUrl, - isCoin = MockTokens.token7.contractAddress == null, - value = TokenStatus.NoAccount, + val tokenState7 = CryptoCurrencyStatus( + currency = MockTokens.token7, + value = CryptoCurrencyStatus.NoAccount, ) - val tokenState8 = TokenStatus( - id = MockTokens.token8.id, - networkId = MockTokens.token8.networkId, - name = MockTokens.token8.name, - symbol = MockTokens.token8.symbol, - decimals = MockTokens.token8.decimals, - iconUrl = MockTokens.token8.iconUrl, - isCoin = MockTokens.token8.contractAddress == null, - value = TokenStatus.NoAccount, + val tokenState8 = CryptoCurrencyStatus( + currency = MockTokens.token8, + value = CryptoCurrencyStatus.NoAccount, ) - val tokenState9 = TokenStatus( - id = MockTokens.token9.id, - networkId = MockTokens.token9.networkId, - name = MockTokens.token9.name, - symbol = MockTokens.token9.symbol, - decimals = MockTokens.token9.decimals, - iconUrl = MockTokens.token9.iconUrl, - isCoin = MockTokens.token9.contractAddress == null, - value = TokenStatus.NoAccount, + val tokenState9 = CryptoCurrencyStatus( + currency = MockTokens.token9, + value = CryptoCurrencyStatus.NoAccount, ) - val tokenState10 = TokenStatus( - id = MockTokens.token10.id, - networkId = MockTokens.token10.networkId, - name = MockTokens.token10.name, - symbol = MockTokens.token10.symbol, - decimals = MockTokens.token10.decimals, - iconUrl = MockTokens.token10.iconUrl, - isCoin = MockTokens.token10.contractAddress == null, - value = TokenStatus.NoAccount, + val tokenState10 = CryptoCurrencyStatus( + currency = MockTokens.token10, + value = CryptoCurrencyStatus.NoAccount, ) val failedTokenStates = nonEmptySetOf( @@ -130,14 +70,15 @@ internal object MockTokensStates { tokenState10, ) - val loadedTokensStates = failedTokenStates.map { state -> - val networkStatus = MockNetworks.verifiedNetworksStatuses.first { it.networkId == state.networkId } - val amount = (networkStatus.value as NetworkStatus.Verified).amounts[state.id]!! - val quote = MockQuotes.quotes.first { it.tokenId == state.id } + val loadedTokensStates = failedTokenStates.map { status -> + val networkStatus = MockNetworks.verifiedNetworksStatuses + .first { it.networkId == status.currency.networkId } + val amount = (networkStatus.value as NetworkStatus.Verified).amounts[status.currency.id]!! + val quote = MockQuotes.quotes.first { it.currencyId == status.currency.id } val fiatAmount = amount * quote.fiatRate - state.copy( - value = TokenStatus.Loaded( + status.copy( + value = CryptoCurrencyStatus.Loaded( amount = amount, fiatAmount = fiatAmount, fiatRate = quote.fiatRate, 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 98c7002327..649cd7808c 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,9 +3,9 @@ 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.Network import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.Token import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -21,7 +21,7 @@ internal class MockNetworksRepository( override fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Map>, 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 fcf52e44e4..9ec660b113 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 @@ -3,8 +3,8 @@ 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.Quote -import com.tangem.domain.tokens.model.Token import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -12,7 +12,7 @@ internal class MockQuotesRepository( private val quotes: Flow>>, ) : QuotesRepository { - override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { + override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { return quotes.map { it.getOrElse { e -> throw e } } } } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt index bffa7f0ac5..8e66695d8f 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt @@ -4,19 +4,19 @@ import arrow.core.Either import arrow.core.getOrElse import com.tangem.domain.core.error.DataError import com.tangem.domain.tokens.mock.MockTokens -import com.tangem.domain.tokens.model.Token +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map internal class MockTokensRepository( private val sortTokensResult: Either, - private val tokens: Flow>>, + private val tokens: Flow>>, private val isGrouped: Flow>, private val isSortedByBalance: Flow>, ) : TokensRepository { - var tokensIdsAfterSortingApply: Set? = null + var tokensIdsAfterSortingApply: Set? = null private set var isTokensGroupedAfterSortingApply: Boolean? = null @@ -27,22 +27,25 @@ internal class MockTokensRepository( override suspend fun saveTokens( userWalletId: UserWalletId, - tokens: Set, + currencies: Set, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) { sortTokensResult.onLeft { throw it } - tokensIdsAfterSortingApply = tokens + tokensIdsAfterSortingApply = currencies isTokensGroupedAfterSortingApply = isGroupedByNetwork isTokensSortedByBalanceAfterSortingApply = isSortedByBalance } - override suspend fun getSingleCurrencyWalletToken(userWalletId: UserWalletId): Token { + override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { return MockTokens.token1 } - override fun getMultiCurrencyWalletTokens(userWalletId: UserWalletId, refresh: Boolean): Flow> { + override fun getMultiCurrencyWalletCurrencies( + userWalletId: UserWalletId, + refresh: Boolean, + ): Flow> { return tokens.map { it.getOrElse { e -> throw e } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt similarity index 63% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenStatusToTokenItemConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index b654b47713..75084dad74 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -3,49 +3,50 @@ package com.tangem.feature.wallet.presentation.wallet.utils import androidx.annotation.DrawableRes import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.domain.tokens.model.TokenStatus +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.utils.converter.Converter import java.math.BigDecimal -internal class TokenStatusToTokenItemConverter( +internal class CryptoCurrencyStatusToTokenItemConverter( private val isWalletContentHidden: Boolean, private val fiatCurrencyCode: String, private val fiatCurrencySymbol: String, -) : Converter { +) : Converter { - private val TokenStatus.networkIconResId: Int? + private val CryptoCurrencyStatus.networkIconResId: Int? @DrawableRes get() { // TODO: [REDACTED_JIRA] - return if (isCoin) null else R.drawable.img_eth_22 + return if (currency is CryptoCurrency.Token) null else R.drawable.img_eth_22 } - private val TokenStatus.tokenIconResId: Int + private val CryptoCurrencyStatus.tokenIconResId: Int @DrawableRes get() { // TODO: [REDACTED_JIRA] return R.drawable.img_eth_22 } - override fun convert(value: TokenStatus): TokenItemState { + override fun convert(value: CryptoCurrencyStatus): TokenItemState { return when (value.value) { - is TokenStatus.Loading -> TokenItemState.Loading - is TokenStatus.Loaded, - is TokenStatus.Custom, + is CryptoCurrencyStatus.Loading -> TokenItemState.Loading + is CryptoCurrencyStatus.Loaded, + is CryptoCurrencyStatus.Custom, -> value.mapToTokenItemState() // TODO: Add other token item states, currently not designed - is TokenStatus.MissedDerivation, - is TokenStatus.NoAccount, - is TokenStatus.Unreachable, + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.NoAccount, + is CryptoCurrencyStatus.Unreachable, -> value.mapToUnreachableTokenItemState() } } - private fun TokenStatus.mapToTokenItemState(): TokenItemState.Content { + private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content { return TokenItemState.Content( - id = this.id.value, - name = this.name, - tokenIconUrl = this.iconUrl, + id = currency.id.value, + name = currency.name, + tokenIconUrl = currency.iconUrl, tokenIconResId = this.tokenIconResId, networkIconResId = this.networkIconResId, amount = getFormattedAmount(), @@ -61,27 +62,27 @@ internal class TokenStatusToTokenItemConverter( ) } - private fun TokenStatus.getFormattedAmount(): String { + private fun CryptoCurrencyStatus.getFormattedAmount(): String { val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN - return BigDecimalFormatter.formatCryptoAmount(amount, symbol, decimals) + return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals) } - private fun TokenStatus.getFormattedFiatAmount(): String { + private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String { val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol) } - private fun TokenStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable( - id = this.id.value, - name = this.name, - tokenIconUrl = this.iconUrl, + private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable( + id = currency.id.value, + name = currency.name, + tokenIconUrl = currency.iconUrl, tokenIconResId = this.tokenIconResId, networkIconResId = this.networkIconResId, ) - private fun TokenStatus.getPriceChangeConfig(): PriceChangeConfig { + private fun CryptoCurrencyStatus.getPriceChangeConfig(): PriceChangeConfig { val priceChange = value.priceChange ?: return PriceChangeConfig(UNKNOWN_AMOUNT_SIGN, PriceChangeConfig.Type.DOWN) 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 f4f1d3803f..574cb11482 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,8 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.utils +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState.TokensListItemState import com.tangem.feature.wallet.presentation.wallet.utils.LoadingItemsProvider.getLoadingMultiCurrencyTokens @@ -19,7 +19,7 @@ internal class TokenListToContentItemsConverter( private val clickCallbacks: WalletClickCallbacks, ) : Converter { - private val tokenStatusConverter = TokenStatusToTokenItemConverter( + private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter( isWalletContentHidden = isWalletContentHidden, fiatCurrencyCode = fiatCurrencyCode, fiatCurrencySymbol = fiatCurrencySymbol, @@ -47,7 +47,7 @@ internal class TokenListToContentItemsConverter( } private fun TokenList.Ungrouped.mapToMultiCurrencyItems(): PersistentList { - return tokens.fold(initial = persistentListOf()) { acc, token -> + return currencies.fold(initial = persistentListOf()) { acc, token -> acc.mutate { it.addToken(token) } } } @@ -55,14 +55,14 @@ internal class TokenListToContentItemsConverter( private fun MutableList.addGroup(group: NetworkGroup): List { this.add(TokensListItemState.NetworkGroupTitle(group.network.name)) - group.tokens.forEach { token -> + group.currencies.forEach { token -> this.addToken(token) } return this } - private fun MutableList.addToken(token: TokenStatus): List { + private fun MutableList.addToken(token: CryptoCurrencyStatus): List { val tokenItemState = tokenStatusConverter.convert(token) this.add(TokensListItemState.Token(tokenItemState)) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index a5bbac817e..1bdd9dd004 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -2,9 +2,9 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels import com.tangem.common.Provider import com.tangem.domain.common.CardTypesResolver +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.tokens.model.TokenStatus import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder @@ -123,12 +123,12 @@ internal class WalletNotificationsListFactory( private fun TokenList.hasMissedDerivations(): Boolean { val statuses = when (this) { - is TokenList.GroupedByNetwork -> groups.flatMap(NetworkGroup::tokens).map(TokenStatus::value) - is TokenList.Ungrouped -> tokens.map(TokenStatus::value) + is TokenList.GroupedByNetwork -> groups.flatMap(NetworkGroup::currencies).map(CryptoCurrencyStatus::value) + is TokenList.Ungrouped -> currencies.map(CryptoCurrencyStatus::value) TokenList.NotInitialized -> emptyList() } - return statuses.any { it is TokenStatus.MissedDerivation } + return statuses.any { it is CryptoCurrencyStatus.MissedDerivation } } private companion object {