From c8b7934d931255bb20a4040b3007226059f078bd Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 10 Jan 2026 14:08:10 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TransactionDomainModule.kt | 19 ++- .../api/gasless/GaslessTxServiceApi.kt | 2 +- .../GaslessTxDataToGaslessRequestConverter.kt | 3 +- .../CreateAndSendGaslessTransactionUseCase.kt | 5 - .../gasless/EstimateFeeForTokenUseCase.kt | 131 ++++++++++++++++++ 5 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/EstimateFeeForTokenUseCase.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 9f0811eff8..b0b821fffb 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 @@ -26,7 +26,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import javax.inject.Singleton -@Suppress("TooManyFunctions") +@Suppress("TooManyFunctions", "LargeClass") @Module @InstallIn(SingletonComponent::class) internal object TransactionDomainModule { @@ -346,4 +346,21 @@ internal object TransactionDomainModule { getHotWalletSigner = tangemHotWalletSignerFactory::create, ) } + + @Provides + @Singleton + fun provideEstimateFeeForTokenUseCase( + walletManagersFacade: WalletManagersFacade, + gaslessTransactionRepository: GaslessTransactionRepository, + currenciesRepository: CurrenciesRepository, + getMultiCryptoCurrencyStatusUseCase: GetMultiCryptoCurrencyStatusUseCase, + ): EstimateFeeForTokenUseCase { + return EstimateFeeForTokenUseCase( + gaslessTransactionRepository = gaslessTransactionRepository, + walletManagersFacade = walletManagersFacade, + demoConfig = DemoConfig, + currenciesRepository = currenciesRepository, + getMultiCryptoCurrencyStatusUseCase = getMultiCryptoCurrencyStatusUseCase, + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt index 193dc620e8..72b01f4139 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt @@ -14,7 +14,7 @@ interface GaslessTxServiceApi { @GET("api/v1/tokens") suspend fun getSupportedTokens(): ApiResponse> - @POST("api/v1/sign") + @POST("api/v1/transaction/sign") suspend fun signGaslessTransaction( @Body transaction: GaslessTransactionRequest, ): ApiResponse> diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/convertes/GaslessTxDataToGaslessRequestConverter.kt b/data/transaction/src/main/java/com/tangem/data/transaction/convertes/GaslessTxDataToGaslessRequestConverter.kt index 44005be4c1..1d20602465 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/convertes/GaslessTxDataToGaslessRequestConverter.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/convertes/GaslessTxDataToGaslessRequestConverter.kt @@ -1,5 +1,6 @@ package com.tangem.data.transaction.convertes +import com.tangem.blockchain.extensions.formatHex import com.tangem.datasource.api.gasless.models.FeeData import com.tangem.datasource.api.gasless.models.TransactionData import com.tangem.domain.transaction.models.GaslessTransactionData @@ -27,7 +28,7 @@ class GaslessTxDataToGaslessRequestConverter : Converter { + return either { + 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) + ) { + demoTransactionSender(userWallet, token).estimateFee( + amount = amountData, + destination = "", + ) + } else { + walletManagersFacade.estimateFee( + amount = amountData, + userWalletId = userWallet.walletId, + network = token.network, + ) + } + + val initialTxFee = when (result) { + is Result.Success -> result.data + is Result.Failure -> raise(result.mapToFeeError()) + null -> raise(GetFeeError.UnknownError) + } + + val initialFeeEth = initialTxFee.normal as? Fee.Ethereum + ?: raiseIllegalStateError( + error = "only Fee.Ethereum supported, but was different", + ) + + val nativeCurrency = currenciesRepository.getNetworkCoin( + userWalletId = userWallet.walletId, + networkId = token.network.id, + derivationPath = token.network.derivationPath, + ) + + val nativeCurrencyStatus = userCurrenciesStatusesByNetwork.find { + it.currency.id == nativeCurrency.id + } ?: raiseIllegalStateError("native currency not found for network ${token.network.id}") + + val walletManager = prepareWalletManager(userWallet, token.network) + + tokenFeeCalculator.calculateTokenFee( + walletManager = walletManager, + tokenForPayFeeStatus = tokenCurrencyStatus, + nativeCurrencyStatus = nativeCurrencyStatus, + initialFee = initialFeeEth, + ).bind() + } + } + + @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 demoTransactionSender( + userWallet: UserWallet, + cryptoCurrency: CryptoCurrency, + ): DemoTransactionSender { + return DemoTransactionSender( + walletManagersFacade + .getOrCreateWalletManager(userWallet.walletId, cryptoCurrency.network) + ?: error("WalletManager is null"), + ) + } +} \ No newline at end of file