Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-28 14:11:23 +03:00
commit 0470e1f010
1144 changed files with 48463 additions and 11166 deletions

View file

@ -133,7 +133,6 @@ private fun Preview() {
value = "bitcoin",
derivationPath = Network.DerivationPath.None,
),
backendId = "bitcoin",
name = "Bitcoin",
currencySymbol = "BTC",
derivationPath = Network.DerivationPath.None,

View file

@ -516,7 +516,6 @@ private val cryptoCurrencyStatus
value = "bitcoin",
derivationPath = Network.DerivationPath.None,
),
backendId = "bitcoin",
name = "Bitcoin",
currencySymbol = "BTC",
derivationPath = Network.DerivationPath.None,

View file

@ -171,7 +171,6 @@ private fun Preview() {
value = "bitcoin",
derivationPath = Network.DerivationPath.None,
),
backendId = "bitcoin",
name = "Bitcoin",
currencySymbol = "BTC",
derivationPath = Network.DerivationPath.None,

View file

@ -258,7 +258,6 @@ private class FeeSelectorUMProvider : PreviewParameterProvider<FeeSelectorUM> {
value = "bitcoin",
derivationPath = Network.DerivationPath.None,
),
backendId = "bitcoin",
name = "Bitcoin",
currencySymbol = "BTC",
derivationPath = Network.DerivationPath.None,

View file

@ -11,7 +11,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.common.ui.account.AccountIconItemStateConverter
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.currency.icon.CurrencyIconStateBuilder
import com.tangem.common.ui.components.currency.icon.CurrencyIconStateBuilder
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.resourceReference

View file

@ -21,7 +21,6 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
@ -74,10 +73,10 @@ import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero
import com.tangem.utils.extensions.stripZeroPlainString
import com.tangem.utils.logging.TangemLogger
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
import javax.inject.Inject
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
@ -114,7 +113,6 @@ internal class SendConfirmModel @Inject constructor(
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val currenciesRepository: CurrenciesRepository,
private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
@ -281,8 +279,7 @@ internal class SendConfirmModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage,
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
networkId = cryptoCurrency.network.id,
destinationAddress = confirmData.enteredDestination.orEmpty(),
tokenSymbol = if (amount?.type is AmountType.Token) {
amount.currencySymbol
@ -587,8 +584,7 @@ internal class SendConfirmModel @Inject constructor(
val confirmUM = uiState.value.confirmUM
val isContent = confirmUM is ConfirmUM.Content
val isReadyToSend = isContent && !confirmUM.isSending
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
userWallet.isHotWallet && isContent
val isHoldToConfirm = userWallet.isHotWallet && isContent
return NavigationButton(
textReference = getPrimaryButtonText(confirmUM, isHoldToConfirm),
iconRes = walletInterationIcon(userWallet),

View file

@ -18,6 +18,7 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.ui.utils.parseBigDecimalOrNull
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.usecase.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
@ -33,7 +34,6 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
import com.tangem.domain.account.status.usecase.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.models.TransactionFeeExtended
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
@ -65,9 +65,9 @@ import com.tangem.features.send.v2.subcomponents.destination.model.transformers.
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
import kotlin.properties.Delegates
@ -528,8 +528,7 @@ internal class SendModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage.orEmpty(),
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
networkId = cryptoCurrency.network.id,
destinationAddress = "",
tokenSymbol = "",
amount = "",

View file

@ -16,17 +16,16 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
import com.tangem.domain.settings.NeverShowTapHelpUseCase
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
@ -57,10 +56,10 @@ import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendCon
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.stripZeroPlainString
import com.tangem.utils.logging.TangemLogger
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
import javax.inject.Inject
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
@ -91,7 +90,6 @@ internal class NFTSendConfirmModel @Inject constructor(
private val nftSendAnalyticHelper: NFTSendAnalyticHelper,
private val nftSendSuccessTrigger: NFTSendSuccessTrigger,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), NFTSendConfirmClickIntents, SendNotificationsComponent.ModelCallback, FeeSelectorModelCallback {
@ -197,8 +195,7 @@ internal class NFTSendConfirmModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage,
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
networkId = cryptoCurrency.network.id,
destinationAddress = confirmData.enteredDestination.orEmpty(),
tokenSymbol = null,
amount = params.nftAsset.amount?.toString().orEmpty(),
@ -379,7 +376,7 @@ internal class NFTSendConfirmModel @Inject constructor(
flow = uiState,
flow2 = params.currentRoute,
transform = { state, route -> state to route },
).onEach { (state, route) ->
).onEach { (state, _) ->
val confirmUM = state.confirmUM
params.callback.onResult(
state.copy(
@ -425,8 +422,7 @@ internal class NFTSendConfirmModel @Inject constructor(
val confirmUM = uiState.value.confirmUM
val isContent = confirmUM is ConfirmUM.Content
val isReadyToSend = isContent && !confirmUM.isSending
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
userWallet.isHotWallet && isContent
val isHoldToConfirm = userWallet.isHotWallet && isContent
return NavigationButton(
textReference = getPrimaryButtonText(confirmUM, isHoldToConfirm),
iconRes = walletInterationIcon(userWallet),

View file

@ -15,6 +15,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.usecase.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
@ -27,7 +28,6 @@ import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.account.status.usecase.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.transaction.error.GetFeeError
@ -223,8 +223,7 @@ internal class NFTSendModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage.orEmpty(),
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
networkId = cryptoCurrency.network.id,
destinationAddress = "",
tokenSymbol = null,
amount = "",

View file

@ -15,18 +15,16 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.WrappedList
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.v2.api.entity.PredefinedValues
@ -44,7 +42,6 @@ import com.tangem.utils.extensions.orZero
import com.tangem.utils.isNullOrZero
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.properties.Delegates
@ -61,7 +58,6 @@ internal class SendAmountModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val rampStateManager: RampStateManager,
private val sendAmountAlertFactory: SendAmountAlertFactory,
private val getWalletsUseCase: GetWalletsUseCase,
) : Model(), SendAmountClickIntents {
@ -73,7 +69,6 @@ internal class SendAmountModel @Inject constructor(
private val _uiState = MutableStateFlow(params.state)
val uiState = _uiState.asStateFlow()
private var isAvailableForSwap: Boolean = false
val isSendWithSwapAvailable: StateFlow<Boolean>
field = MutableStateFlow(false)
@ -87,12 +82,6 @@ internal class SendAmountModel @Inject constructor(
private var maxAmountBoundary: EnterAmountBoundary by Delegates.notNull()
init {
modelScope.launch {
isAvailableForSwap = rampStateManager.availableForSwap(
userWalletId = params.userWalletId,
cryptoCurrency = params.cryptoCurrency,
) == ScenarioUnavailabilityReason.None
}
configAmountNavigation()
initAppCurrency()
subscribeOnCryptoCurrencyStatusFlow()
@ -409,7 +398,7 @@ internal class SendAmountModel @Inject constructor(
val isMultiCurrency = userWallet?.isMultiCurrency == true
isSendWithSwapAvailable.update {
isAvailableForSwap && isMultiCurrency && !params.predefinedValues.isFromMainScreenQr
!params.cryptoCurrency.isCustom && isMultiCurrency && !params.predefinedValues.isFromMainScreenQr
}
}
}

View file

@ -19,7 +19,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.CryptoCurrencyAddress
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
@ -69,7 +68,6 @@ internal class SendDestinationModel @Inject constructor(
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
private val parseQrCodeUseCase: ParseQrCodeUseCase,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
private val analyticsEventHandler: AnalyticsEventHandler,
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
) : Model(), SendDestinationClickIntents {
@ -262,16 +260,15 @@ internal class SendDestinationModel @Inject constructor(
private fun AccountStatus.Payment.getDestinationWalletUM(wallet: UserWallet): DestinationWalletUM? {
val contractAddress = (cryptoCurrency as? CryptoCurrency.Token)?.contractAddress ?: return null
val address = when (val status = this.value) {
is PaymentAccountStatusValue.Loaded -> status.cryptoBalance.depositAddress
is PaymentAccountStatusValue.Locked -> status.cryptoBalance.depositAddress
val (paymentAccountAddress, currency) = when (val status = this.value) {
is PaymentAccountStatusValue.Loaded -> status.cryptoBalance.depositAddress to status.cryptoCurrency
else -> return null
}
val currency = tangemPayCryptoCurrencyFactory.create(wallet).getOrNull() ?: return null
return if (contractAddress.equals(currency.contractAddress, true)) {
DestinationWalletUM(
name = wallet.name,
address = address,
address = paymentAccountAddress,
cryptoCurrency = currency,
userWalletId = wallet.walletId,
account = account,

View file

@ -253,7 +253,7 @@ internal class NotificationsModel @Inject constructor(
addExceedsBalanceNotification(
cryptoCurrencyWarning = currencyWarning,
cryptoCurrencyStatus = cryptoCurrencyStatus,
shouldMergeFeeNetworkName = BlockchainUtils.isArbitrum(currency.network.backendId),
shouldMergeFeeNetworkName = BlockchainUtils.isArbitrum(currency.network.rawId),
onClick = ::showTokenDetails,
onAnalyticsEvent = {
val event = NotificationsAnalyticEvents.NoticeNotEnoughFee(

View file

@ -30,7 +30,7 @@ class NFTSendConfirmationNotificationsTransformerV2Test {
private val appCurrency = AppCurrency(name = "US Dollar", code = "USD", symbol = "$")
private val analyticsCategoryName = "test_category"
val cryptoCurrencyStatus = CryptoCurrencyStatus(
private val cryptoCurrencyStatus = CryptoCurrencyStatus(
currency = CryptoCurrency.Coin(
id = CryptoCurrency.ID.fromValue("coin⟨BITCOIN⟩bitcoin"),
network = Network(
@ -38,7 +38,6 @@ class NFTSendConfirmationNotificationsTransformerV2Test {
value = "bitcoin",
derivationPath = Network.DerivationPath.None,
),
backendId = "bitcoin",
name = "Bitcoin",
currencySymbol = "BTC",
derivationPath = Network.DerivationPath.None,

View file

@ -32,7 +32,7 @@ class SendConfirmationNotificationsTransformerV2Test {
private val appCurrency = AppCurrency(name = "US Dollar", code = "USD", symbol = "$")
private val analyticsCategoryName = "test_category"
val cryptoCurrencyStatus = CryptoCurrencyStatus(
private val cryptoCurrencyStatus = CryptoCurrencyStatus(
currency = CryptoCurrency.Coin(
id = CryptoCurrency.ID.fromValue("coin⟨BITCOIN⟩bitcoin"),
network = Network(
@ -40,7 +40,6 @@ class SendConfirmationNotificationsTransformerV2Test {
value = "bitcoin",
derivationPath = Network.DerivationPath.None,
),
backendId = "bitcoin",
name = "Bitcoin",
currencySymbol = "BTC",
derivationPath = Network.DerivationPath.None,