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 5eb7159463..9f0811eff8 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 @@ -6,6 +6,7 @@ import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusSupplier import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase +import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -14,10 +15,7 @@ import com.tangem.domain.transaction.GaslessTransactionRepository import com.tangem.domain.transaction.TransactionRepository import com.tangem.domain.transaction.WalletAddressServiceRepository import com.tangem.domain.transaction.usecase.* -import com.tangem.domain.transaction.usecase.gasless.GetAvailableFeeTokensUseCase -import com.tangem.domain.transaction.usecase.gasless.GetFeeForGaslessUseCase -import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase -import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork +import com.tangem.domain.transaction.usecase.gasless.* import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -330,4 +328,22 @@ internal object TransactionDomainModule { ): IsGaslessFeeSupportedForNetwork { return IsGaslessFeeSupportedForNetwork(gaslessTransactionRepository) } + + @Provides + @Singleton + fun provideCreateAndSendGaslessTransactionUseCase( + walletManagersFacade: WalletManagersFacade, + gaslessTransactionRepository: GaslessTransactionRepository, + getSingCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, + cardSdkConfigRepository: CardSdkConfigRepository, + tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory, + ): CreateAndSendGaslessTransactionUseCase { + return CreateAndSendGaslessTransactionUseCase( + walletManagersFacade = walletManagersFacade, + getSingleCryptoCurrencyStatusUseCase = getSingCryptoCurrencyStatusUseCase, + gaslessTransactionRepository = gaslessTransactionRepository, + cardSdkConfigRepository = cardSdkConfigRepository, + getHotWalletSigner = tangemHotWalletSignerFactory::create, + ) + } } \ No newline at end of file diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt index ef578be698..3706fa2f92 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt @@ -11,6 +11,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.transaction.models.TransactionFeeExtended import com.tangem.features.send.v2.api.R import com.tangem.features.send.v2.api.entity.FeeItem.* import kotlinx.collections.immutable.ImmutableList @@ -66,6 +67,7 @@ data class FeeExtraInfo( val isTronToken: Boolean, val feeCryptoCurrencyStatus: CryptoCurrencyStatus, val availableFeeCurrencies: ImmutableList? = null, + val transactionFeeExtended: TransactionFeeExtended? = null, ) sealed class FeeNonce { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt index 80a1151aa7..5da1137ef5 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt @@ -81,12 +81,14 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor( val isScreenSource = params.feeDisplaySource == FeeSelectorParams.FeeDisplaySource.Screen val isNotSingleFee = (state as? FeeSelectorUM.Content)?.feeItems?.isSingleItem() == false + val isGaslessAvailable = state is FeeSelectorUM.Content && + (state as FeeSelectorUM.Content).feeExtraInfo.transactionFeeExtended != null FeeSelectorBlockContent( state = state, onReadMoreClick = model::onReadMoreClicked, isGaslessFeatureEnabled = sendFeatureToggles.isGaslessTransactionsEnabled, modifier = modifier - .conditional(isScreenSource && isNotSingleFee) { + .conditional(isScreenSource && (isNotSingleFee || isGaslessAvailable)) { Modifier.clickable { model.showFeeSelector() } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt index 0599751766..42ffaa7515 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt @@ -32,12 +32,10 @@ import com.tangem.utils.transformer.update import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @Suppress("LongParameterList") @@ -58,7 +56,6 @@ internal class FeeSelectorLogic @AssistedInject constructor( ) : FeeSelectorIntents { private var appCurrency: AppCurrency = AppCurrency.Default - private var selectedToken: CryptoCurrencyStatus? = null val uiState = MutableStateFlow(params.state) init { @@ -115,34 +112,7 @@ internal class FeeSelectorLogic @AssistedInject constructor( } override fun onTokenSelected(status: CryptoCurrencyStatus) { - if (status.currency !is CryptoCurrency.Token || - status.currency.id == params.feeCryptoCurrencyStatus.currency.id) { - selectedToken = null - loadFee() - return - } uiState.update(FeeSelectorTokenSelectedTransformer(status)) - - uiState.update { state -> - if (state is FeeSelectorUM.Content) { - state.copy( - selectedFeeItem = FeeItem.Loading, - feeItems = persistentListOf(FeeItem.Loading), - feeExtraInfo = state.feeExtraInfo.copy( - feeCryptoCurrencyStatus = status, - ), - ) - } else { - state - } - } - - if (status.currency is CryptoCurrency.Token) { - selectedToken = status - } else { - selectedToken = null - } - loadFee() } @@ -240,6 +210,9 @@ internal class FeeSelectorLogic @AssistedInject constructor( private suspend fun callLoadFee(): Either { val extended = params.onLoadFeeExtended return if (extended != null && sendFeatureToggles.isGaslessTransactionsEnabled) { + val selectedToken = (uiState.value as? FeeSelectorUM.Content)?.feeExtraInfo?.feeCryptoCurrencyStatus + ?.takeIf { it.currency is CryptoCurrency.Token } + extended(selectedToken).fold( ifLeft = { error -> if (error is GetFeeError.GaslessError) { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt index c80fac2068..96ee2a20ad 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt @@ -89,14 +89,11 @@ internal class FeeSelectorModel @Inject constructor( } fun getInitialRoute(): FeeSelectorRoute { - if (sendFeatureToggles.isGaslessTransactionsEnabled.not()) { - return FeeSelectorRoute.ChooseSpeed - } - - return if (isGaslessFeeSupportedForNetwork(params.feeCryptoCurrencyStatus.currency.network)) { - FeeSelectorRoute.NetworkFee - } else { - FeeSelectorRoute.ChooseSpeed + return when { + sendFeatureToggles.isGaslessTransactionsEnabled && params.onLoadFeeExtended != null && + isGaslessFeeSupportedForNetwork(params.feeCryptoCurrencyStatus.currency.network) -> + FeeSelectorRoute.NetworkFee + else -> FeeSelectorRoute.ChooseSpeed } } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt index 0daa9f3a3c..21fe42a28e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt @@ -38,13 +38,17 @@ internal class FeeSelectorLoadedTransformer( } else { null } + val feeItems: ImmutableList = feeItemsConverter.convert( FeeItemConverter.Input(fees.transactionFee, prevCustomFee), ) val selectedFee = when (prevState) { - is FeeSelectorUM.Content -> - feeItems.firstOrNull { it.isSameClass(prevState.selectedFeeItem) } ?: FeeItem.Loading + is FeeSelectorUM.Content -> if (prevState.selectedFeeItem is FeeItem.Loading) { + feeItems.firstOrNull { it is FeeItem.Market } + } else { + feeItems.firstOrNull { it.isSameClass(prevState.selectedFeeItem) } + } ?: FeeItem.Loading is FeeSelectorUM.Error, FeeSelectorUM.Loading, -> feeItems.find { it is FeeItem.Suggested } ?: feeItems.first { it is FeeItem.Market } @@ -64,6 +68,7 @@ internal class FeeSelectorLoadedTransformer( isTron(cryptoCurrencyStatus.currency.network.rawId), feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, availableFeeCurrencies = getAvailableFeeCurrencies(), + transactionFeeExtended = (fees as? LoadedFeeResult.Extended)?.fee, ), feeFiatRateUM = feeCryptoCurrencyStatus.value.fiatRate?.let { rate -> FeeFiatRateUM( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorBlockContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorBlockContent.kt index 19a1c86d96..2024fbf748 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorBlockContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorBlockContent.kt @@ -213,7 +213,10 @@ private fun FeeContent(state: FeeSelectorUM.Content, isGaslessFeatureEnabled: Bo .padding(start = TangemTheme.dimens.spacing4) .testTag(FeeSelectorBlockTestTags.FEE_AMOUNT), ) - if (!state.feeItems.isSingleItem()) { + + val isGaslessAvailable = isGaslessFeatureEnabled && state.feeExtraInfo.transactionFeeExtended != null + + if (!state.feeItems.isSingleItem() || isGaslessAvailable) { Icon( modifier = Modifier .size(width = 18.dp, height = 24.dp) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index 6fcd04be67..15e6d54b17 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -36,6 +36,7 @@ import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository 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.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.utils.convertToSdkAmount import com.tangem.features.send.v2.api.SendNotificationsComponent @@ -111,6 +112,7 @@ internal class SendConfirmModel @Inject constructor( private val accountsFeatureToggles: AccountsFeatureToggles, private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase, private val currenciesRepository: CurrenciesRepository, + private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase, sendBalanceUpdaterFactory: SendBalanceUpdater.Factory, ) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback { @@ -388,11 +390,21 @@ internal class SendConfirmModel @Inject constructor( } private suspend fun sendTransaction(txData: TransactionData.Uncompiled) { - val result = sendTransactionUseCase( - txData = txData, - userWallet = userWallet, - network = cryptoCurrency.network, - ) + val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended + + val result = if (feeExtended != null) { + createAndSendGaslessTransactionUseCase( + userWallet = userWallet, + transactionData = txData, + fee = feeExtended, + ) + } else { + sendTransactionUseCase( + txData = txData, + userWallet = userWallet, + network = cryptoCurrency.network, + ) + } _uiState.update(SendConfirmSendingStateTransformer(isSending = false)) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt index be8621ba5a..7a7d53e4ec 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt @@ -14,6 +14,7 @@ import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkExceedBalance import com.tangem.features.send.v2.impl.R import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList internal class EthereumCustomFeeConverter( @@ -38,12 +39,16 @@ internal class EthereumCustomFeeConverter( ) override fun convert(value: Fee.Ethereum): ImmutableList { + if (value is Fee.Ethereum.TokenCurrency) { + return persistentListOf() + } + return buildList { add(convertFeeValue(value)) addAll( when (value) { - is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]") + is Fee.Ethereum.TokenCurrency -> error("unreachable") is Fee.Ethereum.EIP1559 -> eipFeeConverter.convert(value) is Fee.Ethereum.Legacy -> legacyFeeConverter.convert(value) }, @@ -56,7 +61,7 @@ internal class EthereumCustomFeeConverter( override fun convertBack(normalFee: Fee.Ethereum, value: ImmutableList): Fee.Ethereum { return when (normalFee) { - is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]") + is Fee.Ethereum.TokenCurrency -> error("unreachable") is Fee.Ethereum.EIP1559 -> eipFeeConverter.convertBack(normalFee = normalFee, value = value) is Fee.Ethereum.Legacy -> legacyFeeConverter.convertBack(normalFee = normalFee, value = value) } @@ -64,7 +69,7 @@ internal class EthereumCustomFeeConverter( override fun getGasLimitIndex(feeValue: Fee.Ethereum): Int { return when (feeValue) { - is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]") + is Fee.Ethereum.TokenCurrency -> error("unreachable") is Fee.Ethereum.EIP1559 -> eipFeeConverter.getGasLimitIndex(feeValue) is Fee.Ethereum.Legacy -> legacyFeeConverter.getGasLimitIndex(feeValue) } @@ -77,7 +82,7 @@ internal class EthereumCustomFeeConverter( value: String, ): ImmutableList { return when (feeValue) { - is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]") + is Fee.Ethereum.TokenCurrency -> error("unreachable") is Fee.Ethereum.EIP1559 -> eipFeeConverter.onValueChange( feeValue = feeValue, customValues = customValues,