Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-27 18:10:09 +05:00
parent 70617df9ad
commit dfbec1dee4
5 changed files with 34 additions and 30 deletions

View file

@ -34,10 +34,10 @@ internal object SwapAmountQuoteUtils {
secondaryFiatRateUSD: BigDecimal?,
primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
secondaryCryptoCurrencyStatus: CryptoCurrencyStatus?,
): PriceImpact {
if (quoteContent == null) return PriceImpact.Empty
): PriceImpact? {
if (quoteContent == null) return null
val fromAmount = quoteContent.fromAmount ?: return PriceImpact.Empty
val fromAmount = quoteContent.fromAmount ?: return null
val toAmount = quoteContent.toAmount
val (fromRate, toRate) = if (swapDirection == SwapDirection.Direct) {

View file

@ -86,7 +86,7 @@ internal fun SwapAmountBlockContent(
val quoteContent = amountUM.selectedQuote as? SwapQuoteUM.Content
val isBestRate = quoteContent?.diffPercent is SwapQuoteUM.Content.DifferencePercent.Best
SwapChooseProviderContent(
isBestRate = isBestRate && amountUM.priceImpact?.shouldShowWarning() == true,
isBestRate = isBestRate && amountUM.priceImpact?.shouldShowWarning() != true,
isSingleProvider = quoteContent?.isSingleProvider == true,
showBestRateAnimation = amountUM.isShowBestRateAnimation,
expressProvider = amountUM.selectedQuote.provider,

View file

@ -68,7 +68,6 @@ import com.tangem.feature.swap.domain.models.toStringWithRightOffset
import com.tangem.feature.swap.domain.models.ui.*
import com.tangem.lib.crypto.BlockchainUtils.SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES
import com.tangem.utils.coroutines.runSuspendCatching
import com.tangem.utils.extensions.orZero
import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -2457,7 +2456,8 @@ internal class SwapInteractorImpl @AssistedInject constructor(
if (fromQuoteStatus == null) return PriceImpact.Empty
val fromTokenFiatValue = fromTokenAmount.multiply(fromQuoteStatus.fiatRate)
val toTokenFiatValue = toTokenAmount.multiply(toRate.orZero())
val toTokenFiatValue = toRate?.let { toTokenAmount.multiply(toRate) } ?: return PriceImpact.Empty
val value = BigDecimal.ONE - toTokenFiatValue.divide(fromTokenFiatValue, 2, RoundingMode.HALF_UP)
val fromAmountUSD = if (fromQuoteStatus.fiatRateUSD != BigDecimal.ZERO) {
@ -2467,7 +2467,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
}
val amountSignificance = when {
fromAmountUSD < PRICE_IMPACT_AMOUNT_MIN_THRESHOLD -> PriceImpact.AmountSignificance.LOW
fromAmountUSD <= PRICE_IMPACT_AMOUNT_MIN_THRESHOLD -> PriceImpact.AmountSignificance.LOW
fromAmountUSD > PRICE_IMPACT_AMOUNT_MAX_THRESHOLD -> PriceImpact.AmountSignificance.HIGH
else -> PriceImpact.AmountSignificance.MEDIUM
}
@ -2552,7 +2552,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
companion object {
private const val INCREASE_GAS_LIMIT_FOR_DEX = 112 // 12%
private const val INCREASE_GAS_LIMIT_FOR_SEND = 105 // 5%
private val PRICE_IMPACT_AMOUNT_MIN_THRESHOLD = 50.toBigDecimal() // in USD
private val PRICE_IMPACT_AMOUNT_MIN_THRESHOLD = 25.toBigDecimal() // in USD
private val PRICE_IMPACT_AMOUNT_MAX_THRESHOLD = 5000.toBigDecimal() // in USD
private val PRICE_IMPACT_LOW_THRESHOLD = 0.1.toBigDecimal() // 10%
private val PRICE_IMPACT_HIGH_THRESHOLD = 0.5.toBigDecimal() // 50%

View file

@ -66,6 +66,10 @@ data class PriceImpact(
return type == Type.HIGH && amountSignificance == AmountSignificance.HIGH
}
fun shouldShowWarning(): Boolean {
return type.ordinal > Type.LOW.ordinal || amountSignificance.ordinal > AmountSignificance.LOW.ordinal
}
companion object {
val Empty = PriceImpact(
value = BigDecimal.ZERO,

View file

@ -387,26 +387,26 @@ internal class StateBuilder(
.formatToUIRepresentation()
.appendApproximateSign(),
),
amountEquivalent = combinedReference(
getFormattedFiatAmount(quoteModel.toTokenInfo.amountFiat),
stringReference(StringsSigns.WHITE_SPACE),
styledStringReference(
priceImpact.value.format {
percent(withoutSign = false)
},
spanStyleReference = {
SpanStyle(
color = when (priceImpact.type) {
PriceImpact.Type.HIGH -> TangemTheme.colors.text.warning
PriceImpact.Type.MEDIUM -> TangemTheme.colors.text.attention
PriceImpact.Type.LOW,
PriceImpact.Type.NONE,
-> TangemTheme.colors.text.tertiary
},
)
},
),
),
amountEquivalent = if (priceImpact.type.ordinal > PriceImpact.Type.LOW.ordinal) {
combinedReference(
getFormattedFiatAmount(quoteModel.toTokenInfo.amountFiat),
stringReference(StringsSigns.WHITE_SPACE),
styledStringReference(
value = "(${StringsSigns.MINUS}${priceImpact.value.format { percent() }})",
spanStyleReference = {
SpanStyle(
color = when (priceImpact.type) {
PriceImpact.Type.HIGH -> TangemTheme.colors.text.warning
PriceImpact.Type.MEDIUM -> TangemTheme.colors.text.attention
else -> TangemTheme.colors.text.tertiary
},
)
},
),
)
} else {
getFormattedFiatAmount(quoteModel.toTokenInfo.amountFiat)
},
token = toCurrencyStatus,
tokenIconUrl = uiStateHolder.receiveCardData.tokenIconUrl,
coinId = toCurrencyStatus.currency.network.backendId,
@ -436,7 +436,7 @@ internal class StateBuilder(
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
providerState = swapProvider.convertToContentClickableProviderState(
isBestRate = bestRatedProviderId == swapProvider.providerId,
isBestRate = bestRatedProviderId == swapProvider.providerId && !priceImpact.shouldShowWarning(),
fromTokenInfo = quoteModel.fromTokenInfo,
toTokenInfo = quoteModel.toTokenInfo,
isNeedBestRateBadge = isNeedBestRateBadge,
@ -444,7 +444,7 @@ internal class StateBuilder(
onProviderClick = actions.onProviderClick,
needApplyFCARestrictions = needApplyFCARestrictions,
),
priceImpact = quoteModel.priceImpact,
priceImpact = priceImpact,
tosState = createTosState(swapProvider),
shouldShowMaxAmount = shouldShowMaxAmount(fromToken, toCurrencyStatus.currency),
)