From efad059d42cee3be2445fdb5c377b6d8242f6468 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Mar 2023 13:32:47 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/proxy/UserWalletManagerImpl.kt | 10 ++- .../feature/swap/domain/SwapInteractor.kt | 3 +- .../feature/swap/domain/SwapInteractorImpl.kt | 64 ++++++++++++------- .../swap/domain/cache/SwapDataCache.kt | 4 +- .../swap/domain/cache/SwapDataCacheImpl.kt | 14 ++-- .../feature/swap/viewmodels/SwapViewModel.kt | 2 +- .../tangem/lib/crypto/UserWalletManager.kt | 3 +- 7 files changed, 65 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt index a983ea5687..72836cacca 100644 --- a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt @@ -28,7 +28,11 @@ class UserWalletManagerImpl( private val appStateHolder: AppStateHolder, ) : UserWalletManager { - override suspend fun getUserTokens(networkId: String, isExcludeCustom: Boolean): List { + override suspend fun getUserTokens( + networkId: String, + derivationPath: String?, + isExcludeCustom: Boolean, + ): List { val card = appStateHolder.getActualCard() val userTokensRepository = requireNotNull(appStateHolder.userTokensRepository) { "userTokensRepository is null" } @@ -40,7 +44,9 @@ class UserWalletManagerImpl( } else { true } - it.blockchain.toNetworkId() == networkId && checkCustom + it.blockchain.toNetworkId() == networkId && + checkCustom && + it.derivationPath == derivationPath } .map { if (it is com.tangem.tap.features.wallet.models.Currency.Token) { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt index 461f048ca8..0c74003585 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt @@ -94,9 +94,10 @@ interface SwapInteractor { /** * Returns token in wallet balance * + * @param networkId * @param token */ - fun getTokenBalance(token: Currency): SwapAmount + fun getTokenBalance(networkId: String, token: Currency): SwapAmount fun isAvailableToSwap(networkId: String): Boolean } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 98350f3244..f1ef395ed1 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -56,14 +56,18 @@ internal class SwapInteractorImpl @Inject constructor( // replace tokens in wallet tokens list with loaded same val loadedOnWalletsMap = mutableSetOf() - val tokensInWallet = userWalletManager.getUserTokens(networkId = networkId, isExcludeCustom = true) - .filter { it.symbol != initialCurrency.symbol } - .map { token -> - allLoadedTokens.firstOrNull { it.symbol == token.symbol }?.let { - loadedOnWalletsMap.add(it.symbol) - it - } ?: cryptoCurrencyConverter.convertBack(token) - } + val tokensInWallet = + userWalletManager.getUserTokens( + networkId = networkId, + derivationPath = derivationPath, + isExcludeCustom = true, + ).filter { it.symbol != initialCurrency.symbol } + .map { token -> + allLoadedTokens.firstOrNull { it.symbol == token.symbol }?.let { + loadedOnWalletsMap.add(it.symbol) + it + } ?: cryptoCurrencyConverter.convertBack(token) + } val loadedTokens = allLoadedTokens .filter { !loadedOnWalletsMap.contains(it.symbol) @@ -72,7 +76,7 @@ internal class SwapInteractorImpl @Inject constructor( .mapValues { SwapAmount(it.value.value, it.value.decimals) } val appCurrency = userWalletManager.getUserAppCurrency() val rates = repository.getRates(appCurrency.code, tokensInWallet.map { it.id }) - cache.cacheBalances(tokensBalance) + cache.cacheBalances(networkId, derivationPath, tokensBalance) cache.cacheLoadedTokens(loadedTokens.map { TokenWithBalance(it) }) cache.cacheInWalletTokens(getTokensWithBalance(tokensInWallet, tokensBalance, rates, appCurrency)) return TokensDataState( @@ -147,7 +151,7 @@ internal class SwapInteractorImpl @Inject constructor( syncWalletBalanceForTokens(networkId, listOf(fromToken, toToken)) val amountDecimal = toBigDecimalOrNull(amountToSwap) if (amountDecimal == null || amountDecimal.compareTo(BigDecimal.ZERO) == 0) { - return createEmptyAmountState(fromToken, toToken) + return createEmptyAmountState(networkId, fromToken, toToken) } val amount = SwapAmount(amountDecimal, getTokenDecimals(fromToken)) val fromTokenAddress = getTokenAddress(fromToken) @@ -157,7 +161,7 @@ internal class SwapInteractorImpl @Inject constructor( allowPermissionsHandler.removeAddressFromProgress(fromTokenAddress) transactionManager.updateWalletManager(networkId, derivationPath) } - val isBalanceWithoutFeeEnough = isBalanceEnough(fromToken, amount, null) + val isBalanceWithoutFeeEnough = isBalanceEnough(networkId, fromToken, amount, null) return if (isAllowedToSpend && isBalanceWithoutFeeEnough) { loadSwapData( networkId = networkId, @@ -224,8 +228,12 @@ internal class SwapInteractorImpl @Inject constructor( } } - override fun getTokenBalance(token: Currency): SwapAmount { - return cache.getBalanceForToken(token.symbol) ?: SwapAmount(BigDecimal.ZERO, getTokenDecimals(token)) + override fun getTokenBalance(networkId: String, token: Currency): SwapAmount { + return cache.getBalanceForToken( + networkId = networkId, + derivationPath = derivationPath, + symbol = token.symbol, + ) ?: SwapAmount(BigDecimal.ZERO, getTokenDecimals(token)) } override fun isAvailableToSwap(networkId: String): Boolean { @@ -298,12 +306,13 @@ internal class SwapInteractorImpl @Inject constructor( } private fun createEmptyAmountState( + networkId: String, fromToken: Currency, toToken: Currency, ): SwapState { val appCurrency = userWalletManager.getUserAppCurrency() - val fromTokenBalance = cache.getBalanceForToken(fromToken.symbol) - val toTokenBalance = cache.getBalanceForToken(toToken.symbol) + val fromTokenBalance = cache.getBalanceForToken(networkId, derivationPath, fromToken.symbol) + val toTokenBalance = cache.getBalanceForToken(networkId, derivationPath, toToken.symbol) return SwapState.EmptyAmountState( fromTokenWalletBalance = fromTokenBalance?.let { amountFormatter.formatSwapAmountToUI(it, "") }.orEmpty(), toTokenWalletBalance = toTokenBalance?.let { amountFormatter.formatSwapAmountToUI(it, "") }.orEmpty(), @@ -411,7 +420,7 @@ internal class SwapInteractorImpl @Inject constructor( decimals = transactionManager.getNativeTokenDecimals(networkId), currency = userWalletManager.getNetworkCurrency(networkId), ) + feeFiat - val isBalanceIncludeFeeEnough = isBalanceEnough(fromToken, amount, feeData.fee.value) + val isBalanceIncludeFeeEnough = isBalanceEnough(networkId, fromToken, amount, feeData.fee.value) val isFeeEnough = checkFeeIsEnough( fee = feeData.fee.value, spendAmount = amount, @@ -458,8 +467,8 @@ internal class SwapInteractorImpl @Inject constructor( val appCurrency = userWalletManager.getUserAppCurrency() val nativeToken = userWalletManager.getNativeTokenForNetwork(networkId) val rates = repository.getRates(appCurrency.code, listOf(fromToken.id, toToken.id, nativeToken.id)) - val fromTokenBalance = cache.getBalanceForToken(fromToken.symbol) - val toTokenBalance = cache.getBalanceForToken(toToken.symbol) + val fromTokenBalance = cache.getBalanceForToken(networkId, derivationPath, fromToken.symbol) + val toTokenBalance = cache.getBalanceForToken(networkId, derivationPath, toToken.symbol) return SwapState.QuotesLoadedState( fromTokenInfo = TokenSwapInfo( tokenAmount = fromTokenAmount, @@ -503,7 +512,7 @@ internal class SwapInteractorImpl @Inject constructor( quotesLoadedState: SwapState.QuotesLoadedState, ): SwapState.QuotesLoadedState { // if token balance ZERO not show permission state to avoid user to spend money for fee - val isTokenZeroBalance = getTokenBalance(fromToken).value.compareTo(BigDecimal.ZERO) == 0 + val isTokenZeroBalance = getTokenBalance(networkId, fromToken).value.compareTo(BigDecimal.ZERO) == 0 if (isTokenZeroBalance) { return quotesLoadedState.copy( permissionState = PermissionDataState.Empty, @@ -546,7 +555,7 @@ internal class SwapInteractorImpl @Inject constructor( } private suspend fun syncWalletBalanceForTokens(networkId: String, tokens: List) { - val tokensToSync = tokens.filter { cache.getBalanceForToken(it.symbol) == null } + val tokensToSync = tokens.filter { cache.getBalanceForToken(networkId, derivationPath, it.symbol) == null } if (tokensToSync.isNotEmpty()) { val tokensBalance = userWalletManager.getCurrentWalletTokensBalance( @@ -554,12 +563,21 @@ internal class SwapInteractorImpl @Inject constructor( extraTokens = tokensToSync.map { cryptoCurrencyConverter.convert(it) }, derivationPath = derivationPath, ) - cache.cacheBalances(tokensBalance.mapValues { SwapAmount(it.value.value, it.value.decimals) }) + cache.cacheBalances( + networkId = networkId, + derivationPath = derivationPath, + balances = tokensBalance.mapValues { SwapAmount(it.value.value, it.value.decimals) }, + ) } } - private fun isBalanceEnough(fromToken: Currency, amount: SwapAmount, fee: BigDecimal?): Boolean { - val tokenBalance = getTokenBalance(fromToken).value + private fun isBalanceEnough( + networkId: String, + fromToken: Currency, + amount: SwapAmount, + fee: BigDecimal?, + ): Boolean { + val tokenBalance = getTokenBalance(networkId, fromToken).value return if (fromToken is Currency.NonNativeToken) { tokenBalance >= amount.value } else { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt index 36a0ef8ef3..7f4bf919cc 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt @@ -10,11 +10,11 @@ interface SwapDataCache { fun cacheAvailableToSwapTokens(networkId: String, tokens: List) fun cacheInWalletTokens(tokens: List) fun cacheLoadedTokens(tokens: List) - fun cacheBalances(balances: Map) + fun cacheBalances(networkId: String, derivationPath: String?, balances: Map) fun cacheLastFeeForNetwork(fee: BigDecimal, networkId: String) fun getAvailableTokens(networkId: String): List fun getInWalletTokens(): List fun getLoadedTokens(): List - fun getBalanceForToken(symbol: String): SwapAmount? + fun getBalanceForToken(networkId: String, derivationPath: String?, symbol: String): SwapAmount? fun getLastFeeForNetwork(networkId: String): BigDecimal? } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt index f044693ece..4379d70d9f 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt @@ -9,7 +9,7 @@ class SwapDataCacheImpl : SwapDataCache { private val availableTokensForNetwork: MutableMap> = mutableMapOf() private val feesForNetworks: MutableMap = mutableMapOf() - private val tokensBalances: MutableMap = mutableMapOf() + private val tokensBalances: MutableMap> = mutableMapOf() private val lastInWalletTokens = mutableListOf() private val lastLoadedTokens = mutableListOf() @@ -35,12 +35,12 @@ class SwapDataCacheImpl : SwapDataCache { return lastLoadedTokens } - override fun getBalanceForToken(symbol: String): SwapAmount? { - return tokensBalances[symbol] + override fun getBalanceForToken(networkId: String, derivationPath: String?, symbol: String): SwapAmount? { + return tokensBalances[createKeyFrom(networkId, derivationPath)]?.get(symbol) } - override fun cacheBalances(balances: Map) { - tokensBalances.putAll(balances) + override fun cacheBalances(networkId: String, derivationPath: String?, balances: Map) { + tokensBalances[createKeyFrom(networkId, derivationPath)] = balances } override fun cacheAvailableToSwapTokens(networkId: String, tokens: List) { @@ -54,4 +54,8 @@ class SwapDataCacheImpl : SwapDataCache { override fun getAvailableTokens(networkId: String): List { return availableTokensForNetwork.getOrElse(networkId) { emptyList() } } + + private fun createKeyFrom(networkId: String, derivationPath: String?): String { + return "$networkId;$derivationPath" + } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index b18ceab795..e441f3bf63 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -371,7 +371,7 @@ internal class SwapViewModel @Inject constructor( private fun onMaxAmountClicked() { dataState.fromCurrency?.let { - val balance = swapInteractor.getTokenBalance(it) + val balance = swapInteractor.getTokenBalance(currency.networkId, it) onAmountChanged(balance.formatToUIRepresentation()) } } diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt index e9c0d1beea..23659c303f 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt @@ -13,7 +13,7 @@ interface UserWalletManager { * Returns all user tokens (merged from local and backend) */ @Throws(IllegalStateException::class) - suspend fun getUserTokens(networkId: String, isExcludeCustom: Boolean): List + suspend fun getUserTokens(networkId: String, derivationPath: String?, isExcludeCustom: Boolean): List @Throws(IllegalStateException::class) fun getNativeTokenForNetwork(networkId: String): Currency @@ -55,6 +55,7 @@ interface UserWalletManager { * Return balances from wallet found by networkId * * @param networkId + * @param extraTokens tokens you want to check balance that not exists in wallet * @param derivationPath if null uses default * @return map of */