From 5e8c334e3dd5e25fe1438fde75b4714d880f4b0c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 May 2026 23:28:30 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../feature/swap/domain/SwapInteractorImpl.kt | 116 ++++++++++++++++++ .../feature/swap/domain/fee/CexFeeResult.kt | 2 - .../swap/domain/fee/CexSwapFeeCalculator.kt | 1 - .../feature/swap/domain/fee/DexFeeResult.kt | 1 - .../swap/domain/fee/DexSwapFeeCalculator.kt | 1 - .../domain/SwapInteractorImplOnSwapTest.kt | 0 .../domain/fee/CexSwapFeeCalculatorTest.kt | 1 - .../domain/fee/DexSwapFeeCalculatorTest.kt | 1 - ...SwapNotificationsFactoryFeeWarningsTest.kt | 11 -- 9 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplOnSwapTest.kt diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index b1b03a0bb9..1f7f07025b 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -1219,6 +1219,122 @@ internal class SwapInteractorImpl @Inject constructor( } } + /** + * [REDACTED_TASK_KEY] — Phase 3 unified fee API. Delegates to [DexSwapFeeCalculator] / + * [CexSwapFeeCalculator] and wraps the result in a [SwapFee]. + * + * Behavior parity with the legacy `loadFeeForSwapTransaction` overloads is intentional — + * the legacy methods stay in place through Phase 4. See `SwapInteractor.loadSwapFee` for + * the full contract. + */ + @Suppress("LongParameterList", "ReturnCount") + override suspend fun loadSwapFee( + provider: SwapProvider, + fromStatus: SwapCurrencyStatus, + toStatus: SwapCurrencyStatus, + amount: SwapAmount, + swapData: SwapDataModel?, + selectedFeeToken: CryptoCurrencyStatus?, + ): Either = either { + if (amount.value.signum() == 0) { + raise(GetFeeError.UnknownError) + } + return when (provider.type) { + ExchangeProviderType.DEX, + ExchangeProviderType.DEX_BRIDGE, + -> loadDexSwapFee( + fromStatus = fromStatus, + swapData = swapData, + selectedFeeToken = selectedFeeToken, + ) + ExchangeProviderType.CEX -> loadCexSwapFee( + fromStatus = fromStatus, + amount = amount, + selectedFeeToken = selectedFeeToken, + ) + } + } + + /** + * [REDACTED_TASK_KEY] — DEX branch of [loadSwapFee]. Pulls the cached `ExpressTransactionModel.DEX` + * out of [swapData] and hands it to [DexSwapFeeCalculator]. Maps [ExpressDataError] → + * `Left(GetFeeError.UnknownError)` to keep the unified surface a single error type, matching + * what the legacy `loadFeeForSwapTransaction` overload 2 does for DEX failures (line 1027 of + * the original code). + */ + private suspend fun loadDexSwapFee( + fromStatus: SwapCurrencyStatus, + swapData: SwapDataModel?, + selectedFeeToken: CryptoCurrencyStatus?, + ): Either { + val transaction = swapData?.transaction as? ExpressTransactionModel.DEX + ?: return GetFeeError.UnknownError.left() + + return dexSwapFeeCalculator.calculate( + fromSwapCurrencyStatus = fromStatus, + transaction = transaction, + selectedToken = selectedFeeToken, + ).fold( + ifLeft = { GetFeeError.UnknownError.left() }, + ifRight = { dexFeeResult -> + val feeToken = selectedFeeToken + ?: resolveNativeFeeTokenStatus(fromStatus) + ?: return@fold GetFeeError.UnknownError.left() + SwapFeeFactory.from( + transactionFeeResult = dexFeeResult.transactionFee, + selectedFeeToken = feeToken, + otherNativeFee = dexFeeResult.otherNativeFee, + feeBucket = FeeBucket.MARKET, + ).right() + }, + ) + } + + /** + * [REDACTED_TASK_KEY] — CEX branch of [loadSwapFee]. Native-fallback behaviour is preserved: when + * [selectedFeeToken] is null the gasless use case (invoked inside [CexSwapFeeCalculator]) + * decides native vs token. The resulting `SwapFee.selectedFeeToken` is the explicit choice + * if provided, otherwise the native coin status of the from-token's network. + */ + private suspend fun loadCexSwapFee( + fromStatus: SwapCurrencyStatus, + amount: SwapAmount, + selectedFeeToken: CryptoCurrencyStatus?, + ): Either { + return cexSwapFeeCalculator.calculate( + userWallet = fromStatus.userWallet, + fromSwapCurrencyStatus = fromStatus, + amount = amount.value, + selectedFeeToken = selectedFeeToken, + ).fold( + ifLeft = { it.left() }, + ifRight = { cexFeeResult -> + val feeToken = selectedFeeToken + ?: resolveNativeFeeTokenStatus(fromStatus) + ?: return@fold GetFeeError.UnknownError.left() + SwapFeeFactory.from( + transactionFeeResult = cexFeeResult.transactionFee, + selectedFeeToken = feeToken, + otherNativeFee = BigDecimal.ZERO, + feeBucket = FeeBucket.MARKET, + ).right() + }, + ) + } + + /** + * [REDACTED_TASK_KEY] — resolves the native-coin [CryptoCurrencyStatus] for the from-token's network. + * Used as the default `selectedFeeToken` of [SwapFee] when the caller did not provide an + * explicit choice. Mirrors how `SwapModel.updateFeePaidCryptoCurrencyFor` populates + * `dataState.feePaidCryptoCurrency`. + */ + private suspend fun resolveNativeFeeTokenStatus(fromStatus: SwapCurrencyStatus): CryptoCurrencyStatus? { + return getFeePaidCryptoCurrencyStatusSyncUseCase( + userWalletId = fromStatus.userWalletId, + cryptoCurrencyStatus = fromStatus.status, + ).getOrNull() + } + private suspend fun storeLastCryptoCurrencyId(swapCurrencyStatus: SwapCurrencyStatus) { swapTransactionRepository.storeLastSwappedCryptoCurrencyId( userWalletId = swapCurrencyStatus.userWalletId, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexFeeResult.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexFeeResult.kt index a007cd936a..3de142fe45 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexFeeResult.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexFeeResult.kt @@ -1,7 +1,5 @@ package com.tangem.feature.swap.domain.fee -import com.tangem.feature.swap.domain.TransactionFeeResult - /** * Result of calculating the CEX swap transaction fee. * diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculator.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculator.kt index e80a72c653..a352fd73a7 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculator.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculator.kt @@ -10,7 +10,6 @@ import com.tangem.domain.transaction.error.GetFeeError 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.feature.swap.domain.TransactionFeeResult import java.math.BigDecimal /** diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexFeeResult.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexFeeResult.kt index 68d2bc0b6b..b6d6f767e4 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexFeeResult.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexFeeResult.kt @@ -1,6 +1,5 @@ package com.tangem.feature.swap.domain.fee -import com.tangem.feature.swap.domain.TransactionFeeResult import java.math.BigDecimal import java.math.BigInteger diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculator.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculator.kt index 16bb399101..1509a7338f 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculator.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculator.kt @@ -19,7 +19,6 @@ import com.tangem.domain.transaction.usecase.GetEthSpecificFeeUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.feature.swap.domain.TransactionFeeResult import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.domain.ExpressTransactionModel import com.tangem.lib.crypto.BlockchainUtils.SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES diff --git a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplOnSwapTest.kt b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplOnSwapTest.kt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculatorTest.kt b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculatorTest.kt index b4d58bbaba..6d4e06580a 100644 --- a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculatorTest.kt +++ b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/CexSwapFeeCalculatorTest.kt @@ -16,7 +16,6 @@ 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.feature.swap.domain.TransactionFeeResult import com.tangem.feature.swap.domain.buildSwapCurrencyStatus import io.mockk.* import kotlinx.coroutines.test.runTest diff --git a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculatorTest.kt b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculatorTest.kt index aa834fe764..6abb4735b4 100644 --- a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculatorTest.kt +++ b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/fee/DexSwapFeeCalculatorTest.kt @@ -21,7 +21,6 @@ import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.feature.swap.domain.fee.PatchEthGasLimitForSwap import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.feature.swap.domain.TransactionFeeResult import com.tangem.feature.swap.domain.buildSwapCurrencyStatus import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount diff --git a/features/swap/impl/src/test/java/com/tangem/feature/swap/SwapNotificationsFactoryFeeWarningsTest.kt b/features/swap/impl/src/test/java/com/tangem/feature/swap/SwapNotificationsFactoryFeeWarningsTest.kt index cb55c0b5a5..02fae0c38b 100644 --- a/features/swap/impl/src/test/java/com/tangem/feature/swap/SwapNotificationsFactoryFeeWarningsTest.kt +++ b/features/swap/impl/src/test/java/com/tangem/feature/swap/SwapNotificationsFactoryFeeWarningsTest.kt @@ -103,7 +103,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -129,7 +128,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -155,7 +153,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -181,7 +178,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = true, ) @@ -207,7 +203,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -237,7 +232,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -265,7 +259,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -296,7 +289,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -322,7 +314,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -351,7 +342,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, ) @@ -378,7 +368,6 @@ internal class SwapNotificationsFactoryFeeWarningsTest { quoteModel = quoteModel, feeCryptoCurrencyStatus = feeStatus, selectedFeeType = FeeType.NORMAL, - providerName = "TestProvider", hideFee = false, )