Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-06 13:39:30 +03:00
commit 92849d43c2
1407 changed files with 64092 additions and 11990 deletions

View file

@ -24,25 +24,6 @@ internal class DefaultSwapFeatureToggles @Inject constructor(
toggle = FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE,
)
override val isSwapAbEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.SWAP_AB_ENABLED,
)
override val isSwapProviderFilterEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15009_SWAP_PROVIDER_FILTER_ENABLED,
)
override val isSwapRateExperienceEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15103_SWAP_RATE_EXPERIENCE_ENABLED,
)
override val isSwapPredefinedButtonsEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15122_SWAP_PREDEFINED_BUTTONS_ENABLED,
)
override val isExpressShareButtonEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15489_EXPRESS_SHARE_BUTTON_ENABLED,
@ -52,4 +33,9 @@ internal class DefaultSwapFeatureToggles @Inject constructor(
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15715_SWAP_BEST_DEX_RATE_ENABLED,
) && isSwapIntegratedApproveEnabled
override val isHighFeeWarningEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.TWI_1367_HIGH_FEE_WARNING_ENABLED,
)
}

View file

@ -12,10 +12,10 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.models.TransactionFeeExtended
import com.tangem.features.send.api.FeeSelectorBlockComponent
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorBlockComponent
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.params.FeeSelectorParams
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.params.FeeSelectorParams
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

View file

@ -65,6 +65,7 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.pay.WithdrawalResult
import com.tangem.domain.pay.usecase.GetPaymentAccountCryptoCurrencyStatusUseCase
import com.tangem.domain.quotes.IsHighNetworkFeeUseCase
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
@ -109,8 +110,8 @@ import com.tangem.features.approval.api.SelectApprovalTypeComponent
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPayload
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult
import com.tangem.features.send.api.entity.FeeItem
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeItem
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.impl.R
import com.tangem.features.swap.SwapComponent
@ -173,6 +174,7 @@ internal class SwapModel @Inject constructor(
private val getSwapUiModeUseCase: GetSwapUiModeUseCase,
private val setSwapUiModeUseCase: SetSwapUiModeUseCase,
private val calculateAmountUseCase: CalculateAmountUseCase,
private val isHighNetworkFeeUseCase: IsHighNetworkFeeUseCase,
) : Model() {
private val params = paramsContainer.require<SwapComponent.Params>()
@ -208,7 +210,6 @@ internal class SwapModel @Inject constructor(
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
isAccountsModeProvider = Provider { isAccountsMode },
isGaslessFeeSupportedForNetwork = isGaslessFeeSupportedForNetwork,
swapFeatureToggles = swapFeatureToggles,
appRouter = appRouter,
)
@ -1102,7 +1103,7 @@ internal class SwapModel @Inject constructor(
)
}
private fun setupLoadedState(
private suspend fun setupLoadedState(
provider: SwapProvider,
state: SwapState,
fromSwapCurrencyStatus: SwapCurrencyStatus,
@ -1126,7 +1127,7 @@ internal class SwapModel @Inject constructor(
}
}
private fun setupQuotesLoadedUiState(provider: SwapProvider, state: SwapState.QuotesLoadedState) {
private suspend fun setupQuotesLoadedUiState(provider: SwapProvider, state: SwapState.QuotesLoadedState) {
val loadedStates = dataState.getLastLoadedSuccessStates()
val additionalBadge = SwapProviderResolver.resolveBadge(
provider = provider,
@ -1135,17 +1136,26 @@ internal class SwapModel @Inject constructor(
state = state,
isSwapBestDexRateEnabled = swapFeatureToggles.isSwapBestDexRateEnabled,
)
val swapFee = getSelectedSwapFee()
uiState = stateBuilder.createQuotesLoadedState(
uiStateHolder = uiState,
quoteModel = state,
feeCryptoCurrencyStatus = dataState.feePaidCryptoCurrency,
swapProvider = provider,
additionalBadge = additionalBadge,
swapFee = getSelectedSwapFee(),
swapFee = swapFee,
feeError = feeSelectorRepository.state.value as? FeeSelectorUM.Error,
isHighNetworkFee = isHighNetworkFee(swapFee),
)
}
private suspend fun isHighNetworkFee(swapFee: SwapFee?): Boolean {
if (!swapFeatureToggles.isHighFeeWarningEnabled) return false
swapFee ?: return false
val feeAmount = swapFee.fee.amount.value ?: return false
return isHighNetworkFeeUseCase(swapFee.selectedFeeToken.currency, feeAmount)
}
private fun sendAnalyticsForNotifications(
provider: SwapProvider,
fromToken: CryptoCurrencyStatus,
@ -2077,12 +2087,14 @@ internal class SwapModel @Inject constructor(
}
analyticsEventHandler.send(SwapEvents.ProviderChosen(provider))
uiState = stateBuilder.dismissBottomSheet(uiState)
setupLoadedState(
provider = provider,
state = swapState,
fromSwapCurrencyStatus = fromSwapCurrencyStatus,
toSwapCurrencyStatus = toSwapCurrencyStatus,
)
modelScope.launch {
setupLoadedState(
provider = provider,
state = swapState,
fromSwapCurrencyStatus = fromSwapCurrencyStatus,
toSwapCurrencyStatus = toSwapCurrencyStatus,
)
}
}
},
onProviderFilterSelect = { filterType ->
@ -2823,12 +2835,14 @@ internal class SwapModel @Inject constructor(
}
},
)
setupLoadedState(
provider = provider,
state = swapState,
fromSwapCurrencyStatus = fromSwapCurrencyStatus,
toSwapCurrencyStatus = toSwapCurrencyStatus,
)
modelScope.launch {
setupLoadedState(
provider = provider,
state = swapState,
fromSwapCurrencyStatus = fromSwapCurrencyStatus,
toSwapCurrencyStatus = toSwapCurrencyStatus,
)
}
} else {
TangemLogger.e("loadFee: ${feeError.error}, isHidden = true")
refreshTransferUIStateIfNeeded()

View file

@ -36,6 +36,7 @@ import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.lib.crypto.BlockchainUtils.getTezosThreshold
import com.tangem.lib.crypto.BlockchainUtils.isTezos
import com.tangem.utils.Provider
import com.tangem.utils.extensions.isZero
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -124,6 +125,7 @@ internal class SwapNotificationsFactory(
swapFee: SwapFee?,
feeError: GetFeeError?,
appRouter: AppRouter,
isHighNetworkFee: Boolean = false,
): ImmutableList<NotificationUM> {
val warnings = buildList {
maybeAddFeeErrorNotification(feeCryptoCurrencyStatus, quoteModel, feeError)
@ -135,10 +137,17 @@ internal class SwapNotificationsFactory(
maybeAddUnableCoverFeeWarning(quoteModel, feeCryptoCurrencyStatus, appRouter)
maybeAddTransactionInProgressWarning(quoteModel)
maybeAddPriceImpactNotification(quoteModel.priceImpact)
maybeAddHighNetworkFeeWarning(isHighNetworkFee)
}
return warnings.toPersistentList()
}
private fun MutableList<NotificationUM>.maybeAddHighNetworkFeeWarning(isHighNetworkFee: Boolean) {
if (isHighNetworkFee) {
add(NotificationUM.Warning.HighNetworkFee)
}
}
private fun MutableList<NotificationUM>.maybeAddRentExemptionError(quoteModel: SwapState.QuotesLoadedState) {
quoteModel.currencyCheck?.rentWarning?.let {
add(NotificationUM.Solana.RentInfo(it))
@ -207,7 +216,7 @@ internal class SwapNotificationsFactory(
actions.onReduceToAmount(amount.copy(value = reduceTo))
},
)
if (!isCardano) {
if (!isCardano && !feeValue.isZero()) {
addDustWarningNotification(
dustValue = quoteModel.currencyCheck?.dustValue,
feeValue = feeValue,

View file

@ -36,7 +36,6 @@ internal data class SwapStateHolder(
val predefinedButtons: ImmutableList<PredefinedPercentButtonUM> = persistentListOf(),
val tosState: TosState? = null,
val swapUIMode: SwapUIMode = SwapUIMode.Detailed,
val shouldShowAbMenu: Boolean = false,
val transferFooter: TextReference? = null,

View file

@ -51,8 +51,7 @@ import com.tangem.feature.swap.models.SwapButton.Mode
import com.tangem.feature.swap.models.states.*
import com.tangem.feature.swap.presentation.R
import com.tangem.feature.swap.utils.formatToUIRepresentation
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import com.tangem.utils.Provider
import com.tangem.utils.StringsSigns
import com.tangem.utils.StringsSigns.DASH_SIGN
@ -73,7 +72,6 @@ internal class StateBuilder(
private val appCurrencyProvider: Provider<AppCurrency>,
private val isAccountsModeProvider: Provider<Boolean>,
private val isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
private val swapFeatureToggles: SwapFeatureToggles,
private val appRouter: AppRouter,
) {
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
@ -124,7 +122,6 @@ internal class StateBuilder(
onSwapUIModeChange = actions.onSwapUIModeChange,
onSwapTypeMenuOpened = actions.onSwapTypeMenuOpened,
onTronBannerShown = actions.onTronBannerShown,
shouldShowAbMenu = swapFeatureToggles.isSwapAbEnabled,
)
}
@ -577,6 +574,7 @@ internal class StateBuilder(
additionalBadge: ProviderState.AdditionalBadge,
swapFee: SwapFee?,
feeError: FeeSelectorUM.Error?,
isHighNetworkFee: Boolean,
): SwapStateHolder {
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
@ -590,6 +588,7 @@ internal class StateBuilder(
swapFee = swapFee,
feeError = feeError?.error,
appRouter = appRouter,
isHighNetworkFee = isHighNetworkFee,
)
val fromAccountTitleUM = when {
@ -735,16 +734,15 @@ internal class StateBuilder(
/**
* Builds the predefined percent buttons once per state update (off the composition path).
* The row is gated by the feature toggle; the MAX button is included only when
* [shouldShowMaxAmount] is `true` (e.g. it is dropped for a native coin swapped within the same
* network, where spending the full balance would leave nothing for the network fee).
* The MAX button is included only when [shouldShowMaxAmount] is `true` (e.g. it is dropped for a
* native coin swapped within the same network, where spending the full balance would leave nothing
* for the network fee).
*/
private fun createPredefinedButtons(
fromToken: CryptoCurrency?,
toCurrency: CryptoCurrency?,
isTransferMode: Boolean = false,
): ImmutableList<PredefinedPercentButtonUM> {
if (!swapFeatureToggles.isSwapPredefinedButtonsEnabled) return persistentListOf()
val shouldShowMaxAmount = shouldShowMaxAmount(fromToken, toCurrency, isTransferMode)
return PredefinedPercentAmount.entries
.filter { it != PredefinedPercentAmount.MAX || shouldShowMaxAmount }
@ -1257,7 +1255,7 @@ internal class StateBuilder(
providerType == ExchangeProviderType.DEX.providerName ||
providerType == ExchangeProviderType.DEX_BRIDGE.providerName
}
val availableFilters = if (swapFeatureToggles.isSwapProviderFilterEnabled && hasCex && hasDex) {
val availableFilters = if (hasCex && hasDex) {
persistentListOf(ProviderFilterType.ALL, ProviderFilterType.CEX, ProviderFilterType.DEX)
} else {
persistentListOf()

View file

@ -85,44 +85,38 @@ private fun SwapTopBar(stateHolder: SwapStateHolder) {
AppBarWithBackButtonAndIcon(
text = stringResourceSafe(stateHolder.titleId),
backIconRes = R.drawable.ic_close_24,
iconRes = if (stateHolder.shouldShowAbMenu) R.drawable.ic_more_vertical_24 else null,
onIconClick = if (stateHolder.shouldShowAbMenu) {
{
stateHolder.onSwapTypeMenuOpened()
shouldShowModeMenu = true
}
} else {
null
iconRes = R.drawable.ic_more_vertical_24,
onIconClick = {
stateHolder.onSwapTypeMenuOpened()
shouldShowModeMenu = true
},
onBackClick = stateHolder.onBackClicked,
)
if (stateHolder.shouldShowAbMenu) {
Box(modifier = Modifier.align(Alignment.TopEnd)) {
TangemDropdownMenu(
expanded = shouldShowModeMenu,
modifier = Modifier.background(TangemTheme.colors.background.primary),
offset = DpOffset(x = TangemTheme.dimens.spacing20, y = 44.dp),
onDismissRequest = { shouldShowModeMenu = false },
content = {
SwapUiModeMenuItem(
title = stringResourceSafe(R.string.swap_simple_mode),
isSelected = stateHolder.swapUIMode == SwapUIMode.Simple,
onClick = {
shouldShowModeMenu = false
stateHolder.onSwapUIModeChange(SwapUIMode.Simple)
},
)
SwapUiModeMenuItem(
title = stringResourceSafe(R.string.swap_detailed_mode),
isSelected = stateHolder.swapUIMode == SwapUIMode.Detailed,
onClick = {
shouldShowModeMenu = false
stateHolder.onSwapUIModeChange(SwapUIMode.Detailed)
},
)
},
)
}
Box(modifier = Modifier.align(Alignment.TopEnd)) {
TangemDropdownMenu(
expanded = shouldShowModeMenu,
modifier = Modifier.background(TangemTheme.colors.background.primary),
offset = DpOffset(x = TangemTheme.dimens.spacing20, y = 44.dp),
onDismissRequest = { shouldShowModeMenu = false },
content = {
SwapUiModeMenuItem(
title = stringResourceSafe(R.string.swap_simple_mode),
isSelected = stateHolder.swapUIMode == SwapUIMode.Simple,
onClick = {
shouldShowModeMenu = false
stateHolder.onSwapUIModeChange(SwapUIMode.Simple)
},
)
SwapUiModeMenuItem(
title = stringResourceSafe(R.string.swap_detailed_mode),
isSelected = stateHolder.swapUIMode == SwapUIMode.Detailed,
onClick = {
shouldShowModeMenu = false
stateHolder.onSwapUIModeChange(SwapUIMode.Detailed)
},
)
},
)
}
}
}

View file

@ -31,7 +31,7 @@ import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.feature.swap.models.SwapSuccessStateHolder
import com.tangem.feature.swap.presentation.R
import com.tangem.feature.swap.preview.SwapSuccessStatePreview
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import com.tangem.features.send.common.ui.FeeBlockSuccess
@Composable

View file

@ -19,12 +19,13 @@ import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.ui.SwapState
import com.tangem.feature.swap.models.UiActions
import com.tangem.feature.swap.models.states.SwapNotificationUM
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.utils.FeeCalculationUtils
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.lib.crypto.BlockchainUtils.getTezosThreshold
import com.tangem.lib.crypto.BlockchainUtils.isTezos
import com.tangem.lib.crypto.BlockchainUtils.isTron
import com.tangem.utils.extensions.isZero
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
@ -107,7 +108,7 @@ internal class SwapTransferNotificationsFactory @Inject constructor() {
onReduceToAmount(amount.copy(value = reduceTo))
},
)
if (!isCardano) {
if (!isCardano && !feeValue.isZero()) {
addDustWarningNotification(
dustValue = state.currencyCheck?.dustValue,
feeValue = feeValue,

View file

@ -37,7 +37,7 @@ import com.tangem.feature.swap.models.states.SwapNotificationUM
import com.tangem.feature.swap.presentation.R
import com.tangem.feature.swap.ui.SwapAmountScreenClickIntents
import com.tangem.feature.swap.ui.swapSuccessNavigation
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import com.tangem.features.send.api.utils.formatFooterFiatFee
import com.tangem.features.send.api.utils.getTronTokenFeeSendingText
import com.tangem.utils.extensions.orZero

View file

@ -18,7 +18,6 @@ import com.tangem.feature.swap.models.*
import com.tangem.feature.swap.models.states.ProviderState
import com.tangem.feature.swap.models.states.SwapNotificationUM
import com.tangem.feature.swap.ui.StateBuilder
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.utils.Provider
import io.mockk.every
import io.mockk.mockk
@ -33,7 +32,6 @@ internal class StateBuilderInitialStateTest {
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
private val isAccountsModeProvider: Provider<Boolean> = mockk()
private val isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
private val swapFeatureToggles: SwapFeatureToggles = mockk(relaxed = true)
private val appRouter: AppRouter = mockk()
private lateinit var sut: StateBuilder
@ -52,7 +50,6 @@ internal class StateBuilderInitialStateTest {
appCurrencyProvider = appCurrencyProvider,
isAccountsModeProvider = isAccountsModeProvider,
isGaslessFeeSupportedForNetwork = isGaslessFeeSupportedForNetwork,
swapFeatureToggles = swapFeatureToggles,
appRouter = appRouter,
)
}

View file

@ -18,7 +18,6 @@ import com.tangem.feature.swap.models.*
import com.tangem.feature.swap.models.states.ProviderState
import com.tangem.feature.swap.models.states.SwapNotificationUM
import com.tangem.feature.swap.ui.StateBuilder
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.utils.Provider
import io.mockk.every
import io.mockk.mockk
@ -33,7 +32,6 @@ internal class StateBuilderPairsTest {
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
private val isAccountsModeProvider: Provider<Boolean> = mockk()
private val isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
private val swapFeatureToggles: SwapFeatureToggles = mockk(relaxed = true)
private val appRouter: AppRouter = mockk()
private lateinit var sut: StateBuilder
@ -62,7 +60,6 @@ internal class StateBuilderPairsTest {
appCurrencyProvider = appCurrencyProvider,
isAccountsModeProvider = isAccountsModeProvider,
isGaslessFeeSupportedForNetwork = isGaslessFeeSupportedForNetwork,
swapFeatureToggles = swapFeatureToggles,
appRouter = appRouter,
)
}
@ -394,8 +391,7 @@ internal class StateBuilderPairsTest {
inner class PredefinedButtonsVisibility {
@Test
fun `GIVEN toggle on and native coin within same network WHEN updateCurrenciesState THEN MAX button is dropped but percents stay`() {
every { swapFeatureToggles.isSwapPredefinedButtonsEnabled } returns true
fun `GIVEN native coin within same network WHEN updateCurrenciesState THEN MAX button is dropped but percents stay`() {
val baseState = buildReadyState(coldWallet)
val networkId: Network.ID = mockk(relaxed = true)
val fromStatus = buildCoinSwapCurrencyStatus(coldWallet, networkId)
@ -420,8 +416,7 @@ internal class StateBuilderPairsTest {
}
@Test
fun `GIVEN toggle on and non-coin WHEN updateCurrenciesState THEN all percents including MAX are built`() {
every { swapFeatureToggles.isSwapPredefinedButtonsEnabled } returns true
fun `GIVEN non-coin WHEN updateCurrenciesState THEN all percents including MAX are built`() {
val baseState = buildReadyState(coldWallet)
val fromStatus = buildSwapCurrencyStatus(coldWallet)
val toStatus = buildSwapCurrencyStatus(coldWallet)
@ -440,24 +435,6 @@ internal class StateBuilderPairsTest {
.inOrder()
}
@Test
fun `GIVEN toggle off WHEN updateCurrenciesState THEN no predefined buttons are built`() {
every { swapFeatureToggles.isSwapPredefinedButtonsEnabled } returns false
val baseState = buildReadyState(coldWallet)
val fromStatus = buildSwapCurrencyStatus(coldWallet)
val toStatus = buildSwapCurrencyStatus(coldWallet)
val result = sut.updateCurrenciesState(
uiStateHolder = baseState,
emptyAmountState = emptyAmountState,
fromSwapCurrencyStatus = fromStatus,
toSwapCurrencyStatus = toStatus,
shouldResetAmount = false,
)
assertThat(result.predefinedButtons).isEmpty()
}
@Test
fun `WHEN createInitialLoadingState THEN no predefined buttons are built`() {
val result = sut.createInitialLoadingState()

View file

@ -25,7 +25,6 @@ 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.ui.StateBuilder
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.utils.Provider
import io.mockk.every
import io.mockk.mockk
@ -59,7 +58,6 @@ internal class StateBuilderSwapButtonTest {
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
private val isAccountsModeProvider: Provider<Boolean> = mockk()
private val isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
private val swapFeatureToggles: SwapFeatureToggles = mockk(relaxed = true)
private val appRouter: AppRouter = mockk()
private lateinit var sut: StateBuilder
@ -81,7 +79,6 @@ internal class StateBuilderSwapButtonTest {
appCurrencyProvider = appCurrencyProvider,
isAccountsModeProvider = isAccountsModeProvider,
isGaslessFeeSupportedForNetwork = isGaslessFeeSupportedForNetwork,
swapFeatureToggles = swapFeatureToggles,
appRouter = appRouter,
)
}
@ -109,6 +106,7 @@ internal class StateBuilderSwapButtonTest {
additionalBadge = ProviderState.AdditionalBadge.Empty,
swapFee = null,
feeError = null,
isHighNetworkFee = false,
)
assertThat(result.swapButton.isEnabled).isTrue()
@ -134,6 +132,7 @@ internal class StateBuilderSwapButtonTest {
additionalBadge = ProviderState.AdditionalBadge.Empty,
swapFee = null,
feeError = null,
isHighNetworkFee = false,
)
assertThat(result.swapButton.isEnabled).isFalse()
@ -163,6 +162,7 @@ internal class StateBuilderSwapButtonTest {
additionalBadge = ProviderState.AdditionalBadge.Empty,
swapFee = null,
feeError = null,
isHighNetworkFee = false,
)
assertThat(result.swapButton.isEnabled).isFalse()
@ -187,6 +187,7 @@ internal class StateBuilderSwapButtonTest {
additionalBadge = ProviderState.AdditionalBadge.Empty,
swapFee = buildSwapFee(),
feeError = null,
isHighNetworkFee = false,
)
assertThat(result.swapButton.isEnabled).isTrue()
@ -215,6 +216,7 @@ internal class StateBuilderSwapButtonTest {
additionalBadge = ProviderState.AdditionalBadge.Empty,
swapFee = buildSwapFee(),
feeError = null,
isHighNetworkFee = false,
)
assertThat(result.swapButton.isEnabled).isFalse()

View file

@ -17,7 +17,6 @@ import com.tangem.feature.swap.models.SwapStateHolder
import com.tangem.feature.swap.models.TransactionCardType
import com.tangem.feature.swap.models.UiActions
import com.tangem.feature.swap.ui.StateBuilder
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.utils.Provider
import io.mockk.every
import io.mockk.mockk
@ -34,7 +33,6 @@ internal class StateBuilderUpdateSwapAmountTest {
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
private val isAccountsModeProvider: Provider<Boolean> = mockk()
private val isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
private val swapFeatureToggles: SwapFeatureToggles = mockk(relaxed = true)
private val appRouter: AppRouter = mockk()
private val appCurrency = AppCurrency.Default
@ -58,7 +56,6 @@ internal class StateBuilderUpdateSwapAmountTest {
appCurrencyProvider = appCurrencyProvider,
isAccountsModeProvider = isAccountsModeProvider,
isGaslessFeeSupportedForNetwork = isGaslessFeeSupportedForNetwork,
swapFeatureToggles = swapFeatureToggles,
appRouter = appRouter,
)
}

View file

@ -3,7 +3,7 @@ package com.tangem.feature.swap.model
import com.google.common.truth.Truth.assertThat
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.feature.swap.domain.models.ui.PermissionDataState
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import io.mockk.coVerify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest

View file

@ -6,7 +6,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.feature.swap.analytics.SwapEvents
import com.tangem.feature.swap.domain.models.ui.PermissionDataState
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import io.mockk.coVerify
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi

View file

@ -26,6 +26,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.usecase.GetPaymentAccountCryptoCurrencyStatusUseCase
import com.tangem.domain.quotes.IsHighNetworkFeeUseCase
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.stories.ShouldShowStoriesUseCase
@ -103,6 +104,7 @@ internal abstract class SwapModelTestBase {
protected val getSwapUiModeUseCase: GetSwapUiModeUseCase = mockk(relaxed = true)
protected val setSwapUiModeUseCase: SetSwapUiModeUseCase = mockk(relaxed = true)
protected val calculateAmountUseCase: CalculateAmountUseCase = mockk(relaxed = true)
protected val isHighNetworkFeeUseCase: IsHighNetworkFeeUseCase = mockk(relaxed = true)
protected val isWalletBackupProblematicUseCase: IsWalletBackupProblematicUseCase = mockk(relaxed = true)
protected val sendBackupProblemEmailUseCase: SendBackupProblemEmailUseCase = mockk(relaxed = true)
@ -175,6 +177,7 @@ internal abstract class SwapModelTestBase {
getSwapUiModeUseCase = getSwapUiModeUseCase,
setSwapUiModeUseCase = setSwapUiModeUseCase,
calculateAmountUseCase = calculateAmountUseCase,
isHighNetworkFeeUseCase = isHighNetworkFeeUseCase,
isWalletBackupProblematicUseCase = isWalletBackupProblematicUseCase,
sendBackupProblemEmailUseCase = sendBackupProblemEmailUseCase,
)

View file

@ -19,9 +19,9 @@ import com.tangem.feature.swap.domain.models.ui.SwapState
import com.tangem.feature.swap.domain.models.ui.TokenSwapInfo
import com.tangem.feature.swap.models.UiActions
import com.tangem.feature.swap.models.states.SwapNotificationUM
import com.tangem.features.send.api.entity.CustomFeeFieldUM
import com.tangem.features.send.api.entity.FeeItem
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.CustomFeeFieldUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeItem
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
import io.mockk.every
import io.mockk.mockk
import kotlinx.collections.immutable.persistentListOf
@ -113,7 +113,9 @@ internal class SwapTransferNotificationsFactoryTest {
val result = sut.getNotifications(
transferState = transferState,
feeSelectorUM = null,
// A loaded (non-zero) fee is required: the manual dust check is now skipped when feeValue is zero
// (fee errored / not yet loaded), so it cannot duplicate the fee-error path's MinimumAmountError.
feeSelectorUM = contentWithFee(feeValue = BigDecimal("0.001")),
feeCryptoCurrencyStatus = null,
actions = actions,
)