Updated on 2026-08-14
This commit is contained in:
parent
3b63151a79
commit
efad059d42
7 changed files with 65 additions and 35 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]>
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue