Updated on 2026-08-14
This commit is contained in:
parent
d97b7b9bf7
commit
266e49734e
11 changed files with 67 additions and 32 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -620,6 +620,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
| Primary -> $primaryStatus
|
||||
| Secondary -> $secondaryStatus
|
||||
""".trimIndent(),
|
||||
shouldSanitize = false,
|
||||
)
|
||||
showErrorAlert(errorMessage = null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ internal class DefaultSwapRepository(
|
|||
)
|
||||
},
|
||||
catch = { exception ->
|
||||
TangemLogger.e("getExchangeStatus error: $exception")
|
||||
TangemLogger.e("getExchangeStatus error", exception)
|
||||
raise(UnknownError(exception.message))
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue