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 b9aa6f95e5..38ff3ef4af 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 @@ -295,9 +295,13 @@ internal class SwapInteractorImpl @Inject constructor( ) val fromTokenAddress = getTokenAddress(fromToken.currency) - val isAllowedToSpend = quotes.dataModel?.allowanceContract?.let { - isAllowedToSpend(networkId, fromToken.currency, amount, it) - } ?: true + val isAllowedToSpend = if (quotes.dataModel != null) { + quotes.dataModel.allowanceContract?.let { + isAllowedToSpend(networkId, fromToken.currency, amount, it) + } ?: true + } else { + false + } if (isAllowedToSpend && allowPermissionsHandler.isAddressAllowanceInProgress(fromTokenAddress)) { allowPermissionsHandler.removeAddressFromProgress(fromTokenAddress) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ProviderState.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ProviderState.kt index 63088433c5..84254a5716 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ProviderState.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ProviderState.kt @@ -25,7 +25,7 @@ sealed class ProviderState { val subtitle: TextReference, val selectionType: SelectionType, val additionalBadge: AdditionalBadge, - val percentLowerThenBest: Float?, + val percentLowerThenBest: Float = 0f, override val onProviderClick: (String) -> Unit, ) : ProviderState() @@ -48,4 +48,20 @@ sealed class ProviderState { enum class SelectionType { NONE, CLICK, SELECT } +} + +object ProviderPercentDiffComparator : Comparator { + override fun compare(o1: ProviderState, o2: ProviderState): Int { + if (o1 is ProviderState.Content && o2 !is ProviderState.Content) { + return 1 + } + if (o1 !is ProviderState.Content && o2 is ProviderState.Content) { + return -1 + } + return if (o1 is ProviderState.Content && o2 is ProviderState.Content) { + o1.percentLowerThenBest.compareTo(o2.percentLowerThenBest) + } else { + 0 + } + } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt index 3dead8dfcd..e5fe4ef2c9 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt @@ -151,7 +151,7 @@ private fun ProviderContentState( maxLines = 1, ) } - if (state.percentLowerThenBest != null) { + if (state.percentLowerThenBest > 0f) { AnimatedContent(targetState = state.percentLowerThenBest, label = "") { Text( text = "-$it%", diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index bb0f7e892c..094eac3c40 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -780,9 +780,11 @@ internal class StateBuilder( unavailableProviders: List, onDismiss: () -> Unit, ): SwapStateHolder { - val availableProvidersStates = providersStates.entries.mapNotNull { - it.convertToProviderBottomSheetState(pricesLowerBest, actions.onProviderSelect) - } + val availableProvidersStates = providersStates.entries + .mapNotNull { + it.convertToProviderBottomSheetState(pricesLowerBest, actions.onProviderSelect) + } + .sortedWith(ProviderPercentDiffComparator) val unavailableProviderStates = unavailableProviders.map { it.convertToUnavailableProviderState( alertText = resourceReference(R.string.express_provider_not_available), @@ -1024,7 +1026,7 @@ internal class StateBuilder( subtitle = stringReference(rateString), additionalBadge = badge, selectionType = selectionType, - percentLowerThenBest = null, + percentLowerThenBest = ZERO_PERCENT, onProviderClick = onProviderClick, ) } @@ -1053,7 +1055,7 @@ internal class StateBuilder( subtitle = stringReference(rateString), additionalBadge = additionalBadge, selectionType = selectionType, - percentLowerThenBest = pricesLowerBest[this], + percentLowerThenBest = pricesLowerBest[this] ?: ZERO_PERCENT, onProviderClick = onProviderClick, ) } @@ -1087,7 +1089,7 @@ internal class StateBuilder( selectionType = selectionType, subtitle = alertText, additionalBadge = ProviderState.AdditionalBadge.Empty, - percentLowerThenBest = null, + percentLowerThenBest = ZERO_PERCENT, onProviderClick = onProviderClick, ) } @@ -1128,5 +1130,6 @@ internal class StateBuilder( private const val HUNDRED_PERCENTS = 100 private const val UNKNOWN_AMOUNT_SIGN = "—" private const val MAX_DECIMALS_TO_SHOW = 8 + private const val ZERO_PERCENT = 0f } } \ 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 62425f7111..e37c581deb 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 @@ -42,6 +42,7 @@ import java.text.DecimalFormat import java.text.NumberFormat import java.util.Locale import javax.inject.Inject +import kotlin.math.absoluteValue import kotlin.properties.Delegates typealias SuccessLoadedSwapData = Map @@ -863,21 +864,17 @@ internal class SwapViewModel @Inject constructor( } private fun getPricesLowerBest(state: SuccessLoadedSwapData): Map { - val rates = state.mapValues { - it.value.fromTokenInfo.amountFiat.divide( - it.value.toTokenInfo.amountFiat, - 2, - RoundingMode.HALF_UP, - ) - } - val bestRate = rates.minByOrNull { it.value } ?: return emptyMap() - return rates.mapNotNull { - if (it.key != bestRate.key) { - val percentDiff = bestRate.value - .divide(it.value, 2, RoundingMode.HALF_UP) - .multiply(BigDecimal(HUNDRED_PERCENT)) - .toFloat() - HUNDRED_PERCENT - percentDiff + val bestRateEntry = state.maxByOrNull { it.value.toTokenInfo.tokenAmount.value } ?: return emptyMap() + val bestRate = bestRateEntry.value.toTokenInfo.tokenAmount.value + val hundredPercent = BigDecimal("100") + return state.mapNotNull { + if (it.key != bestRateEntry.key) { + val amount = it.value.toTokenInfo.tokenAmount.value + val percentDiff = BigDecimal.ONE.minus( + amount.divide(bestRate, RoundingMode.HALF_UP) + .multiply(hundredPercent), + ) + percentDiff.setScale(2, RoundingMode.HALF_UP).toFloat().absoluteValue } else { null } @@ -935,6 +932,5 @@ internal class SwapViewModel @Inject constructor( private const val INITIAL_AMOUNT = "" private const val UPDATE_DELAY = 10000L private const val DEBOUNCE_AMOUNT_DELAY = 1000L - private const val HUNDRED_PERCENT = 100 } } \ No newline at end of file