Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-29 21:24:41 +05:00
parent 9e3a352ff6
commit 8b69fb70dc
10 changed files with 56 additions and 25 deletions

View file

@ -68,7 +68,7 @@
"version": "undefined" "version": "undefined"
}, },
{ {
"name": "SWAP_INTEGRATED_APPROVE", "name": "AND_15120_SWAP_INTEGRATED_APPROVE",
"version": "undefined" "version": "undefined"
}, },
{ {

View file

@ -50,7 +50,6 @@ internal class FeatureTogglesNamingConventionTest {
"SOLANA_TX_HISTORY_ENABLED", "SOLANA_TX_HISTORY_ENABLED",
"STAKING_ETH_ENABLED", "STAKING_ETH_ENABLED",
"SWAP_AB_ENABLED", "SWAP_AB_ENABLED",
"SWAP_INTEGRATED_APPROVE",
"USEDESK_ENABLED", "USEDESK_ENABLED",
"VIRTUAL_ACCOUNTS_ENABLED", "VIRTUAL_ACCOUNTS_ENABLED",
"VISA_ONBOARDING_ENABLED", "VISA_ONBOARDING_ENABLED",

View file

@ -30,13 +30,13 @@ interface SwapTransferInteractor {
suspend fun loadFee( suspend fun loadFee(
fromSwapCurrencyStatus: SwapCurrencyStatus, fromSwapCurrencyStatus: SwapCurrencyStatus,
toSwapCurrencyStatus: SwapCurrencyStatus, toSwapCurrencyStatus: SwapCurrencyStatus,
fromTokenAmount: String, fromTokenAmount: BigDecimal,
): Either<GetFeeError, TransactionFee> ): Either<GetFeeError, TransactionFee>
suspend fun loadFeeExtended( suspend fun loadFeeExtended(
fromSwapCurrencyStatus: SwapCurrencyStatus, fromSwapCurrencyStatus: SwapCurrencyStatus,
toSwapCurrencyStatus: SwapCurrencyStatus, toSwapCurrencyStatus: SwapCurrencyStatus,
fromTokenAmount: String, fromTokenAmount: BigDecimal,
): Either<GetFeeError, TransactionFeeExtended> ): Either<GetFeeError, TransactionFeeExtended>
suspend fun sendTransfer( suspend fun sendTransfer(

View file

@ -205,15 +205,14 @@ class SwapTransferInteractorImpl @Inject constructor(
override suspend fun loadFee( override suspend fun loadFee(
fromSwapCurrencyStatus: SwapCurrencyStatus, fromSwapCurrencyStatus: SwapCurrencyStatus,
toSwapCurrencyStatus: SwapCurrencyStatus, toSwapCurrencyStatus: SwapCurrencyStatus,
fromTokenAmount: String, fromTokenAmount: BigDecimal,
): Either<GetFeeError, TransactionFee> { ): Either<GetFeeError, TransactionFee> {
val amount = fromTokenAmount.parseBigDecimalOrNull() ?: BigDecimal.ZERO
val destination = toSwapCurrencyStatus.destinationAddress() ?: return feeDataError( val destination = toSwapCurrencyStatus.destinationAddress() ?: return feeDataError(
message = "Destination address is null", message = "Destination address is null",
) )
return getFeeUseCase( return getFeeUseCase(
amount = amount, amount = fromTokenAmount,
destination = destination, destination = destination,
userWallet = fromSwapCurrencyStatus.userWallet, userWallet = fromSwapCurrencyStatus.userWallet,
cryptoCurrency = fromSwapCurrencyStatus.currency, cryptoCurrency = fromSwapCurrencyStatus.currency,
@ -223,9 +222,8 @@ class SwapTransferInteractorImpl @Inject constructor(
override suspend fun loadFeeExtended( override suspend fun loadFeeExtended(
fromSwapCurrencyStatus: SwapCurrencyStatus, fromSwapCurrencyStatus: SwapCurrencyStatus,
toSwapCurrencyStatus: SwapCurrencyStatus, toSwapCurrencyStatus: SwapCurrencyStatus,
fromTokenAmount: String, fromTokenAmount: BigDecimal,
): Either<GetFeeError, TransactionFeeExtended> { ): Either<GetFeeError, TransactionFeeExtended> {
val amount = fromTokenAmount.parseBigDecimalOrNull() ?: BigDecimal.ZERO
val destination = toSwapCurrencyStatus.destinationAddress() ?: return feeDataError( val destination = toSwapCurrencyStatus.destinationAddress() ?: return feeDataError(
message = "Destination address is null", message = "Destination address is null",
) )
@ -233,7 +231,7 @@ class SwapTransferInteractorImpl @Inject constructor(
val currency = fromSwapCurrencyStatus.currency val currency = fromSwapCurrencyStatus.currency
val transactionData = createTransferTransactionUseCase( val transactionData = createTransferTransactionUseCase(
amount = amount.convertToSdkAmount( amount = fromTokenAmount.convertToSdkAmount(
cryptoCurrencyStatus = fromSwapCurrencyStatus.status, cryptoCurrencyStatus = fromSwapCurrencyStatus.status,
), ),
memo = null, memo = null,

View file

@ -133,7 +133,17 @@ internal class SwapTransferInteractorImplTest {
every { getBalanceHidingSettingsUseCase.isBalanceHidden() } returns flowOf(true) every { getBalanceHidingSettingsUseCase.isBalanceHidden() } returns flowOf(true)
coEvery { isAccountsModeEnabledUseCase.invokeSync() } returns true coEvery { isAccountsModeEnabledUseCase.invokeSync() } returns true
val currencyCheck = buildCurrencyCheck() val currencyCheck = buildCurrencyCheck()
coEvery { getCurrencyCheckUseCase(any(), any(), any(), any(), any(), any(), any()) } returns currencyCheck coEvery {
getCurrencyCheckUseCase(
userWalletId = any(),
currencyStatus = any(),
feeCurrencyStatus = any(),
amount = any(),
fee = any(),
feeCurrencyBalanceAfterTransaction = any(),
recipientAddress = any(),
)
} returns currencyCheck
coEvery { coEvery {
isAmountSubtractAvailableUseCase(any(), any(), any()) isAmountSubtractAvailableUseCase(any(), any(), any())
} returns false.right() } returns false.right()
@ -193,7 +203,17 @@ internal class SwapTransferInteractorImplTest {
every { getBalanceHidingSettingsUseCase.isBalanceHidden() } returns flowOf(true) every { getBalanceHidingSettingsUseCase.isBalanceHidden() } returns flowOf(true)
coEvery { isAccountsModeEnabledUseCase.invokeSync() } returns true coEvery { isAccountsModeEnabledUseCase.invokeSync() } returns true
val currencyCheck = buildCurrencyCheck() val currencyCheck = buildCurrencyCheck()
coEvery { getCurrencyCheckUseCase(any(), any(), any(), any(), any(), any(), any()) } returns currencyCheck coEvery {
getCurrencyCheckUseCase(
userWalletId = any(),
currencyStatus = any(),
feeCurrencyStatus = any(),
amount = any(),
fee = any(),
feeCurrencyBalanceAfterTransaction = any(),
recipientAddress = any(),
)
} returns currencyCheck
coEvery { coEvery {
isAmountSubtractAvailableUseCase(any(), any(), any()) isAmountSubtractAvailableUseCase(any(), any(), any())
} returns false.right() } returns false.right()
@ -259,7 +279,15 @@ internal class SwapTransferInteractorImplTest {
every { getBalanceHidingSettingsUseCase.isBalanceHidden() } returns flowOf(false) every { getBalanceHidingSettingsUseCase.isBalanceHidden() } returns flowOf(false)
coEvery { isAccountsModeEnabledUseCase.invokeSync() } returns false coEvery { isAccountsModeEnabledUseCase.invokeSync() } returns false
coEvery { coEvery {
getCurrencyCheckUseCase(any(), any(), any(), any(), any(), any(), any()) getCurrencyCheckUseCase(
userWalletId = any(),
currencyStatus = any(),
feeCurrencyStatus = any(),
amount = any(),
fee = any(),
feeCurrencyBalanceAfterTransaction = any(),
recipientAddress = any(),
)
} returns buildCurrencyCheck() } returns buildCurrencyCheck()
coEvery { coEvery {
isAmountSubtractAvailableUseCase(any(), any(), any()) isAmountSubtractAvailableUseCase(any(), any(), any())
@ -309,7 +337,7 @@ internal class SwapTransferInteractorImplTest {
val result = sut.loadFee( val result = sut.loadFee(
fromSwapCurrencyStatus = fromCurrencyStatus, fromSwapCurrencyStatus = fromCurrencyStatus,
toSwapCurrencyStatus = toCurrencyStatus, toSwapCurrencyStatus = toCurrencyStatus,
fromTokenAmount = "1.5", fromTokenAmount = BigDecimal("1.5"),
) )
assertThat(result).isEqualTo(transactionFee.right()) assertThat(result).isEqualTo(transactionFee.right())
@ -365,7 +393,7 @@ internal class SwapTransferInteractorImplTest {
val result = sut.loadFeeExtended( val result = sut.loadFeeExtended(
fromSwapCurrencyStatus = fromCurrencyStatus, fromSwapCurrencyStatus = fromCurrencyStatus,
toSwapCurrencyStatus = toCurrencyStatus, toSwapCurrencyStatus = toCurrencyStatus,
fromTokenAmount = "2.0", fromTokenAmount = BigDecimal("2.0"),
) )
assertThat(result).isEqualTo(feeExtended.right()) assertThat(result).isEqualTo(feeExtended.right())

View file

@ -18,7 +18,7 @@ internal class DefaultSwapFeatureToggles @Inject constructor(
) )
override val isSwapIntegratedApproveEnabled: Boolean = featureTogglesManager.isFeatureEnabled( override val isSwapIntegratedApproveEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.SWAP_INTEGRATED_APPROVE, toggle = FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE,
) )
override val isSwapAbEnabled: Boolean = featureTogglesManager.isFeatureEnabled( override val isSwapAbEnabled: Boolean = featureTogglesManager.isFeatureEnabled(

View file

@ -2180,6 +2180,7 @@ internal class SwapModel @Inject constructor(
dataState.fromSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError) dataState.fromSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError)
val toSwapCurrencyStatus = val toSwapCurrencyStatus =
dataState.toSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError) dataState.toSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError)
val amount = lastAmount.value.parseBigDecimalOrNull() ?: return Either.Left(GetFeeError.UnknownError)
val shouldTransferInsteadOfSwap = swapTransferInteractor.shouldTransferInsteadOfSwap( val shouldTransferInsteadOfSwap = swapTransferInteractor.shouldTransferInsteadOfSwap(
fromSwapCurrencyStatus.currency, fromSwapCurrencyStatus.currency,
toSwapCurrencyStatus.currency, toSwapCurrencyStatus.currency,
@ -2188,7 +2189,7 @@ internal class SwapModel @Inject constructor(
return swapTransferInteractor.loadFee( return swapTransferInteractor.loadFee(
fromSwapCurrencyStatus = fromSwapCurrencyStatus, fromSwapCurrencyStatus = fromSwapCurrencyStatus,
toSwapCurrencyStatus = toSwapCurrencyStatus, toSwapCurrencyStatus = toSwapCurrencyStatus,
fromTokenAmount = lastAmount.value, fromTokenAmount = amount,
).onLeft { ).onLeft {
TangemLogger.e("loadFee[transfer]: Failed to load fee with error $it") TangemLogger.e("loadFee[transfer]: Failed to load fee with error $it")
}.onRight { }.onRight {
@ -2202,9 +2203,7 @@ internal class SwapModel @Inject constructor(
return Either.Left(GetFeeError.UnknownError) return Either.Left(GetFeeError.UnknownError)
} }
val amountDecimal = lastAmount.value.replace(",", ".").toBigDecimalOrNull() val swapAmount = SwapAmount(amount, fromSwapCurrencyStatus.currency.decimals)
?: return Either.Left(GetFeeError.UnknownError)
val swapAmount = SwapAmount(amountDecimal, fromSwapCurrencyStatus.currency.decimals)
val swapDataForCall = when (quoteState.swapProvider.type) { val swapDataForCall = when (quoteState.swapProvider.type) {
ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE -> { ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE -> {
quoteState.swapDataModel ?: return Either.Left(GetFeeError.UnknownError) quoteState.swapDataModel ?: return Either.Left(GetFeeError.UnknownError)
@ -2235,6 +2234,8 @@ internal class SwapModel @Inject constructor(
dataState.fromSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError) dataState.fromSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError)
val toSwapCurrencyStatus = val toSwapCurrencyStatus =
dataState.toSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError) dataState.toSwapCurrencyStatus ?: return Either.Left(GetFeeError.UnknownError)
val amount = lastAmount.value.parseBigDecimalOrNull() ?: return Either.Left(GetFeeError.UnknownError)
val shouldTransferInsteadOfSwap = swapTransferInteractor.shouldTransferInsteadOfSwap( val shouldTransferInsteadOfSwap = swapTransferInteractor.shouldTransferInsteadOfSwap(
fromSwapCurrencyStatus.currency, fromSwapCurrencyStatus.currency,
toSwapCurrencyStatus.currency, toSwapCurrencyStatus.currency,
@ -2243,7 +2244,7 @@ internal class SwapModel @Inject constructor(
return swapTransferInteractor.loadFeeExtended( return swapTransferInteractor.loadFeeExtended(
fromSwapCurrencyStatus = fromSwapCurrencyStatus, fromSwapCurrencyStatus = fromSwapCurrencyStatus,
toSwapCurrencyStatus = toSwapCurrencyStatus, toSwapCurrencyStatus = toSwapCurrencyStatus,
fromTokenAmount = lastAmount.value, fromTokenAmount = amount,
) )
} }
val quoteState = dataState.getCurrentLoadedSwapState() ?: return Either.Left(GetFeeError.UnknownError) val quoteState = dataState.getCurrentLoadedSwapState() ?: return Either.Left(GetFeeError.UnknownError)
@ -2252,8 +2253,7 @@ internal class SwapModel @Inject constructor(
return Either.Left(GetFeeError.UnknownError) return Either.Left(GetFeeError.UnknownError)
} }
val amountDecimal = lastAmount.value.parseBigDecimalOrNull() ?: return Either.Left(GetFeeError.UnknownError) val swapAmount = SwapAmount(amount, fromSwapCurrencyStatus.currency.decimals)
val swapAmount = SwapAmount(amountDecimal, fromSwapCurrencyStatus.currency.decimals)
// DEX path requires a SwapDataModel. // DEX path requires a SwapDataModel.
val swapDataForCall = when (quoteState.swapProvider.type) { val swapDataForCall = when (quoteState.swapProvider.type) {

View file

@ -5,7 +5,7 @@
# https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico # https://github.com/tangem/vico
tangemBlockchainSdk = "develop-1532" tangemBlockchainSdk = "develop-1535"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-620" tangemCardSdk = "develop-620"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^

View file

@ -20,6 +20,7 @@ import javax.inject.Inject
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
@Suppress("LongParameterList")
internal class WalletManagerFactoryCreator @Inject constructor( internal class WalletManagerFactoryCreator @Inject constructor(
private val accountCreator: AccountCreator, private val accountCreator: AccountCreator,
private val blockchainDataStorage: BlockchainDataStorage, private val blockchainDataStorage: BlockchainDataStorage,
@ -41,6 +42,7 @@ internal class WalletManagerFactoryCreator @Inject constructor(
isSolanaTxHistoryEnabled = featureToggleValues.isSolanaTxHistoryEnabled, isSolanaTxHistoryEnabled = featureToggleValues.isSolanaTxHistoryEnabled,
isSolanaScaledUiAmountEnabled = featureToggleValues.isSolanaScaledUiAmountEnabled, isSolanaScaledUiAmountEnabled = featureToggleValues.isSolanaScaledUiAmountEnabled,
isHederaErc20Enabled = featureToggleValues.isHederaErc20Enabled, isHederaErc20Enabled = featureToggleValues.isHederaErc20Enabled,
isStateOverrideGasEstimateEnabled = featureToggleValues.isStateOverrideGasEstimateEnabled,
), ),
blockchainDataStorage = blockchainDataStorage, blockchainDataStorage = blockchainDataStorage,
loggers = listOf(blockchainSDKLogger), loggers = listOf(blockchainSDKLogger),
@ -52,5 +54,6 @@ internal class WalletManagerFactoryCreator @Inject constructor(
val isSolanaScaledUiAmountEnabled: Boolean, val isSolanaScaledUiAmountEnabled: Boolean,
val isYieldModeSwapEnabled: Boolean, val isYieldModeSwapEnabled: Boolean,
val isHederaErc20Enabled: Boolean, val isHederaErc20Enabled: Boolean,
val isStateOverrideGasEstimateEnabled: Boolean,
) )
} }

View file

@ -23,8 +23,8 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.config.environment.EnvironmentConfig import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.libs.blockchain_sdk.BuildConfig import com.tangem.libs.blockchain_sdk.BuildConfig
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -110,6 +110,9 @@ internal object BlockchainSDKFactoryModule {
isHederaErc20Enabled = featureTogglesManager.isFeatureEnabled( isHederaErc20Enabled = featureTogglesManager.isFeatureEnabled(
FeatureToggles.HEDERA_ERC20_ENABLED, FeatureToggles.HEDERA_ERC20_ENABLED,
), ),
isStateOverrideGasEstimateEnabled = featureTogglesManager.isFeatureEnabled(
FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE,
),
), ),
) )
} }