From 266e49734ecbfaeac21a68a0e50ebed889c4f38e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 29 Apr 2026 12:43:31 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../DefaultDeviceSecurityInfoProvider.kt | 15 +++++-- .../data/swap/DefaultSwapRepositoryV2.kt | 2 +- .../tokens/BalanceFetchingOperations.kt | 5 ++- .../v2/impl/amount/model/SwapAmountModel.kt | 1 + .../model/SwapChooseTokenNetworkModel.kt | 8 ++-- .../confirm/model/SwapTransactionSender.kt | 5 ++- .../sendviaswap/model/SendWithSwapModel.kt | 6 +-- .../feature/swap/DefaultSwapRepository.kt | 2 +- .../feature/swap/domain/SwapInteractorImpl.kt | 11 +++-- .../feature/swap/DefaultSwapComponent.kt | 3 +- .../tangem/feature/swap/model/SwapModel.kt | 41 ++++++++++++++----- 11 files changed, 67 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/core/security/DefaultDeviceSecurityInfoProvider.kt b/app/src/main/java/com/tangem/tap/core/security/DefaultDeviceSecurityInfoProvider.kt index e606ddfde2..586122f7b3 100644 --- a/app/src/main/java/com/tangem/tap/core/security/DefaultDeviceSecurityInfoProvider.kt +++ b/app/src/main/java/com/tangem/tap/core/security/DefaultDeviceSecurityInfoProvider.kt @@ -18,8 +18,9 @@ internal class DefaultDeviceSecurityInfoProvider : DeviceSecurityInfoProvider { val isPatched by lazy { hasSecurityPatch() } val isVulnerable = isAffected && !isPatched TangemLogger.i( - "CVE-2026-20435 check: isAffectedMediaTek=$isAffected, " + + messageString = "CVE-2026-20435 check: isAffectedMediaTek=$isAffected, " + "isPatched=$isPatched, isVulnerable=$isVulnerable", + shouldSanitize = false, ) isVulnerable } @@ -27,7 +28,10 @@ internal class DefaultDeviceSecurityInfoProvider : DeviceSecurityInfoProvider { private fun isAffectedMediaTekDevice(): Boolean { val socModel = resolveMediaTekSocModel() val isAffected = socModel != null && socModel in AFFECTED_MEDIATEK_SOCS - TangemLogger.i("CVE-2026-20435 SoC result: model=$socModel, isAffected=$isAffected") + TangemLogger.i( + messageString = "CVE-2026-20435 SoC result: model=$socModel, isAffected=$isAffected", + shouldSanitize = false, + ) return isAffected } @@ -36,7 +40,10 @@ internal class DefaultDeviceSecurityInfoProvider : DeviceSecurityInfoProvider { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val manufacturer = Build.SOC_MANUFACTURER val model = Build.SOC_MODEL - TangemLogger.i("CVE-2026-20435 Layer 1: SOC_MANUFACTURER=$manufacturer, SOC_MODEL=$model") + TangemLogger.i( + messageString = "CVE-2026-20435 Layer 1: SOC_MANUFACTURER=$manufacturer, SOC_MODEL=$model", + shouldSanitize = false, + ) if (manufacturer.equals("MediaTek", ignoreCase = true)) { extractSocModel(model)?.let { return it } } @@ -44,7 +51,7 @@ internal class DefaultDeviceSecurityInfoProvider : DeviceSecurityInfoProvider { // Layer 2: Build.HARDWARE often contains "mtXXXX" on MediaTek devices (public API) val hardware = Build.HARDWARE - TangemLogger.i("CVE-2026-20435 Layer 2: HARDWARE=$hardware") + TangemLogger.i(messageString = "CVE-2026-20435 Layer 2: HARDWARE=$hardware", shouldSanitize = false) extractSocModel(hardware)?.let { return it } return null diff --git a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt index 82196f8a4e..a888483d9c 100644 --- a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt +++ b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt @@ -470,7 +470,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor( ).getOrThrow() }, onError = { error -> - TangemLogger.w("Unable to get pairs", error) + TangemLogger.e("Unable to get pairs", error) throw error }, ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/BalanceFetchingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/BalanceFetchingOperations.kt index 9538efb817..2071908140 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/BalanceFetchingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/BalanceFetchingOperations.kt @@ -116,7 +116,10 @@ class BalanceFetchingOperations( val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = currency) if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { - TangemLogger.e("Unable to get staking ID for user wallet $userWalletId and currency ${currency.id}") + TangemLogger.e( + messageString = "Unable to get stakingID for user wallet $userWalletId and currency ${currency.id}", + shouldSanitize = false, + ) } stakingId.getOrNull() diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index eb17fb5103..59de970d62 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -620,6 +620,7 @@ internal class SwapAmountModel @Inject constructor( | Primary -> $primaryStatus | Secondary -> $secondaryStatus """.trimIndent(), + shouldSanitize = false, ) showErrorAlert(errorMessage = null) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/model/SwapChooseTokenNetworkModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/model/SwapChooseTokenNetworkModel.kt index b40d57b863..1567523f5c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/model/SwapChooseTokenNetworkModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/model/SwapChooseTokenNetworkModel.kt @@ -23,12 +23,12 @@ import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.transfor import com.tangem.features.swap.v2.impl.common.SwapUtils.SEND_WITH_SWAP_PROVIDER_TYPES import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.logging.TangemLogger import com.tangem.utils.transformer.update import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import com.tangem.utils.logging.TangemLogger import javax.inject.Inject @Suppress("LongParameterList") @@ -76,8 +76,8 @@ internal class SwapChooseTokenNetworkModel @Inject constructor( val cryptoCurrencyList = createCryptoCurrencyUseCase( token = params.token, userWalletId = params.userWalletId, - ).getOrElse { - TangemLogger.e("Failed to get crypto currency") + ).getOrElse { throwable -> + TangemLogger.e("Failed to get crypto currency", throwable) swapChooseTokenAlertFactory.getGenericErrorState(params.onDismiss) return@launch } @@ -88,7 +88,7 @@ internal class SwapChooseTokenNetworkModel @Inject constructor( filterProviderTypes = SEND_WITH_SWAP_PROVIDER_TYPES, swapTxType = SwapTxType.SendWithSwap, ).getOrElse { error -> - TangemLogger.e(error.toString()) + TangemLogger.e("Error", error) uiState.update( SwapChooseErrorStateTransformer( tokenName = params.token.name, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt index e35f01fcf3..bfe0696e43 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt @@ -67,7 +67,10 @@ internal class SwapTransactionSender @AssistedInject constructor( ExpressProviderType.DEX_BRIDGE, ExpressProviderType.ONRAMP, -> { - TangemLogger.w("Provider $providerType is not supported in Send With Swap") + TangemLogger.i( + messageString = "Provider $providerType is not supported in Send With Swap", + shouldSanitize = false, + ) onExpressError(ExpressError.UnknownError) } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt index 0ec7b02ecd..e991accc0c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt @@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router 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 @@ -17,7 +18,6 @@ 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.swap.models.SwapDirection -import com.tangem.domain.account.status.usecase.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.entity.FeeSelectorUM @@ -32,9 +32,9 @@ import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute import com.tangem.features.swap.v2.impl.sendviaswap.confirm.SendWithSwapConfirmComponent import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider +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 @@ -159,7 +159,7 @@ internal class SendWithSwapModel @Inject constructor( getPrimaryCurrencyStatusUpdates(params.currency) }, ifLeft = { error -> - TangemLogger.w(error.toString()) + TangemLogger.e(error.toString()) swapAlertFactory.getGenericErrorState( expressError = ExpressError.UnknownError, onFailedTxEmailClick = { diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index 21cc38dfd0..f2c9cfd2af 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -249,7 +249,7 @@ internal class DefaultSwapRepository( ) }, catch = { exception -> - TangemLogger.e("getExchangeStatus error: $exception") + TangemLogger.e("getExchangeStatus error", exception) raise(UnknownError(exception.message)) }, ) 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 6079eb8646..35e943bda2 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 @@ -208,6 +208,7 @@ internal class SwapInteractorImpl @Inject constructor( |- amountToSwap: $amountToSwap |- selectedFee: $txFeeSealedState """.trimIndent(), + shouldSanitize = false, ) val amountDecimal = toBigDecimalOrNull(amountToSwap) @@ -562,6 +563,7 @@ internal class SwapInteractorImpl @Inject constructor( |- includeFeeInAmount: $includeFeeInAmount |- fee: $fee """.trimIndent(), + shouldSanitize = false, ) val userWallet = fromSwapCurrencyStatus.userWallet @@ -1364,9 +1366,7 @@ internal class SwapInteractorImpl @Inject constructor( reduceBalanceBy: BigDecimal, feeValue: BigDecimal, ): IncludeFeeInAmount { - val feePaidCurrency = getFeePaidCurrency(fromSwapCurrencyStatus) - - return when (feePaidCurrency) { + return when (val feePaidCurrency = getFeePaidCurrency(fromSwapCurrencyStatus)) { is FeePaidCurrency.Token -> { if (feePaidCurrency.balance > feeValue) { IncludeFeeInAmount.Excluded @@ -1433,8 +1433,7 @@ internal class SwapInteractorImpl @Inject constructor( vararg fees: BigDecimal, ): List { val appCurrency = getSelectedAppCurrencyUseCase.unwrap() - val feePaidCurrency = getFeePaidCurrency(fromSwapCurrencyStatus) - val feeCurrencyId: CryptoCurrency.ID = when (feePaidCurrency) { + val feeCurrencyId: CryptoCurrency.ID = when (val feePaidCurrency = getFeePaidCurrency(fromSwapCurrencyStatus)) { is FeePaidCurrency.Token -> feePaidCurrency.tokenId else -> getNativeToken(fromSwapCurrencyStatus).id } @@ -2195,7 +2194,7 @@ internal class SwapInteractorImpl @Inject constructor( return try { SolanaTransactionHelper.removeSignaturesPlaceholders(hash) } catch (e: Exception) { - TangemLogger.e("Failed to format the hash: ${e.message.orEmpty()}") + TangemLogger.e("Failed to format the hash: ${e.message.orEmpty()}", e) hash } } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt index 417c0e44eb..bf5725be3b 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt @@ -157,9 +157,10 @@ internal class DefaultSwapComponent @AssistedInject constructor( LaunchedEffect(fromCryptoCurrency, feePaidCryptoCurrency, shouldHideBlock) { if (shouldHideBlock) { TangemLogger.e( - "Dismissing fee selector: " + + messageString = "Dismissing fee selector: " + "shouldHideBlock = $shouldHideBlock, amount = ${dataState.amount}, " + "isInsufficientFunds = ${model.uiState.isInsufficientFunds}", + shouldSanitize = false, ) slotNavigation.dismiss() return@LaunchedEffect diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index 89c11aa06a..a1ea402b4b 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -10,6 +10,7 @@ import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.dismiss import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsErrorHandler @@ -69,9 +70,6 @@ import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNet import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.feature.swap.analytics.SwapEvents import com.tangem.feature.swap.analytics.SwapQuotePerformanceTracker -import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPayload -import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge -import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult import com.tangem.feature.swap.component.SwapFeeSelectorBlockComponent import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter import com.tangem.feature.swap.domain.AllowPermissionsHandler @@ -94,9 +92,11 @@ import com.tangem.feature.swap.ui.StateBuilder import com.tangem.feature.swap.utils.formatToUIRepresentation import com.tangem.feature.swap.utils.getContractAddress import com.tangem.features.approval.api.GiveApprovalComponent +import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPayload +import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge +import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger -import com.tangem.common.TangemBlogUrlBuilder import com.tangem.features.swap.SwapComponent import com.tangem.utils.Provider import com.tangem.utils.coroutines.* @@ -650,10 +650,16 @@ internal class SwapModel @Inject constructor( userWalletId = fromSwapCurrencyStatus.userWalletId, cryptoCurrencyStatus = fromSwapCurrencyStatus.status, ).onLeft { - TangemLogger.e("Unable to get fee paid crypto currency status for ${fromCryptoCurrency.id}") + TangemLogger.e( + messageString = "Unable to get fee paid crypto currency status for ${fromCryptoCurrency.id}", + shouldSanitize = false, + ) }.onRight { currencyStatus -> if (currencyStatus == null) { - TangemLogger.e("Fee paid crypto currency status is null for ${fromCryptoCurrency.id}") + TangemLogger.e( + messageString = "Fee paid crypto currency status is null for ${fromCryptoCurrency.id}", + shouldSanitize = false, + ) } }.getOrNull() } @@ -722,7 +728,7 @@ internal class SwapModel @Inject constructor( } }, onError = { error -> - TangemLogger.e("Error when loading quotes: $error") + TangemLogger.e("Error when loading quotes", error) performanceTracker.onLoadingFinished(hasError = true) feeSelectorRepository.state.value = FeeSelectorUM.Error(GetFeeError.UnknownError, isHidden = true) uiState = stateBuilder.addNotification(uiState, null) { startLoadingQuotesFromLastState() } @@ -966,6 +972,7 @@ internal class SwapModel @Inject constructor( }.onSuccess { swapTransactionState -> when (swapTransactionState) { is SwapTransactionState.TxSent -> { + TangemLogger.i("onSwapClick: onSuccess: txHash: $swapTransactionState", shouldSanitize = false) if (fee == null) { TangemLogger.e("onSwapClick: onSuccess: fee is null after swap") showAlert(resourceReference(R.string.swapping_fee_estimation_error_text)) @@ -1015,6 +1022,10 @@ internal class SwapModel @Inject constructor( showDemoModeAlert() } is SwapTransactionState.Error -> { + TangemLogger.e( + messageString = "onSwapClick: swap transaction error: $swapTransactionState", + shouldSanitize = false, + ) startLoadingQuotesFromLastState() showTransactionErrorAlert(swapTransactionState) } @@ -1714,7 +1725,11 @@ internal class SwapModel @Inject constructor( val feeStateUM = feeSelectorRepository.state.value as? FeeSelectorUM.Content if (feeStateUM == null) { - TangemLogger.e("getSelectedFeeState: FeeSelectorUM is not Content: $feeStateUM, returning Legacy state") + TangemLogger.e( + messageString = "getSelectedFeeState: FeeSelectorUM is not Content: $feeStateUM, " + + "returning Legacy state", + shouldSanitize = false, + ) return TxFeeSealedState.Legacy( txFeeState = TxFeeState.Empty, selectedFee = dataState.selectedFee?.feeType ?: FeeType.NORMAL, @@ -1736,7 +1751,10 @@ internal class SwapModel @Inject constructor( val feeStateUM = feeSelectorRepository.state.value as? FeeSelectorUM.Content if (feeStateUM == null) { - TangemLogger.e("getSelectedFee: FeeSelectorUM is not Content: $feeStateUM, returning null") + TangemLogger.e( + messageString = "getSelectedFee: FeeSelectorUM is not Content: $feeStateUM, returning null", + shouldSanitize = false, + ) return null } @@ -1842,7 +1860,10 @@ internal class SwapModel @Inject constructor( val selectedProvider = dataStateStateFlow.first { it.selectedProvider != null }.selectedProvider!! if (dataState.lastLoadedSwapStates[selectedProvider] !is SwapState.QuotesLoadedState) { - TangemLogger.e("loadFee: Quotes not loaded ${dataState.lastLoadedSwapStates[selectedProvider]}") + TangemLogger.e( + messageString = "loadFee: Quotes not loaded ${dataState.lastLoadedSwapStates[selectedProvider]}", + shouldSanitize = false, + ) return Either.Left(GetFeeError.UnknownError) }