Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-09 18:41:25 +08:00
commit 768666b267
5 changed files with 97 additions and 67 deletions

View file

@ -46,11 +46,11 @@
},
{
"name": "GASLESS_APPROVAL_ENABLED",
"version": "undefined"
"version": "5.37"
},
{
"name": "TANGEM_PAY_ACCOUNTS_REFACTOR_ENABLED",
"version": "undefined"
"version": "5.37"
},
{
"name": "DYNAMIC_ADDRESSES_ENABLED",
@ -66,7 +66,7 @@
},
{
"name": "NEW_PROMO_BANNERS_ENABLED",
"version": "undefined"
"version": "5.37"
},
{
"name": "VIRTUAL_ACCOUNTS_ENABLED",

View file

@ -15,6 +15,7 @@ import com.tangem.domain.markets.GetTokenMarketCryptoCurrency
import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
@ -27,9 +28,9 @@ import com.tangem.features.feed.components.market.details.portfolio.impl.analyti
import com.tangem.features.feed.impl.R
import com.tangem.features.feed.model.earn.analytics.EarnAnalyticsEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
import javax.inject.Inject
@ -172,12 +173,11 @@ internal class AddToPortfolioPreselectedDataModel @Inject constructor(
val availableToAddInWallets = portfolioData.balances.mapNotNull { (walletId, balance) ->
val wallet = balance.userWallet
val accounts = balance.accountsBalance.accountStatuses
val accounts = balance.accountsBalance.accountStatuses.filterCryptoPortfolio()
val availableToAddAccounts = accounts.mapNotNull { accountStatus ->
val accountIndex = when (accountStatus) {
is AccountStatus.CryptoPortfolio -> accountStatus.account.derivationIndex
is AccountStatus.Payment -> TODO("[REDACTED_JIRA]")
}
val cryptoCurrency = getTokenMarketCryptoCurrency(
@ -224,7 +224,7 @@ internal class AddToPortfolioPreselectedDataModel @Inject constructor(
): CryptoCurrency? {
val accountIndex = when (val accountStatus = account.account) {
is AccountStatus.CryptoPortfolio -> accountStatus.account.derivationIndex
is AccountStatus.Payment -> TODO("[REDACTED_JIRA]")
is AccountStatus.Payment -> return null
}
return getTokenMarketCryptoCurrency(
userWalletId = userWallet.walletId,

View file

@ -200,6 +200,7 @@ internal class SwapAmountModel @Inject constructor(
if (content.swapRateMode != SwapRateMode.FLOAT_ONLY) {
ExpressRateType.Fixed
} else {
onSelectTokenClick()
return
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.swap.v2.impl.amount.model.converter
import com.tangem.common.ui.account.AccountTitleUM
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
import com.tangem.common.ui.amountScreen.converters.AmountAccountConverter
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
@ -66,15 +67,20 @@ internal class SwapAmountFieldConverter(
maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus),
iconStateConverter = iconStateConverter,
isBalanceHidden = isBalanceHidden,
accountTitleUM = AmountAccountConverter(
isAccountsMode = isAccountsMode,
walletTitle = walletTitle,
prefixText = when {
swapAmountType.isEnteringField() -> resourceReference(R.string.common_from)
swapAmountType.isViewingField() -> resourceReference(R.string.common_to)
else -> TextReference.Companion.EMPTY
},
).convert(account),
accountTitleUM = if (swapAmountType.isViewingField()) {
AccountTitleUM.Text(
resourceReference(R.string.send_with_swap_recipient_get_amount, wrappedList("")),
)
} else {
AmountAccountConverter(
isAccountsMode = isAccountsMode,
walletTitle = walletTitle,
prefixText = when {
swapAmountType.isEnteringField() -> resourceReference(R.string.common_from)
else -> TextReference.Companion.EMPTY
},
).convert(account)
},
).convert(
AmountParameters(
title = walletTitle,

View file

@ -72,7 +72,11 @@ import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.supervisorScope
import java.math.BigDecimal
import java.math.BigInteger
import java.math.RoundingMode
@ -346,63 +350,82 @@ internal class SwapInteractorImpl @AssistedInject constructor(
""".trimIndent(),
)
return providers.map { provider ->
val amountDecimal = toBigDecimalOrNull(amountToSwap)
if (amountDecimal == null || amountDecimal.signum() == 0) {
return providers.associateWith { createEmptyAmountState() }
}
val amount = SwapAmount(amountDecimal, fromToken.currency.decimals)
val isBalanceWithoutFeeEnough = when (fromAccount) {
is Account.Payment -> true
else -> isBalanceEnough(fromToken, amount, null)
}
val networkId = fromToken.currency.network.backendId
when (provider.type) {
ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE -> {
if (isSolana(networkId)) {
manageDexSolana(
networkId = networkId,
val amountDecimal = toBigDecimalOrNull(amountToSwap)
if (amountDecimal == null || amountDecimal.signum() == 0) {
return providers.associateWith { createEmptyAmountState() }
}
val amount = SwapAmount(amountDecimal, fromToken.currency.decimals)
val isBalanceWithoutFeeEnough = when (fromAccount) {
is Account.Payment -> true
else -> isBalanceEnough(fromToken, amount, null)
}
val networkId = fromToken.currency.network.backendId
return supervisorScope {
providers.map { provider ->
async {
try {
when (provider.type) {
ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE -> {
if (isSolana(networkId)) {
manageDexSolana(
networkId = networkId,
fromToken = fromToken,
fromAccount = fromAccount,
toToken = toToken,
toAccount = toAccount,
provider = provider,
txFeeSealedState = txFeeSealedState,
amount = amount,
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
expressOperationType = ExpressOperationType.SWAP,
)
} else {
manageDex(
networkId = networkId,
fromToken = fromToken,
fromAccount = fromAccount,
toToken = toToken,
toAccount = toAccount,
provider = provider,
txFeeSealedState = txFeeSealedState,
amount = amount,
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
expressOperationType = ExpressOperationType.SWAP,
)
}
}
ExchangeProviderType.CEX -> {
manageCex(
networkId = networkId,
fromToken = fromToken,
fromAccount = fromAccount,
toToken = toToken,
toAccount = toAccount,
provider = provider,
amount = amount,
reduceBalanceBy = reduceBalanceBy,
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
txFeeSealedState = txFeeSealedState,
)
}
}
} catch (e: Throwable) {
if (e is CancellationException) {
throw e
}
TangemLogger.e("Failed to find quote for provider: ${provider.providerId}", e)
provider to createSwapErrorWith(
fromToken = fromToken,
fromAccount = fromAccount,
toToken = toToken,
toAccount = toAccount,
provider = provider,
txFeeSealedState = txFeeSealedState,
amount = amount,
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
expressOperationType = ExpressOperationType.SWAP,
)
} else {
manageDex(
networkId = networkId,
fromToken = fromToken,
fromAccount = fromAccount,
toToken = toToken,
toAccount = toAccount,
provider = provider,
txFeeSealedState = txFeeSealedState,
amount = amount,
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
expressOperationType = ExpressOperationType.SWAP,
includeFeeInAmount = IncludeFeeInAmount.Excluded,
expressDataError = ExpressDataError.UnknownError,
)
}
}
ExchangeProviderType.CEX -> {
manageCex(
networkId = networkId,
fromToken = fromToken,
fromAccount = fromAccount,
toToken = toToken,
toAccount = toAccount,
provider = provider,
amount = amount,
reduceBalanceBy = reduceBalanceBy,
isBalanceWithoutFeeEnough = isBalanceWithoutFeeEnough,
txFeeSealedState = txFeeSealedState,
)
}
}
}.toMap()
}.awaitAll().toMap()
}
}
@Suppress("LongMethod")