From bdf2433cb739c823bdf9b35ae5e1c044e01b829b Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 10 Jan 2026 19:49:57 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TransactionDomainModule.kt | 19 ++ .../gasless/EstimateFeeForGaslessTxUseCase.kt | 183 ++++++++++++++++++ .../gasless/EstimateFeeForTokenUseCase.kt | 20 +- .../usecase/gasless/TokenFeeCalculator.kt | 32 +++ .../confirm/SendWithSwapConfirmComponent.kt | 1 + .../confirm/model/SendWithSwapConfirmModel.kt | 53 ++++- .../confirm/model/SwapTransactionSender.kt | 29 ++- 7 files changed, 317 insertions(+), 20 deletions(-) create mode 100644 domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForGaslessTxUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt index b0b821fffb..e664e2294a 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt @@ -363,4 +363,23 @@ internal object TransactionDomainModule { getMultiCryptoCurrencyStatusUseCase = getMultiCryptoCurrencyStatusUseCase, ) } + + @Provides + @Singleton + fun provideEstimateFeeForGaslessTxUseCase( + walletManagersFacade: WalletManagersFacade, + gaslessTransactionRepository: GaslessTransactionRepository, + currenciesRepository: CurrenciesRepository, + getMultiCryptoCurrencyStatusUseCase: GetMultiCryptoCurrencyStatusUseCase, + estimateFeeUseCase: EstimateFeeUseCase, + ): EstimateFeeForGaslessTxUseCase { + return EstimateFeeForGaslessTxUseCase( + gaslessTransactionRepository = gaslessTransactionRepository, + walletManagersFacade = walletManagersFacade, + demoConfig = DemoConfig, + currenciesRepository = currenciesRepository, + getMultiCryptoCurrencyStatusUseCase = getMultiCryptoCurrencyStatusUseCase, + estimateFeeUseCase = estimateFeeUseCase, + ) + } } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForGaslessTxUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForGaslessTxUseCase.kt new file mode 100644 index 0000000000..b7d9ec271c --- /dev/null +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForGaslessTxUseCase.kt @@ -0,0 +1,183 @@ +package com.tangem.domain.transaction.usecase.gasless + +import arrow.core.Either +import arrow.core.raise.Raise +import arrow.core.raise.catch +import arrow.core.raise.either +import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager +import com.tangem.blockchain.common.transaction.Fee +import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.domain.demo.models.DemoConfig +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase +import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.transaction.GaslessTransactionRepository +import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.transaction.error.GetFeeError.GaslessError +import com.tangem.domain.transaction.models.TransactionFeeExtended +import com.tangem.domain.transaction.raiseIllegalStateError +import com.tangem.domain.transaction.usecase.EstimateFeeUseCase +import com.tangem.domain.walletmanager.WalletManagersFacade +import java.math.BigDecimal + +class EstimateFeeForGaslessTxUseCase( + private val walletManagersFacade: WalletManagersFacade, + private val demoConfig: DemoConfig, + private val gaslessTransactionRepository: GaslessTransactionRepository, + private val currenciesRepository: CurrenciesRepository, + private val getMultiCryptoCurrencyStatusUseCase: GetMultiCryptoCurrencyStatusUseCase, + private val estimateFeeUseCase: EstimateFeeUseCase, +) { + + private val tokenFeeCalculator = TokenFeeCalculator( + walletManagersFacade = walletManagersFacade, + gaslessTransactionRepository = gaslessTransactionRepository, + demoConfig = demoConfig, + ) + + suspend operator fun invoke( + userWallet: UserWallet, + amount: BigDecimal, + cryptoCurrencyStatus: CryptoCurrencyStatus, + ): Either { + return either { + catch( + block = { + val network = cryptoCurrencyStatus.currency.network + val nativeCurrency = currenciesRepository.getNetworkCoin( + userWalletId = userWallet.walletId, + networkId = network.id, + derivationPath = network.derivationPath, + ) + + if (!gaslessTransactionRepository.isNetworkSupported(network)) { + estimateFeeUseCase.invoke( + userWallet = userWallet, + amount = amount, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).fold( + ifLeft = { raise(it) }, + ifRight = { fee -> + return@either TransactionFeeExtended( + transactionFee = fee, + feeTokenId = nativeCurrency.id, + ) + }, + ) + } + + val walletManager = prepareWalletManager(userWallet, network) + + val initialFee = tokenFeeCalculator.estimateInitialFee( + userWallet = userWallet, + amount = amount, + tokenCurrencyStatus = cryptoCurrencyStatus, + ).bind() + + selectFeePaymentStrategy( + userWallet = userWallet, + walletManager = walletManager, + nativeCurrency = nativeCurrency, + network = network, + initialFee = initialFee, + ) + }, + catch = { + raise(GetFeeError.DataError(it)) + }, + ) + } + } + + @Suppress("NullableToStringCall") + private suspend fun Raise.prepareWalletManager( + userWallet: UserWallet, + network: Network, + ): EthereumWalletManager { + val walletManager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWallet.walletId, + network = network, + ) + val ethereumWalletManager = walletManager as? EthereumWalletManager + ?: raiseIllegalStateError("WalletManager type ${walletManager?.javaClass?.name} not supported") + return ethereumWalletManager + } + + private suspend fun Raise.selectFeePaymentStrategy( + userWallet: UserWallet, + walletManager: EthereumWalletManager, + nativeCurrency: CryptoCurrency, + network: Network, + initialFee: TransactionFee, + ): TransactionFeeExtended { + val feeValue = initialFee.normal.amount.value ?: raise(GetFeeError.UnknownError) + + val userCurrenciesStatuses = getMultiCryptoCurrencyStatusUseCase.invokeMultiWalletSync( + userWallet.walletId, + ).getOrNull() ?: raiseIllegalStateError("currencies list is null for userWalletId=${userWallet.walletId}") + + val networkCurrenciesStatuses = userCurrenciesStatuses.filter { + it.currency.network.id == network.id + } + + val nativeCurrencyStatus = networkCurrenciesStatuses.find { + it.currency.id == nativeCurrency.id + } ?: raiseIllegalStateError("native currency not found for network ${network.id}") + + val nativeBalance = nativeCurrencyStatus.value.amount ?: BigDecimal.ZERO + return if (nativeBalance >= feeValue) { + TransactionFeeExtended(transactionFee = initialFee, feeTokenId = nativeCurrencyStatus.currency.id) + } else { + findTokensToPayFee( + walletManager = walletManager, + initialTxFee = initialFee, + nativeCurrencyStatus = nativeCurrencyStatus, + networkCurrenciesStatuses = networkCurrenciesStatuses, + ) + } + } + + @Suppress("NullableToStringCall") + private suspend fun Raise.findTokensToPayFee( + walletManager: EthereumWalletManager, + initialTxFee: TransactionFee, + nativeCurrencyStatus: CryptoCurrencyStatus, + networkCurrenciesStatuses: List, + ): TransactionFeeExtended { + val initialFee = initialTxFee.normal as? Fee.Ethereum + ?: raiseIllegalStateError( + error = "only Fee.Ethereum supported, but was ${initialTxFee.normal::class.qualifiedName}", + ) + + val supportedGaslessTokens = gaslessTransactionRepository.getSupportedTokens( + network = nativeCurrencyStatus.currency.network, + ).mapNotNull { currency -> + (currency as? CryptoCurrency.Token)?.contractAddress + }.toSet() + + val supportedGaslessTokensStatusesSortedByBalanceDesc = networkCurrenciesStatuses + .filterNot { it.value.amount == BigDecimal.ZERO || it.currency !is CryptoCurrency.Token } + .sortedByDescending { it.value.amount } + .filter { status -> + val token = status.currency as? CryptoCurrency.Token ?: return@filter false + token.contractAddress.lowercase() in supportedGaslessTokens + } + + /** + * Selects token with highest balance to maximize chances of successful fee payment. + * Returns null if no suitable tokens found. + */ + val tokenForPayFeeStatus = supportedGaslessTokensStatusesSortedByBalanceDesc.firstOrNull() + ?: raise(GaslessError.NoSupportedTokensFound) + + return tokenFeeCalculator.calculateTokenFee( + walletManager = walletManager, + tokenForPayFeeStatus = tokenForPayFeeStatus, + nativeCurrencyStatus = nativeCurrencyStatus, + initialFee = initialFee, + ).bind() + } +} \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForTokenUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForTokenUseCase.kt index de5f21becf..33e6864487 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForTokenUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForTokenUseCase.kt @@ -9,6 +9,7 @@ import com.tangem.blockchain.extensions.Result import com.tangem.domain.demo.DemoTransactionSender import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase @@ -38,24 +39,15 @@ class EstimateFeeForTokenUseCase( suspend operator fun invoke( userWallet: UserWallet, - token: CryptoCurrency, + tokenCurrencyStatus: CryptoCurrencyStatus, amount: BigDecimal, ): Either { return either { + val token = tokenCurrencyStatus.currency if (!gaslessTransactionRepository.isNetworkSupported(token.network)) { raise(GetFeeError.GaslessError.NetworkIsNotSupported) } - val userCurrenciesStatusesByNetwork = getMultiCryptoCurrencyStatusUseCase.invokeMultiWalletSync( - userWallet.walletId, - ).getOrNull()?.filter { - it.currency.network.id == token.network.id - } ?: raiseIllegalStateError("currencies list is null for userWalletId=${userWallet.walletId}") - - val tokenCurrencyStatus = userCurrenciesStatusesByNetwork.find { - it.currency.id == token.id - } ?: raiseIllegalStateError("token currency not found for network ${token.network.id}") - val amountData = amount.convertToSdkAmount(tokenCurrencyStatus) val result = if (userWallet is UserWallet.Cold && demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId) @@ -89,6 +81,12 @@ class EstimateFeeForTokenUseCase( derivationPath = token.network.derivationPath, ) + val userCurrenciesStatusesByNetwork = getMultiCryptoCurrencyStatusUseCase.invokeMultiWalletSync( + userWallet.walletId, + ).getOrNull()?.filter { + it.currency.network.id == token.network.id + } ?: raiseIllegalStateError("currencies list is null for userWalletId=${userWallet.walletId}") + val nativeCurrencyStatus = userCurrenciesStatusesByNetwork.find { it.currency.id == nativeCurrency.id } ?: raiseIllegalStateError("native currency not found for network ${token.network.id}") diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt index 612cd7cb24..8934f4fb11 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt @@ -23,6 +23,7 @@ import com.tangem.domain.transaction.error.GetFeeError.GaslessError import com.tangem.domain.transaction.error.mapToFeeError import com.tangem.domain.transaction.models.TransactionFeeExtended import com.tangem.domain.transaction.raiseIllegalStateError +import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.walletmanager.WalletManagersFacade import java.math.BigDecimal import java.math.RoundingMode @@ -56,6 +57,37 @@ internal class TokenFeeCalculator( } } + suspend fun estimateInitialFee( + userWallet: UserWallet, + amount: BigDecimal, + tokenCurrencyStatus: CryptoCurrencyStatus, + ): Either { + return either { + val network = tokenCurrencyStatus.currency.network + val amountData = amount.convertToSdkAmount(tokenCurrencyStatus) + val result = if (userWallet is UserWallet.Cold && + demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId) + ) { + demoTransactionSender(userWallet, network).estimateFee( + amount = amountData, + destination = "", + ) + } else { + walletManagersFacade.estimateFee( + amount = amountData, + userWalletId = userWallet.walletId, + network = network, + ) + } + + when (result) { + is Result.Success -> result.data + is Result.Failure -> raise(result.mapToFeeError()) + null -> raise(GetFeeError.UnknownError) + } + } + } + suspend fun calculateTokenFee( walletManager: EthereumWalletManager, tokenForPayFeeStatus: CryptoCurrencyStatus, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt index a81a280eda..5d53b1d40e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt @@ -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, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt index ead2a200fb..72cfe665f9 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt @@ -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 { + 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 } } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt index 15b59a6ef4..50a94cdb36 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt @@ -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()