Updated on 2026-08-14
This commit is contained in:
parent
64ed64820b
commit
a6f272223a
18 changed files with 237 additions and 179 deletions
|
|
@ -32,7 +32,7 @@ interface TangemExpressApi {
|
|||
@Query("toContractAddress") toContractAddress: String,
|
||||
@Query("toNetwork") toNetwork: String,
|
||||
@Query("fromAmount") fromAmount: String,
|
||||
@Query("providerId") providerId: Int,
|
||||
@Query("providerId") providerId: String,
|
||||
@Query("rateType") rateType: String,
|
||||
): ApiResponse<ExchangeQuoteResponse>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,19 +4,19 @@ import com.squareup.moshi.Json
|
|||
|
||||
data class ExchangeProvider(
|
||||
@Json(name = "id")
|
||||
val id: Int,
|
||||
val id: String,
|
||||
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
|
||||
@Json(name = "id")
|
||||
@Json(name = "type")
|
||||
val type: ExchangeProviderType,
|
||||
|
||||
@Json(name = "imageLarge")
|
||||
val imageLargeUrl: Int,
|
||||
val imageLargeUrl: String,
|
||||
|
||||
@Json(name = "imageSmall")
|
||||
val imageSmallUrl: Int,
|
||||
val imageSmallUrl: String,
|
||||
)
|
||||
|
||||
enum class ExchangeProviderType {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ data class SwapPair(
|
|||
|
||||
data class SwapPairProvider(
|
||||
@Json(name = "providerId")
|
||||
val providerId: Int,
|
||||
val providerId: String,
|
||||
|
||||
@Json(name = "rateTypes")
|
||||
val rateTypes: List<RateType>,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ internal class SwapRepositoryImpl @Inject constructor(
|
|||
private val leastTokenInfoConverter = LeastTokenInfoConverter()
|
||||
private val swapPairInfoConverter = SwapPairInfoConverter()
|
||||
private val rateTypeConverter = RateTypeConverter()
|
||||
private val swapProviderConverter = SwapProviderConverter()
|
||||
|
||||
override suspend fun getPairs(
|
||||
initialCurrency: LeastTokenInfo,
|
||||
|
|
@ -78,6 +79,18 @@ internal class SwapRepositoryImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getProvidersDetails(providers: Set<SwapProvider>): List<SwapProvider> {
|
||||
val providersMap = providers.associateBy { it.providerId }
|
||||
return tangemExpressApi.getProviders().getOrThrow().mapNotNull {
|
||||
val provider = providersMap[it.id]
|
||||
if (provider != null) {
|
||||
swapProviderConverter.convert(it).copy(rateTypes = provider.rateTypes)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getPairsInternal(
|
||||
from: List<NetworkLeastTokenInfo>,
|
||||
to: List<NetworkLeastTokenInfo>,
|
||||
|
|
@ -133,7 +146,7 @@ internal class SwapRepositoryImpl @Inject constructor(
|
|||
toContractAddress: String,
|
||||
toNetwork: String,
|
||||
fromAmount: String,
|
||||
providerId: Int,
|
||||
providerId: String,
|
||||
rateType: RateType,
|
||||
): AggregatedSwapDataModel<QuoteModel> {
|
||||
return withContext(coroutineDispatcher.io) {
|
||||
|
|
@ -274,7 +287,7 @@ internal class SwapRepositoryImpl @Inject constructor(
|
|||
toContractAddress: String,
|
||||
toNetwork: String,
|
||||
fromAmount: String,
|
||||
providerId: Int,
|
||||
providerId: String,
|
||||
rateType: RateType,
|
||||
): ExchangeQuote {
|
||||
val response = tangemExpressApi.getExchangeQuote(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.datasource.api.express.models.response.ExchangeProvider
|
||||
import com.tangem.datasource.api.express.models.response.ExchangeProviderType
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeProviderType as ExchangeProviderTypeDomain
|
||||
|
||||
class SwapProviderConverter : Converter<ExchangeProvider, SwapProvider> {
|
||||
override fun convert(value: ExchangeProvider): SwapProvider {
|
||||
return SwapProvider(
|
||||
providerId = value.id,
|
||||
name = value.name,
|
||||
type = convertExchangeType(value.type),
|
||||
imageLarge = value.imageLargeUrl,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertExchangeType(type: ExchangeProviderType): ExchangeProviderTypeDomain {
|
||||
return when (type) {
|
||||
ExchangeProviderType.DEX -> ExchangeProviderTypeDomain.DEX
|
||||
ExchangeProviderType.CEX -> ExchangeProviderTypeDomain.CEX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.swap.domain
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
|
|
@ -14,16 +15,6 @@ interface SwapInteractor {
|
|||
|
||||
fun initDerivationPathAndNetwork(derivationPath: String?, network: Network)
|
||||
|
||||
/**
|
||||
* Init tokens to swap, load tokens list available to swap for given network
|
||||
*
|
||||
* @param initialCurrency currency which to swap or receive
|
||||
* @return [TokensDataState] that contains info about all available to swap tokens for networkId
|
||||
* and preselected tokens which initially select to swap
|
||||
*/
|
||||
@Deprecated("method is used in the old swap mechanism")
|
||||
suspend fun initTokensToSwap(initialCurrency: Currency): TokensDataState
|
||||
|
||||
/**
|
||||
* On search token, locally search tokens in previously loaded list to swap
|
||||
* searching in names and symbols
|
||||
|
|
@ -65,8 +56,8 @@ interface SwapInteractor {
|
|||
@Throws(IllegalStateException::class)
|
||||
suspend fun findBestQuote(
|
||||
networkId: String,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
providers: List<SwapProvider>,
|
||||
amountToSwap: String,
|
||||
selectedFee: FeeType = FeeType.NORMAL,
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ import com.tangem.lib.crypto.UserWalletManager
|
|||
import com.tangem.lib.crypto.models.*
|
||||
import com.tangem.lib.crypto.models.transactions.SendTxResult
|
||||
import com.tangem.utils.toFiatString
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
internal class SwapInteractorImpl @Inject constructor(
|
||||
|
|
@ -150,7 +150,16 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
initialCurrency: LeastTokenInfo,
|
||||
currenciesList: List<CryptoCurrency>,
|
||||
): List<SwapPairLeast> {
|
||||
return repository.getPairs(initialCurrency, currenciesList)
|
||||
val pairs = repository.getPairs(initialCurrency, currenciesList)
|
||||
val providers = pairs.flatMap { it.providers }.toSet()
|
||||
val updatedProviders = repository.getProvidersDetails(providers).associateBy { it.providerId }
|
||||
return pairs.map { pair ->
|
||||
pair.copy(
|
||||
providers = pair.providers.mapNotNull { currentProvider ->
|
||||
updatedProviders[currentProvider.providerId]
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("used in old swap mechanism")
|
||||
|
|
@ -159,54 +168,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
this.network = network
|
||||
}
|
||||
|
||||
@Deprecated("used in old swap mechanism")
|
||||
override suspend fun initTokensToSwap(initialCurrency: Currency): TokensDataState {
|
||||
// TODO: refactor this function
|
||||
val networkId = initialCurrency.networkId
|
||||
val availableTokens = cache.getAvailableTokens(networkId)
|
||||
val allLoadedTokens = availableTokens.ifEmpty {
|
||||
val tokens = repository.getExchangeableTokens(networkId)
|
||||
cache.cacheAvailableToSwapTokens(networkId, tokens)
|
||||
tokens
|
||||
}.filter { it.symbol != initialCurrency.symbol }
|
||||
|
||||
// replace tokens in wallet tokens list with loaded same
|
||||
val loadedOnWalletsMap = mutableSetOf<String>()
|
||||
val tokensInWallet = userWalletManager.getUserTokens(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
isExcludeCustom = true,
|
||||
)
|
||||
.map { token ->
|
||||
allLoadedTokens.firstOrNull { it.symbol == token.symbol }?.let {
|
||||
loadedOnWalletsMap.add(it.symbol)
|
||||
it
|
||||
} ?: TODO()
|
||||
}
|
||||
.filter { it.symbol != initialCurrency.symbol && allLoadedTokens.contains(it) }
|
||||
val loadedTokens = allLoadedTokens
|
||||
.filter {
|
||||
!loadedOnWalletsMap.contains(it.symbol)
|
||||
}
|
||||
val tokensBalance = userWalletManager.getCurrentWalletTokensBalance(networkId, emptyList(), derivationPath)
|
||||
.mapValues { SwapAmount(it.value.value, it.value.decimals) }
|
||||
val appCurrency = userWalletManager.getUserAppCurrency()
|
||||
val rates = repository.getRates(appCurrency.code, tokensInWallet.map { it.id })
|
||||
cache.cacheBalances(networkId, derivationPath, tokensBalance)
|
||||
// cache.cacheLoadedTokens(loadedTokens.map { TokenWithBalance(it) })
|
||||
// cache.cacheInWalletTokens(getTokensWithBalance(tokensInWallet, tokensBalance, rates, appCurrency))
|
||||
return TokensDataState(
|
||||
preselectTokens = PreselectTokens(
|
||||
fromToken = initialCurrency,
|
||||
toToken = selectToToken(initialCurrency, tokensInWallet, loadedTokens),
|
||||
),
|
||||
foundTokensState = FoundTokensState(
|
||||
tokensInWallet = emptyList(), // cache.getInWalletTokens(),
|
||||
loadedTokens = emptyList(), // cache.getLoadedTokens(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("used in old swap mechanism")
|
||||
override suspend fun searchTokens(networkId: String, searchQuery: String): FoundTokensStateExpress {
|
||||
val searchQueryLowerCase = searchQuery.lowercase()
|
||||
|
|
@ -276,26 +237,26 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
@Deprecated("used in old swap mechanism")
|
||||
override suspend fun findBestQuote(
|
||||
networkId: String,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
providers: List<SwapProvider>,
|
||||
amountToSwap: String,
|
||||
selectedFee: FeeType,
|
||||
): Map<SwapProvider, SwapState> {
|
||||
syncWalletBalanceForTokens(networkId, listOf(fromToken, toToken))
|
||||
syncWalletBalanceForTokens(networkId, listOf(fromToken.currency, toToken.currency))
|
||||
val amountDecimal = toBigDecimalOrNull(amountToSwap)
|
||||
if (amountDecimal == null || amountDecimal.signum() == 0) {
|
||||
return providers.associateWith { createEmptyAmountState(networkId, fromToken, toToken) }
|
||||
return providers.associateWith { createEmptyAmountState(networkId, fromToken.currency, toToken.currency) }
|
||||
}
|
||||
val amount = SwapAmount(amountDecimal, getTokenDecimals(fromToken))
|
||||
val fromTokenAddress = getTokenAddress(fromToken)
|
||||
val toTokenAddress = getTokenAddress(toToken)
|
||||
val isAllowedToSpend = isAllowedToSpend(networkId, fromToken, amount)
|
||||
val amount = SwapAmount(amountDecimal, getTokenDecimals(fromToken.currency))
|
||||
val fromTokenAddress = getTokenAddress(fromToken.currency)
|
||||
val toTokenAddress = getTokenAddress(toToken.currency)
|
||||
val isAllowedToSpend = isAllowedToSpend(networkId, fromToken.currency, amount)
|
||||
if (isAllowedToSpend && allowPermissionsHandler.isAddressAllowanceInProgress(fromTokenAddress)) {
|
||||
allowPermissionsHandler.removeAddressFromProgress(fromTokenAddress)
|
||||
transactionManager.updateWalletManager(networkId, derivationPath)
|
||||
}
|
||||
val isBalanceWithoutFeeEnough = isBalanceEnough(networkId, fromToken, amount, null)
|
||||
val isBalanceWithoutFeeEnough = isBalanceEnough(networkId, fromToken.currency, amount, null)
|
||||
return if (isAllowedToSpend && isBalanceWithoutFeeEnough) {
|
||||
// TODO
|
||||
providers.associateWith {
|
||||
|
|
@ -313,8 +274,8 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
loadQuoteData(
|
||||
networkId = networkId,
|
||||
amount = amount,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
fromTokenStatus = fromToken,
|
||||
toTokenStatus = toToken,
|
||||
isAllowedToSpend = isAllowedToSpend,
|
||||
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
|
||||
providers = providers,
|
||||
|
|
@ -521,12 +482,14 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
private suspend fun loadQuoteData(
|
||||
networkId: String,
|
||||
amount: SwapAmount,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromTokenStatus: CryptoCurrencyStatus,
|
||||
toTokenStatus: CryptoCurrencyStatus,
|
||||
providers: List<SwapProvider>,
|
||||
isAllowedToSpend: Boolean,
|
||||
isBalanceWithoutFeeEnough: Boolean,
|
||||
): Map<SwapProvider, SwapState> {
|
||||
val fromToken = fromTokenStatus.currency
|
||||
val toToken = toTokenStatus.currency
|
||||
return coroutineScope {
|
||||
val quoteRequests = providers.map { provider ->
|
||||
async {
|
||||
|
|
@ -547,11 +510,11 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
getState(
|
||||
quoteDataModel = it.second,
|
||||
amount = amount,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
fromToken = fromTokenStatus,
|
||||
toToken = toTokenStatus,
|
||||
networkId = networkId,
|
||||
isAllowedToSpend = isAllowedToSpend,
|
||||
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough
|
||||
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
|
||||
)
|
||||
}.associate { it.first to it.second }
|
||||
}
|
||||
|
|
@ -560,8 +523,8 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
private suspend fun getState(
|
||||
quoteDataModel: AggregatedSwapDataModel<QuoteModel>,
|
||||
amount: SwapAmount,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
networkId: String,
|
||||
isAllowedToSpend: Boolean,
|
||||
isBalanceWithoutFeeEnough: Boolean,
|
||||
|
|
@ -570,15 +533,15 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
if (quoteModel != null) {
|
||||
val swapState = updateBalances(
|
||||
networkId = networkId,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
fromTokenStatus = fromToken,
|
||||
toTokenStatus = toToken,
|
||||
fromTokenAmount = amount,
|
||||
toTokenAmount = quoteModel.toTokenAmount,
|
||||
swapStateData = null,
|
||||
)
|
||||
val quotesState = updatePermissionState(
|
||||
networkId = networkId,
|
||||
fromToken = fromToken,
|
||||
fromToken = fromToken.currency,
|
||||
swapAmount = amount,
|
||||
quotesLoadedState = swapState,
|
||||
)
|
||||
|
|
@ -612,8 +575,8 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
networkId: String,
|
||||
fromTokenAddress: String,
|
||||
toTokenAddress: String,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
amount: SwapAmount,
|
||||
selectedFee: FeeType,
|
||||
): SwapState {
|
||||
|
|
@ -630,7 +593,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
val feeData = transactionManager.getFee(
|
||||
networkId = networkId,
|
||||
amountToSend = amount.value,
|
||||
currencyToSend = swapCurrencyConverter.convert(fromToken),
|
||||
currencyToSend = swapCurrencyConverter.convert(fromToken.currency),
|
||||
destinationAddress = swapData.transaction.toWalletAddress,
|
||||
increaseBy = INCREASE_GAS_LIMIT_BY,
|
||||
data = swapData.transaction.data,
|
||||
|
|
@ -642,17 +605,17 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
FeeType.PRIORITY -> txFeeState.priorityFee.feeValue
|
||||
}
|
||||
val isBalanceIncludeFeeEnough =
|
||||
isBalanceEnough(networkId, fromToken, amount, feeByPriority)
|
||||
isBalanceEnough(networkId, fromToken.currency, amount, feeByPriority)
|
||||
val isFeeEnough = checkFeeIsEnough(
|
||||
fee = feeByPriority,
|
||||
spendAmount = amount,
|
||||
networkId = networkId,
|
||||
fromToken = fromToken,
|
||||
fromToken = fromToken.currency,
|
||||
)
|
||||
val swapState = updateBalances(
|
||||
networkId = networkId,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
fromTokenStatus = fromToken,
|
||||
toTokenStatus = toToken,
|
||||
fromTokenAmount = amount,
|
||||
toTokenAmount = swapData.toTokenAmount,
|
||||
swapStateData = SwapStateData(
|
||||
|
|
@ -677,42 +640,32 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
@Suppress("LongParameterList")
|
||||
private suspend fun updateBalances(
|
||||
networkId: String,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromTokenStatus: CryptoCurrencyStatus,
|
||||
toTokenStatus: CryptoCurrencyStatus,
|
||||
fromTokenAmount: SwapAmount,
|
||||
toTokenAmount: SwapAmount,
|
||||
swapStateData: SwapStateData?,
|
||||
): SwapState.QuotesLoadedState {
|
||||
val fromToken = fromTokenStatus.currency
|
||||
val toToken = toTokenStatus.currency
|
||||
val appCurrency = userWalletManager.getUserAppCurrency()
|
||||
val nativeToken = userWalletManager.getNativeTokenForNetwork(networkId)
|
||||
val rates = repository.getRates(
|
||||
appCurrency.code,
|
||||
listOf(fromToken.network.backendId, toToken.network.backendId, nativeToken.id),
|
||||
)
|
||||
val fromTokenBalance = cache.getBalanceForToken(networkId, derivationPath, fromToken.symbol)
|
||||
val toTokenBalance = cache.getBalanceForToken(networkId, derivationPath, toToken.symbol)
|
||||
return SwapState.QuotesLoadedState(
|
||||
fromTokenInfo = TokenSwapInfo(
|
||||
tokenAmount = fromTokenAmount,
|
||||
coinId = fromToken.id.value,
|
||||
tokenWalletBalance = fromTokenBalance?.let { amountFormatter.formatSwapAmountToUI(it, "") }
|
||||
?: ZERO_BALANCE,
|
||||
tokenFiatBalance = fromTokenAmount.value.toFiatString(
|
||||
rateValue = rates[fromToken.network.backendId]?.toBigDecimal() ?: BigDecimal.ZERO,
|
||||
fiatCurrencyName = appCurrency.symbol,
|
||||
formatWithSpaces = true,
|
||||
),
|
||||
cryptoCurrencyStatus = fromTokenStatus,
|
||||
amountFiat = rates[fromToken.network.backendId]?.toBigDecimal()?.multiply(fromTokenAmount.value)
|
||||
?: BigDecimal.ZERO,
|
||||
),
|
||||
toTokenInfo = TokenSwapInfo(
|
||||
tokenAmount = toTokenAmount,
|
||||
coinId = toToken.id.value,
|
||||
tokenWalletBalance = toTokenBalance?.let { amountFormatter.formatSwapAmountToUI(it, "") }
|
||||
?: ZERO_BALANCE,
|
||||
tokenFiatBalance = toTokenAmount.value.toFiatString(
|
||||
rateValue = rates[toToken.network.backendId]?.toBigDecimal() ?: BigDecimal.ZERO,
|
||||
fiatCurrencyName = appCurrency.symbol,
|
||||
formatWithSpaces = true,
|
||||
),
|
||||
cryptoCurrencyStatus = toTokenStatus,
|
||||
amountFiat = rates[toToken.network.backendId]?.toBigDecimal()?.multiply(toTokenAmount.value)
|
||||
?: BigDecimal.ZERO,
|
||||
),
|
||||
priceImpact = calculatePriceImpact(
|
||||
fromTokenAmount = fromTokenAmount.value,
|
||||
|
|
@ -933,7 +886,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
|
||||
companion object {
|
||||
private const val DEFAULT_SLIPPAGE = 2
|
||||
private const val ZERO_BALANCE = "0"
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.0 // if need to increase fee when check isEnough
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ interface SwapRepository {
|
|||
|
||||
suspend fun getPairs(initialCurrency: LeastTokenInfo, currencyList: List<CryptoCurrency>): List<SwapPairLeast>
|
||||
|
||||
suspend fun getProvidersDetails(providers: Set<SwapProvider>): List<SwapProvider>
|
||||
|
||||
suspend fun getRates(currencyId: String, tokenIds: List<String>): Map<String, Double>
|
||||
|
||||
suspend fun getExchangeableTokens(networkId: String): List<Currency>
|
||||
|
|
@ -21,7 +23,7 @@ interface SwapRepository {
|
|||
toContractAddress: String,
|
||||
toNetwork: String,
|
||||
fromAmount: String,
|
||||
providerId: Int,
|
||||
providerId: String,
|
||||
rateType: RateType,
|
||||
): AggregatedSwapDataModel<QuoteModel>
|
||||
|
||||
|
|
@ -73,7 +75,7 @@ interface SwapRepository {
|
|||
toContractAddress: String,
|
||||
toNetwork: String,
|
||||
fromAmount: String,
|
||||
providerId: Int,
|
||||
providerId: String,
|
||||
rateType: RateType,
|
||||
): ExchangeQuote
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.feature.swap.domain.di
|
||||
|
||||
import com.tangem.domain.tokens.GetCardTokensListUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
|
|
|
|||
|
|
@ -27,10 +27,18 @@ data class CryptoCurrencySwapInfo(
|
|||
* @property rateTypes supported rate types
|
||||
*/
|
||||
data class SwapProvider(
|
||||
val providerId: Int,
|
||||
val rateTypes: List<RateType>,
|
||||
val providerId: String,
|
||||
val rateTypes: List<RateType> = emptyList(),
|
||||
val name: String? = null,
|
||||
val type: ExchangeProviderType? = null,
|
||||
val imageLarge: String? = null,
|
||||
)
|
||||
|
||||
enum class ExchangeProviderType {
|
||||
DEX,
|
||||
CEX,
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate type.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.swap.domain.models.ui
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.swap.domain.models.DataError
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
import com.tangem.feature.swap.domain.models.domain.PreparedSwapConfigState
|
||||
|
|
@ -51,9 +52,8 @@ sealed class PermissionDataState {
|
|||
|
||||
data class TokenSwapInfo(
|
||||
val tokenAmount: SwapAmount,
|
||||
val coinId: String,
|
||||
val tokenWalletBalance: String,
|
||||
val tokenFiatBalance: String,
|
||||
val amountFiat: BigDecimal,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
)
|
||||
|
||||
data class RequestApproveStateData(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.ui.components.notifications.NotificationConfig
|
|||
import com.tangem.core.ui.components.states.Item
|
||||
import com.tangem.core.ui.components.states.SelectableItemsState
|
||||
import com.tangem.feature.swap.domain.models.ui.TxFee
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
|
||||
data class SwapStateHolder(
|
||||
val sendCardData: SwapCardState,
|
||||
|
|
@ -16,6 +17,7 @@ data class SwapStateHolder(
|
|||
val warnings: List<SwapWarning> = emptyList(),
|
||||
val alert: SwapWarning.GenericWarning? = null,
|
||||
val updateInProgress: Boolean = false,
|
||||
val providerState: ProviderState,
|
||||
|
||||
val permissionState: SwapPermissionState = SwapPermissionState.Empty,
|
||||
val successState: SwapSuccessStateHolder? = null,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ sealed class ProviderState {
|
|||
abstract val onProviderClick: ((String) -> Unit)?
|
||||
abstract val id: String
|
||||
|
||||
data class Empty(
|
||||
override val id: String = "",
|
||||
override val onProviderClick: ((String) -> Unit)? = null,
|
||||
) : ProviderState()
|
||||
|
||||
data class Loading(
|
||||
override val id: String = "",
|
||||
override val onProviderClick: ((String) -> Unit)? = null,
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ private val GrayscaleColorFilter: ColorFilter
|
|||
|
||||
@Composable
|
||||
fun ProviderItemBlock(state: ProviderState) {
|
||||
BaseContainer(state) {
|
||||
ProviderItem(
|
||||
state = state,
|
||||
modifier = Modifier.align(Alignment.CenterStart),
|
||||
)
|
||||
if (state !is ProviderState.Empty) {
|
||||
BaseContainer(state) {
|
||||
ProviderItem(
|
||||
state = state,
|
||||
modifier = Modifier.align(Alignment.CenterStart),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +67,9 @@ fun ProviderItem(state: ProviderState, modifier: Modifier = Modifier, isSelected
|
|||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is ProviderState.Empty -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.feature.swap.converters.TokensDataConverter
|
||||
import com.tangem.feature.swap.domain.models.DataError
|
||||
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.swap.domain.models.formatToUIRepresentation
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* State builder creates a specific states for SwapScreen
|
||||
|
|
@ -51,7 +54,7 @@ internal class StateBuilder(
|
|||
amountTextFieldValue = null,
|
||||
tokenIconUrl = initialCurrency.iconUrl,
|
||||
tokenCurrency = initialCurrency.symbol,
|
||||
coinId = null,
|
||||
coinId = initialCurrency.network.backendId,
|
||||
canSelectAnotherToken = false,
|
||||
isNotNativeToken = initialCurrency is CryptoCurrency.Token,
|
||||
balance = "",
|
||||
|
|
@ -79,6 +82,7 @@ internal class StateBuilder(
|
|||
updateInProgress = true,
|
||||
onShowPermissionBottomSheet = actions.openPermissionBottomSheet,
|
||||
onCancelPermissionBottomSheet = actions.hidePermissionBottomSheet,
|
||||
providerState = ProviderState.Loading(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +150,7 @@ internal class StateBuilder(
|
|||
amountEquivalent = null,
|
||||
tokenIconUrl = fromToken.iconUrl,
|
||||
tokenCurrency = fromToken.symbol,
|
||||
coinId = fromToken.id.value,
|
||||
coinId = fromToken.network.backendId,
|
||||
isNotNativeToken = fromToken is CryptoCurrency.Token,
|
||||
canSelectAnotherToken = canSelectSendToken,
|
||||
balance = if (!canSelectSendToken) uiStateHolder.sendCardData.balance else "",
|
||||
|
|
@ -158,7 +162,7 @@ internal class StateBuilder(
|
|||
amountEquivalent = null,
|
||||
tokenIconUrl = toToken.iconUrl,
|
||||
tokenCurrency = toToken.symbol,
|
||||
coinId = toToken.id.value,
|
||||
coinId = toToken.network.backendId,
|
||||
isNotNativeToken = toToken is CryptoCurrency.Token,
|
||||
canSelectAnotherToken = canSelectReceiveToken,
|
||||
balance = if (!canSelectReceiveToken) uiStateHolder.receiveCardData.balance else "",
|
||||
|
|
@ -185,6 +189,7 @@ internal class StateBuilder(
|
|||
uiStateHolder: SwapStateHolder,
|
||||
quoteModel: SwapState.QuotesLoadedState,
|
||||
fromToken: CryptoCurrency,
|
||||
swapProvider: SwapProvider,
|
||||
onFeeSetup: (TxFee) -> Unit,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
|
|
@ -213,29 +218,31 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
val feeState = createFeeState(quoteModel, uiStateHolder, onFeeSetup)
|
||||
val fromCurrencyStatus = quoteModel.fromTokenInfo.cryptoCurrencyStatus
|
||||
val toCurrencyStatus = quoteModel.toTokenInfo.cryptoCurrencyStatus
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard),
|
||||
amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue,
|
||||
amountEquivalent = quoteModel.fromTokenInfo.tokenFiatBalance,
|
||||
amountEquivalent = fromCurrencyStatus.getFormattedAmount(quoteModel.fromTokenInfo.amountFiat),
|
||||
tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl,
|
||||
coinId = quoteModel.fromTokenInfo.coinId,
|
||||
coinId = fromCurrencyStatus.currency.network.backendId,
|
||||
isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken,
|
||||
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
|
||||
balance = quoteModel.fromTokenInfo.tokenWalletBalance,
|
||||
balance = fromCurrencyStatus.getFormattedAmount(),
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountTextFieldValue = TextFieldValue(quoteModel.toTokenInfo.tokenAmount.formatToUIRepresentation()),
|
||||
amountEquivalent = quoteModel.toTokenInfo.tokenFiatBalance,
|
||||
amountEquivalent = toCurrencyStatus.getFormattedAmount(quoteModel.toTokenInfo.amountFiat),
|
||||
tokenIconUrl = uiStateHolder.receiveCardData.tokenIconUrl,
|
||||
coinId = quoteModel.toTokenInfo.coinId,
|
||||
coinId = toCurrencyStatus.currency.network.backendId,
|
||||
isNotNativeToken = uiStateHolder.receiveCardData.isNotNativeToken,
|
||||
tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken,
|
||||
balance = quoteModel.toTokenInfo.tokenWalletBalance,
|
||||
balance = toCurrencyStatus.getFormattedAmount(),
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
networkCurrency = quoteModel.networkCurrency,
|
||||
|
|
@ -256,6 +263,10 @@ internal class StateBuilder(
|
|||
onClick = actions.onSwapClick,
|
||||
),
|
||||
updateInProgress = false,
|
||||
providerState = swapProvider.convertToContentClickableProviderState(
|
||||
fromTokenInfo = quoteModel.fromTokenInfo,
|
||||
toTokenInfo = quoteModel.toTokenInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -298,6 +309,7 @@ internal class StateBuilder(
|
|||
onClick = { },
|
||||
),
|
||||
updateInProgress = false,
|
||||
providerState = ProviderState.Empty(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -726,12 +738,37 @@ internal class StateBuilder(
|
|||
return "$firstAddressPart...$secondAddressPart"
|
||||
}
|
||||
|
||||
private fun SwapProvider.convertToContentClickableProviderState(
|
||||
fromTokenInfo: TokenSwapInfo,
|
||||
toTokenInfo: TokenSwapInfo,
|
||||
): ProviderState {
|
||||
val rate = fromTokenInfo.tokenAmount.value / toTokenInfo.tokenAmount.value
|
||||
val fromCurrencySymbol = fromTokenInfo.cryptoCurrencyStatus.currency.symbol
|
||||
val toCurrencySymbol = toTokenInfo.cryptoCurrencyStatus.currency.symbol
|
||||
val rateString = "1 $fromCurrencySymbol ≈ $rate $toCurrencySymbol"
|
||||
return ProviderState.Content(
|
||||
id = this.providerId,
|
||||
name = this.name ?: "",
|
||||
iconUrl = this.imageLarge ?: "",
|
||||
type = this.type.toString(),
|
||||
rate = rateString,
|
||||
additionalBadge = ProviderState.AdditionalBadge.BestTrade,
|
||||
selectionType = ProviderState.SelectionType.CLICK,
|
||||
percentLowerThenBest = null,
|
||||
onProviderClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(amount: BigDecimal): String {
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.feature.swap.domain.models.ui.FeeType
|
||||
import com.tangem.feature.swap.domain.models.ui.TxFee
|
||||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -77,6 +78,8 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
) {
|
||||
MainInfo(state)
|
||||
|
||||
ProviderItemBlock(state = state.providerState)
|
||||
|
||||
FeeItem(feeState = state.fee, currency = state.networkCurrency)
|
||||
|
||||
if (state.warnings.isNotEmpty()) SwapWarnings(warnings = state.warnings)
|
||||
|
|
@ -476,6 +479,7 @@ private val state = SwapStateHolder(
|
|||
onChangeCardsClicked = {},
|
||||
permissionState = SwapPermissionState.InProgress,
|
||||
blockchainId = "POLYGON",
|
||||
providerState = ProviderState.Loading(),
|
||||
)
|
||||
|
||||
@Preview
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ package com.tangem.feature.swap.viewmodels
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.ui.RequestApproveStateData
|
||||
import com.tangem.feature.swap.domain.models.ui.SwapStateData
|
||||
import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress
|
||||
import com.tangem.feature.swap.domain.models.ui.TxFee
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
|
||||
data class SwapProcessDataState(
|
||||
// Initial network id
|
||||
|
|
@ -22,4 +20,6 @@ data class SwapProcessDataState(
|
|||
val swapDataModel: SwapStateData? = null,
|
||||
val selectedFee: TxFee? = null,
|
||||
val tokensDataState: TokensDataStateExpress? = null,
|
||||
val selectedProvider: SwapProvider? = null,
|
||||
val lastLoadedSwapStates: Map<SwapProvider, SwapState> = emptyMap(),
|
||||
)
|
||||
|
|
@ -18,7 +18,6 @@ import com.tangem.feature.swap.analytics.SwapEvents
|
|||
import com.tangem.feature.swap.domain.BlockchainInteractor
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.feature.swap.domain.models.domain.PermissionOptions
|
||||
import com.tangem.feature.swap.domain.models.domain.RateType
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.swap.domain.models.formatToUIRepresentation
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
|
|
@ -74,7 +73,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
private val inputNumberFormatter =
|
||||
InputNumberFormatter(NumberFormat.getInstance(Locale.getDefault()) as DecimalFormat)
|
||||
private val amountDebouncer = Debouncer()
|
||||
private val singleTaskScheduler = SingleTaskScheduler<SwapState>()
|
||||
private val singleTaskScheduler = SingleTaskScheduler<Map<SwapProvider, SwapState>>()
|
||||
|
||||
private var dataState by mutableStateOf(SwapProcessDataState(networkId = initialCryptoCurrency.network.backendId))
|
||||
|
||||
|
|
@ -174,10 +173,10 @@ internal class SwapViewModel @Inject constructor(
|
|||
)
|
||||
} else {
|
||||
startLoadingQuotes(
|
||||
fromToken = fromCurrencyStatus.currency,
|
||||
toToken = selectedCurrency.currency,
|
||||
fromToken = fromCurrencyStatus,
|
||||
toToken = selectedCurrency,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(fromCurrencyStatus, selectedCurrency)
|
||||
toProvidersList = findSwapProviders(fromCurrencyStatus, selectedCurrency),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -196,13 +195,18 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun startLoadingQuotes(
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
amount: String,
|
||||
toProvidersList: List<SwapProvider>,
|
||||
) {
|
||||
singleTaskScheduler.cancelTask()
|
||||
uiState = stateBuilder.createQuotesLoadingState(uiState, fromToken, toToken, initialCryptoCurrency.id.value)
|
||||
uiState = stateBuilder.createQuotesLoadingState(
|
||||
uiState,
|
||||
fromToken.currency,
|
||||
toToken.currency,
|
||||
initialCryptoCurrency.id.value,
|
||||
)
|
||||
singleTaskScheduler.scheduleTask(
|
||||
viewModelScope,
|
||||
loadQuotesTask(
|
||||
|
|
@ -220,20 +224,20 @@ internal class SwapViewModel @Inject constructor(
|
|||
val amount = dataState.amount
|
||||
if (fromCurrency != null && toCurrency != null && amount != null) {
|
||||
startLoadingQuotes(
|
||||
fromToken = fromCurrency.currency,
|
||||
toToken = toCurrency.currency,
|
||||
fromToken = fromCurrency,
|
||||
toToken = toCurrency,
|
||||
amount = amount,
|
||||
toProvidersList = findSwapProviders(fromCurrency, toCurrency)
|
||||
toProvidersList = findSwapProviders(fromCurrency, toCurrency),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadQuotesTask(
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
amount: String,
|
||||
toProvidersList: List<SwapProvider>,
|
||||
): PeriodicTask<SwapState> {
|
||||
): PeriodicTask<Map<SwapProvider, SwapState>> {
|
||||
return PeriodicTask(
|
||||
UPDATE_DELAY,
|
||||
task = {
|
||||
|
|
@ -251,17 +255,19 @@ internal class SwapViewModel @Inject constructor(
|
|||
providers = toProvidersList,
|
||||
amountToSwap = amount,
|
||||
selectedFee = dataState.selectedFee?.feeType ?: FeeType.NORMAL,
|
||||
).entries.first().value// TODO
|
||||
)
|
||||
}
|
||||
},
|
||||
onSuccess = { swapState ->
|
||||
when (swapState) {
|
||||
onSuccess = { providersState ->
|
||||
val (provider, state) = updateLoadedQuotes(providersState)
|
||||
when (state) {
|
||||
is SwapState.QuotesLoadedState -> {
|
||||
fillDataState(swapState.permissionState, swapState.swapDataModel)
|
||||
fillDataState(state.permissionState, state.swapDataModel)
|
||||
uiState = stateBuilder.createQuotesLoadedState(
|
||||
uiStateHolder = uiState,
|
||||
quoteModel = swapState,
|
||||
fromToken = fromToken,
|
||||
quoteModel = state,
|
||||
fromToken = fromToken.currency,
|
||||
swapProvider = provider,
|
||||
) { updatedFee ->
|
||||
dataState = dataState.copy(
|
||||
selectedFee = updatedFee,
|
||||
|
|
@ -271,12 +277,12 @@ internal class SwapViewModel @Inject constructor(
|
|||
is SwapState.EmptyAmountState -> {
|
||||
uiState = stateBuilder.createQuotesEmptyAmountState(
|
||||
uiStateHolder = uiState,
|
||||
emptyAmountState = swapState,
|
||||
emptyAmountState = state,
|
||||
)
|
||||
}
|
||||
is SwapState.SwapError -> {
|
||||
Timber.e("SwapError when loading quotes ${swapState.error}")
|
||||
uiState = stateBuilder.mapError(uiState, swapState.error) { startLoadingQuotesFromLastState() }
|
||||
Timber.e("SwapError when loading quotes ${state.error}")
|
||||
uiState = stateBuilder.mapError(uiState, state.error) { startLoadingQuotesFromLastState() }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -287,6 +293,15 @@ internal class SwapViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun updateLoadedQuotes(state: Map<SwapProvider, SwapState>): Pair<SwapProvider, SwapState> {
|
||||
val selectedSwapProvider = dataState.selectedProvider ?: state.keys.first()
|
||||
dataState = dataState.copy(
|
||||
selectedProvider = selectedSwapProvider,
|
||||
lastLoadedSwapStates = state,
|
||||
)
|
||||
return state.entries.first { it.key == selectedSwapProvider }.toPair()
|
||||
}
|
||||
|
||||
private fun fillDataState(permissionState: PermissionDataState, swapDataModel: SwapStateData?) {
|
||||
dataState = if (permissionState is PermissionDataState.PermissionReadyForRequest) {
|
||||
dataState.copy(
|
||||
|
|
@ -435,10 +450,10 @@ internal class SwapViewModel @Inject constructor(
|
|||
toCryptoCurrency = toToken,
|
||||
)
|
||||
startLoadingQuotes(
|
||||
fromToken = fromToken.currency,
|
||||
toToken = toToken.currency,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(fromToken, toToken)
|
||||
toProvidersList = findSwapProviders(fromToken, toToken),
|
||||
)
|
||||
swapRouter.openScreen(SwapNavScreen.Main)
|
||||
}
|
||||
|
|
@ -460,10 +475,10 @@ internal class SwapViewModel @Inject constructor(
|
|||
inputNumberFormatter.formatWithThousands(lastAmount.value, decimals),
|
||||
)
|
||||
startLoadingQuotes(
|
||||
fromToken = newFromToken.currency,
|
||||
toToken = newToToken.currency,
|
||||
fromToken = newFromToken,
|
||||
toToken = newToToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(newFromToken, newToToken)
|
||||
toProvidersList = findSwapProviders(newFromToken, newToToken),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -479,10 +494,10 @@ internal class SwapViewModel @Inject constructor(
|
|||
stateBuilder.updateSwapAmount(uiState, inputNumberFormatter.formatWithThousands(cutValue, decimals))
|
||||
amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) {
|
||||
startLoadingQuotes(
|
||||
fromToken = fromToken.currency,
|
||||
toToken = toToken.currency,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(fromToken, toToken)
|
||||
toProvidersList = findSwapProviders(fromToken, toToken),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue