From ad6cb09999b6768e67bccb59a5384848033ded1a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 29 Jun 2026 03:33:08 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../feature/swap/domain/SwapInteractorImpl.kt | 10 ++++-- ...pInteractorImplLoadDexSwapDataNoFeeTest.kt | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) 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 5df8ab2c15..9e3e2227b4 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 @@ -1343,7 +1343,6 @@ internal class SwapInteractorImpl @Inject constructor( val dexFeeResultEither = if (fromStatus.isYieldSwapActive && fromStatus.currency is CryptoCurrency.Token) { val network = (fromStatus.currency as CryptoCurrency.Token).network val yieldModuleAddress = yieldModuleAddressProvider.getOrFetch(fromStatus.userWalletId, network) - // TODO YIELD [REDACTED_TASK_KEY] dexSwapFeeCalculator.calculateYield( fromSwapCurrencyStatus = fromStatus, transaction = transaction, @@ -1992,7 +1991,11 @@ internal class SwapInteractorImpl @Inject constructor( swapData = swapData, provider = provider, ) - val isIntegratedApprovalNeeded = swapFeatureToggles.isSwapIntegratedApproveEnabled && + + val isYieldSwap = fromSwapCurrencyStatus.isYieldSwapActive && + fromSwapCurrencyStatus.currency is CryptoCurrency.Token + val isIntegratedApprovalNeeded = !isYieldSwap && + swapFeatureToggles.isSwapIntegratedApproveEnabled && allowanceInfo is AllowanceInfo.NotEnough && !hasIntegratedApprovalFallenBack(fromSwapCurrencyStatus, spenderAddress) swapState.copy( @@ -2138,7 +2141,8 @@ internal class SwapInteractorImpl @Inject constructor( requiredAmount = swapAmount.value, ).getOrNull() ?: return quotesLoadedState.copy(permissionState = PermissionDataState.Empty) - val isIntegratedApprovalNeeded = swapFeatureToggles.isSwapIntegratedApproveEnabled && + val isIntegratedApprovalNeeded = !isYieldSwap && + swapFeatureToggles.isSwapIntegratedApproveEnabled && allowanceInfo is AllowanceInfo.NotEnough && !hasIntegratedApprovalFallenBack(fromSwapCurrencyStatus, quoteModel.allowanceContract) return quotesLoadedState.copy( diff --git a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplLoadDexSwapDataNoFeeTest.kt b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplLoadDexSwapDataNoFeeTest.kt index 66736a8e47..5004c017ad 100644 --- a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplLoadDexSwapDataNoFeeTest.kt +++ b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/SwapInteractorImplLoadDexSwapDataNoFeeTest.kt @@ -189,6 +189,38 @@ internal class SwapInteractorImplLoadDexSwapDataNoFeeTest : SwapInteractorImplTe assertThat(state.permissionState).isEqualTo(PermissionDataState.Empty) } + @Test + fun `GIVEN yield swap AND NotEnough allowance AND integrated active THEN permissionState is not integrated`() = + runTest { + // [REDACTED_TASK_KEY] / iOS parity: yield swaps must never use the integrated approve+swap path. + // The yield-module proxy allowance is granted at enrollment, so no in-flow approval is shown. + every { swapFeatureToggles.isSwapIntegratedApproveEnabled } returns true + every { swapFeatureToggles.isYieldSwapEnabled } returns true + coEvery { yieldModuleAddressProvider.getOrFetch(any(), any()) } returns YIELD_PROXY + coEvery { walletManagersFacade.isSwapSpenderAllowed(any(), any(), any()) } returns true + stubAllowance(AllowanceInfo.NotEnough(allowance = BigDecimal.ZERO, requiredAmount = BigDecimal.ONE)) + + val dexProvider = stubDexQuoteAndExchangeData() + val result = sut.findBestQuote( + fromSwapCurrencyStatus = buildSwapCurrencyStatus( + networkRawId = ethNetwork, + isCoin = false, + contractAddress = "0xToken", + amount = BigDecimal("10"), + yieldSupplyActive = true, + ), + toSwapCurrencyStatus = buildSwapCurrencyStatus(networkRawId = ethNetwork), + providers = listOf(dexProvider), + amountToSwap = "1.0", + reduceBalanceBy = BigDecimal.ZERO, + ) + val state = result[dexProvider] as SwapState.QuotesLoadedState + + assertThat(state.permissionState) + .isNotInstanceOf(PermissionDataState.PermissionSettings::class.java) + assertThat(state.permissionState).isEqualTo(PermissionDataState.Empty) + } + @Test fun `GIVEN NotEnough allowance AND integrated toggle OFF THEN does not reach loadDexSwapDataNoFee`() = runTest { // With the integrated toggle off, NotEnough is not allowance-satisfied (requires Enough), @@ -290,5 +322,6 @@ internal class SwapInteractorImplLoadDexSwapDataNoFeeTest : SwapInteractorImplTe private companion object { const val SPENDER = "0xSpender" + const val YIELD_PROXY = "0xYieldProxy" } } \ No newline at end of file