Updated on 2026-08-14

This commit is contained in:
Tangem 2026-01-10 19:49:57 +03:00
parent c8b7934d93
commit bdf2433cb7
7 changed files with 317 additions and 20 deletions

View file

@ -90,6 +90,7 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor(
params = FeeSelectorBlockParams(
state = model.uiState.value.feeSelectorUM,
onLoadFee = model::loadFee,
onLoadFeeExtended = model::loadFeeExtended,
feeCryptoCurrencyStatus = model.primaryFeePaidCurrencyStatus,
cryptoCurrencyStatus = model.primaryCurrencyStatus,
feeStateConfiguration = FeeStateConfiguration.ExcludeLow,

View file

@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.confirm.model
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
@ -26,7 +27,10 @@ import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.models.TransactionFeeExtended
import com.tangem.domain.transaction.usecase.EstimateFeeUseCase
import com.tangem.domain.transaction.usecase.gasless.EstimateFeeForGaslessTxUseCase
import com.tangem.domain.transaction.usecase.gasless.EstimateFeeForTokenUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
@ -64,6 +68,7 @@ import jakarta.inject.Inject
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.math.BigDecimal
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
import com.tangem.utils.transformer.update as transformerUpdate
@Suppress("LongParameterList", "LargeClass")
@ -73,6 +78,8 @@ internal class SendWithSwapConfirmModel @Inject constructor(
private val router: Router,
private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase,
private val estimateFeeUseCase: EstimateFeeUseCase,
private val estimateFeeForTokenUseCase: EstimateFeeForTokenUseCase,
private val estimateFeeForGaslessTxUseCase: EstimateFeeForGaslessTxUseCase,
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val sendNotificationsUpdateTrigger: SendNotificationsUpdateTrigger,
@ -106,6 +113,8 @@ internal class SendWithSwapConfirmModel @Inject constructor(
get() = uiState.value.destinationUM as? DestinationUM.Content
private val feeSelectorUM
get() = uiState.value.feeSelectorUM as? FeeSelectorUM.Content
private val feeUMV2
get() = uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content
val secondaryCurrencyStatus: CryptoCurrencyStatus? = amountUM?.secondaryCryptoCurrencyStatus
val secondaryCurrency: CryptoCurrency = requireNotNull(amountUM?.secondaryCryptoCurrencyStatus?.currency) {
@ -143,7 +152,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
}
init {
initAmountSubtractAvailability()
updateAmountSubtractAvailability()
configConfirmNavigation()
initialState()
subscribeOnNotificationUpdates()
@ -152,6 +161,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
override fun onFeeResult(feeSelectorUM: FeeSelectorUM) {
uiState.update { it.copy(feeSelectorUM = feeSelectorUM) }
updateAmountSubtractAvailability()
updateConfirmNotifications()
}
@ -243,11 +253,43 @@ internal class SendWithSwapConfirmModel @Inject constructor(
}
}
suspend fun loadFeeExtended(maybeToken: CryptoCurrencyStatus?): Either<GetFeeError, TransactionFeeExtended> {
val defaultError = GetFeeError.UnknownError.left()
val provider = (confirmData.quote as? SwapQuoteUM.Content)?.provider ?: return defaultError
val amountValue = confirmData.enteredAmount ?: return defaultError
return when (val providerType = provider.type) {
ExpressProviderType.CEX -> {
if (maybeToken != null) {
estimateFeeForTokenUseCase(
amount = amountValue,
userWallet = params.userWallet,
tokenCurrencyStatus = maybeToken,
)
} else {
estimateFeeForGaslessTxUseCase(
amount = amountValue,
userWallet = params.userWallet,
cryptoCurrencyStatus = primaryCurrencyStatus,
)
}
}
ExpressProviderType.DEX,
ExpressProviderType.DEX_BRIDGE,
ExpressProviderType.ONRAMP,
-> GetFeeError.DataError(
cause = IllegalStateException("Provider $providerType is not supported in Send With Swap"),
).left()
}
}
private fun onSendClick() {
val provider = confirmData.quote?.provider ?: return
modelScope.launch {
uiState.transformerUpdate(SendWithSwapConfirmSendingStateTransformer(true))
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
swapTransactionSender.sendTransaction(
feeExtended = feeExtended,
confirmData = confirmData,
isAmountSubtractAvailable = isAmountSubtractAvailable,
onExpressError = { expressError ->
@ -305,12 +347,15 @@ internal class SendWithSwapConfirmModel @Inject constructor(
}
}
private fun initAmountSubtractAvailability() {
private fun updateAmountSubtractAvailability() {
modelScope.launch {
val isGaslessEthTx =
feeUMV2?.feeExtraInfo?.transactionFeeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
isAmountSubtractAvailable =
isAmountSubtractAvailableUseCase(
params.userWallet.walletId,
primaryCurrencyStatus.currency,
userWalletId = params.userWallet.walletId,
currency = primaryCurrencyStatus.currency,
isGaslessEthTx = isGaslessEthTx,
).getOrElse { false }
}
}

View file

@ -17,8 +17,10 @@ import com.tangem.domain.swap.models.SwapTxType
import com.tangem.domain.swap.usecase.GetSwapDataUseCase
import com.tangem.domain.swap.usecase.SwapTransactionSentUseCase
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.models.TransactionFeeExtended
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.transaction.usecase.gasless.CreateAndSendGaslessTransactionUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils
import com.tangem.features.swap.v2.impl.common.ConfirmData
@ -35,6 +37,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val swapTransactionSentUseCase: SwapTransactionSentUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase,
@Assisted private val userWallet: UserWallet,
) {
@ -45,6 +48,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
onExpressError: (ExpressError) -> Unit,
onSendError: (SendTransactionError?) -> Unit,
expressOperationType: ExpressOperationType,
feeExtended: TransactionFeeExtended?,
) {
val provider = confirmData.quote?.provider ?: return
@ -56,6 +60,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
onSendSuccess = onSendSuccess,
onSendError = onSendError,
expressOperationType = expressOperationType,
feeExtended = feeExtended,
)
ExpressProviderType.DEX,
ExpressProviderType.DEX_BRIDGE,
@ -74,6 +79,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
onExpressError: (ExpressError) -> Unit,
onSendError: (SendTransactionError?) -> Unit,
expressOperationType: ExpressOperationType,
feeExtended: TransactionFeeExtended?,
) {
val fromStatus = confirmData.fromCryptoCurrencyStatus ?: return
val toStatus = confirmData.toCryptoCurrencyStatus ?: return
@ -114,6 +120,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
onSendSuccess = onSendSuccess,
onExpressError = onExpressError,
onSendError = onSendError,
feeExtended = feeExtended,
)
}
@ -123,6 +130,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
toStatus: CryptoCurrencyStatus,
fromAccount: Account.CryptoPortfolio?,
fee: Fee,
feeExtended: TransactionFeeExtended?,
provider: ExpressProvider,
swapData: SwapDataModel,
onSendSuccess: (String, Long, SwapDataModel) -> Unit,
@ -161,11 +169,22 @@ internal class SwapTransactionSender @AssistedInject constructor(
txData.destinationAddress
}
sendTransactionUseCase(
txData = txData,
userWallet = userWallet,
network = fromStatus.currency.network,
).fold(
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
val result = if (feeExtended != null && isFeeInTokenCurrency) {
createAndSendGaslessTransactionUseCase(
userWallet = userWallet,
transactionData = txData,
fee = feeExtended,
)
} else {
sendTransactionUseCase(
txData = txData,
userWallet = userWallet,
network = fromStatus.currency.network,
)
}
result.fold(
ifLeft = { onSendError(it) },
ifRight = { txHash ->
val timestamp = System.currentTimeMillis()