Updated on 2026-08-14

This commit is contained in:
Tangem 2023-03-07 13:32:47 +03:00
parent 3b63151a79
commit efad059d42
7 changed files with 65 additions and 35 deletions

View file

@ -28,7 +28,11 @@ class UserWalletManagerImpl(
private val appStateHolder: AppStateHolder,
) : UserWalletManager {
override suspend fun getUserTokens(networkId: String, isExcludeCustom: Boolean): List<Currency> {
override suspend fun getUserTokens(
networkId: String,
derivationPath: String?,
isExcludeCustom: Boolean,
): List<Currency> {
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) {

View file

@ -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
}

View file

@ -56,14 +56,18 @@ internal class SwapInteractorImpl @Inject constructor(
// replace tokens in wallet tokens list with loaded same
val loadedOnWalletsMap = mutableSetOf<String>()
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<Currency>) {
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 {

View file

@ -10,11 +10,11 @@ interface SwapDataCache {
fun cacheAvailableToSwapTokens(networkId: String, tokens: List<Currency>)
fun cacheInWalletTokens(tokens: List<TokenWithBalance>)
fun cacheLoadedTokens(tokens: List<TokenWithBalance>)
fun cacheBalances(balances: Map<String, SwapAmount>)
fun cacheBalances(networkId: String, derivationPath: String?, balances: Map<String, SwapAmount>)
fun cacheLastFeeForNetwork(fee: BigDecimal, networkId: String)
fun getAvailableTokens(networkId: String): List<Currency>
fun getInWalletTokens(): List<TokenWithBalance>
fun getLoadedTokens(): List<TokenWithBalance>
fun getBalanceForToken(symbol: String): SwapAmount?
fun getBalanceForToken(networkId: String, derivationPath: String?, symbol: String): SwapAmount?
fun getLastFeeForNetwork(networkId: String): BigDecimal?
}

View file

@ -9,7 +9,7 @@ class SwapDataCacheImpl : SwapDataCache {
private val availableTokensForNetwork: MutableMap<String, List<Currency>> = mutableMapOf()
private val feesForNetworks: MutableMap<String, BigDecimal> = mutableMapOf()
private val tokensBalances: MutableMap<String, SwapAmount> = mutableMapOf()
private val tokensBalances: MutableMap<String, Map<String, SwapAmount>> = mutableMapOf()
private val lastInWalletTokens = mutableListOf<TokenWithBalance>()
private val lastLoadedTokens = mutableListOf<TokenWithBalance>()
@ -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<String, SwapAmount>) {
tokensBalances.putAll(balances)
override fun cacheBalances(networkId: String, derivationPath: String?, balances: Map<String, SwapAmount>) {
tokensBalances[createKeyFrom(networkId, derivationPath)] = balances
}
override fun cacheAvailableToSwapTokens(networkId: String, tokens: List<Currency>) {
@ -54,4 +54,8 @@ class SwapDataCacheImpl : SwapDataCache {
override fun getAvailableTokens(networkId: String): List<Currency> {
return availableTokensForNetwork.getOrElse(networkId) { emptyList() }
}
private fun createKeyFrom(networkId: String, derivationPath: String?): String {
return "$networkId;$derivationPath"
}
}

View file

@ -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())
}
}

View file

@ -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<Currency>
suspend fun getUserTokens(networkId: String, derivationPath: String?, isExcludeCustom: Boolean): List<Currency>
@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 <Symbol, [ProxyAmount]>
*/