Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-29 20:53:48 +04:00
parent bccf17effe
commit df9ecedf67
14 changed files with 135 additions and 40 deletions

View file

@ -1440,6 +1440,8 @@
<string name="wc_transaction_request">取引リクエスト</string>
<string name="wc_transaction_request_title">取引リクエスト</string>
<string name="wc_unlimited_amount">無制限</string>
<string name="wc_uri_already_used_description">各ペアリング試行で、新しくユニークなURIが使用されていることを確認します</string>
<string name="wc_uri_already_used_title">URIはすでに使用されています</string>
<string name="wc_wallet_connect">ウォレットコネクト</string>
<string name="welcome_interrupted_backup_alert_discard">破棄</string>
<string name="welcome_interrupted_backup_alert_message">バックアップが中断されました。再開しますか?</string>

View file

@ -1184,6 +1184,7 @@
<string name="wallet_promo_banner_description">Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменивайте и стейкайте в один клик. Свяжите до трех карт для резервного копирования. </string>
<string name="wallet_promo_banner_title">Откройте Tangem Wallet</string>
<string name="wallet_settings_push_notifications_description">Получайте уведомления о входящих транзакциях в кошельке и обновлениях Tangem.</string>
<string name="wallet_settings_push_notifications_huawei_warning">В настоящий момент push-уведомления могут не работать на устройствах Huawei. Мы уже работаем над решением этой проблемы и планируем исправление в ближайших обновлениях. Спасибо за понимание!</string>
<string name="wallet_settings_push_notifications_title">Уведомления о транзакциях</string>
<string name="wallet_settings_title">Настройки кошелька</string>
<string name="wallet_title">Tangem</string>

View file

@ -1351,6 +1351,7 @@
<string name="wallet_settings_access_code_description">Secret code to protect this wallet. Used for login and signatures.</string>
<string name="wallet_settings_access_code_title">Set/Change Access Code</string>
<string name="wallet_settings_push_notifications_description">Stay notified on wallet incoming transactions and Tangem updates.</string>
<string name="wallet_settings_push_notifications_huawei_warning">Push notifications may currently not work on Huawei devices. We\'re actively working on a solution and will release a fix in an upcoming update. Thank you for your understanding!</string>
<string name="wallet_settings_push_notifications_title">Transaction Notifications</string>
<string name="wallet_settings_title">Wallet settings</string>
<string name="wallet_title">Tangem</string>
@ -1510,6 +1511,7 @@
<string name="wc_uri_already_used_description">Ensure that each pairing attempt uses a fresh and unique URI</string>
<string name="wc_uri_already_used_title">URI already used</string>
<string name="wc_wallet_connect">Wallet connect</string>
<string name="wc_warning_transaction">Suspicious transaction</string>
<string name="welcome_interrupted_backup_alert_discard">Discard</string>
<string name="welcome_interrupted_backup_alert_message">You have an interrupted backup. Do you want to resume?</string>
<string name="welcome_interrupted_backup_alert_resume">Yes, resume</string>

View file

@ -211,11 +211,14 @@ internal fun TextsBlock(
SpacerH(height = TangemTheme.dimens.spacing2)
}
Text(
text = subtitle.resolveReference(),
color = subtitleColor,
style = TangemTheme.typography.caption2,
)
val subtitleText = subtitle.resolveReference()
if (subtitleText.isNotEmpty()) {
Text(
text = subtitleText,
color = subtitleColor,
style = TangemTheme.typography.caption2,
)
}
}
}

View file

@ -16,6 +16,7 @@ import org.json.JSONArray
private const val SUCCESS_STATUS = "Success"
private const val DOMAIN_CHECKED_STATUS = "hit"
private const val VALIDATION_SAFE_STATUS = "Benign"
private const val VALIDATION_WARNING_STATUS = "Warning"
internal object BlockAidMapper {
@ -32,6 +33,7 @@ internal object BlockAidMapper {
validation = when {
from.validation.status != SUCCESS_STATUS -> ValidationResult.FAILED_TO_VALIDATE
from.validation.resultType == VALIDATION_SAFE_STATUS -> ValidationResult.SAFE
from.validation.resultType == VALIDATION_WARNING_STATUS -> ValidationResult.WARNING
else -> ValidationResult.UNSAFE
},
simulation = if (from.simulation.status != SUCCESS_STATUS) {

View file

@ -15,6 +15,11 @@ enum class ValidationResult {
*/
UNSAFE,
/**
* Transaction was confirmed suspicious
*/
WARNING,
/**
* Validation wasn't performed, BlockAid cannot guarantee transaction's safety
*/

View file

@ -17,8 +17,8 @@ internal object WcAlertsFactory {
createUnknownDomainAlert()
is WcTransactionRoutes.Alert.Type.UnsafeDomain ->
createUnsafeDomainAlert()
is WcTransactionRoutes.Alert.Type.MaliciousInfo ->
createMaliciousDAppAlert(alertType.description, alertType.onClick)
is WcTransactionRoutes.Alert.Type.BlockAidErrorInfo ->
createMaliciousDAppAlert(alertType.description, alertType.onClick, alertType.iconType, alertType.iconBgType)
is WcTransactionRoutes.Alert.Type.UnknownError ->
createUnknownErrorAlert(alertType.errorMessage, alertType.onDismiss)
}
@ -154,11 +154,14 @@ internal object WcAlertsFactory {
private fun createMaliciousDAppAlert(
description: String?,
activeButtonOnClick: (() -> Unit),
iconType: Type,
iconBgType: MessageBottomSheetUMV2.Icon.BackgroundType,
): MessageBottomSheetUMV2 {
return messageBottomSheetUM {
infoBlock {
icon(R.drawable.img_knight_shield_32) {
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Attention
type = iconType
backgroundType = iconBgType
}
title = resourceReference(R.string.security_alert_title)
if (!description.isNullOrEmpty()) {

View file

@ -6,6 +6,16 @@ import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllow
internal data class WcSendReceiveTransactionCheckResultsUM(
val estimatedWalletChanges: WcEstimatedWalletChangesUM? = null,
val spendAllowance: WcSpendAllowanceUM? = null,
val notificationText: TextReference? = null,
val notification: BlockAidNotificationUM? = null,
val isLoading: Boolean = true,
)
)
internal data class BlockAidNotificationUM(
val type: Type,
val title: TextReference,
val text: TextReference? = null,
) {
internal enum class Type {
ERROR, WARNING
}
}

View file

@ -15,6 +15,8 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2.Icon.Type
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.models.network.Network
@ -251,10 +253,10 @@ internal class WcSendTransactionModel @Inject constructor(
}
private fun onSign(securityCheck: BlockAidTransactionCheck.Result?) {
if (securityCheck?.result?.validation == ValidationResult.UNSAFE) {
showMaliciousAlert(securityCheck.result.description)
} else {
sign()
when (securityCheck?.result?.validation) {
ValidationResult.UNSAFE -> showMaliciousAlert(securityCheck.result.description)
ValidationResult.WARNING -> showWarningAlert(securityCheck.result.description)
else -> sign()
}
securityCheck?.result?.validation?.let { securityStatus ->
val event = WcAnalyticEvents.NoticeSecurityAlert(
@ -265,6 +267,7 @@ internal class WcSendTransactionModel @Inject constructor(
when (securityStatus) {
ValidationResult.SAFE -> Unit
ValidationResult.UNSAFE,
ValidationResult.WARNING,
ValidationResult.FAILED_TO_VALIDATE,
-> analytics.send(event)
}
@ -288,7 +291,22 @@ internal class WcSendTransactionModel @Inject constructor(
}
private fun showMaliciousAlert(description: String?) {
val type = WcTransactionRoutes.Alert.Type.MaliciousInfo(description = description, onClick = ::signFromAlert)
val type = WcTransactionRoutes.Alert.Type.BlockAidErrorInfo(
description = description,
onClick = ::signFromAlert,
iconType = Type.Warning,
iconBgType = MessageBottomSheetUMV2.Icon.BackgroundType.Warning,
)
stackNavigation.pushNew(WcTransactionRoutes.Alert(type))
}
private fun showWarningAlert(description: String?) {
val type = WcTransactionRoutes.Alert.Type.BlockAidErrorInfo(
description = description,
onClick = ::signFromAlert,
iconType = Type.Attention,
iconBgType = MessageBottomSheetUMV2.Icon.BackgroundType.Attention,
)
stackNavigation.pushNew(WcTransactionRoutes.Alert(type))
}

View file

@ -3,6 +3,7 @@ package com.tangem.features.walletconnect.transaction.routes
import androidx.compose.runtime.Immutable
import com.tangem.core.decompose.navigation.Route
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
import kotlinx.serialization.Serializable
@Serializable
@ -27,7 +28,12 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout
data class Verified(val appName: String) : Type()
data object UnknownDomain : Type()
data object UnsafeDomain : Type()
data class MaliciousInfo(val description: String?, val onClick: () -> Unit) : Type()
data class BlockAidErrorInfo(
val description: String?,
val onClick: () -> Unit,
val iconType: MessageBottomSheetUMV2.Icon.Type,
val iconBgType: MessageBottomSheetUMV2.Icon.BackgroundType,
) : Type()
data class UnknownError(val errorMessage: String?, val onDismiss: () -> Unit) : Type()
}
}

View file

@ -11,11 +11,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.blockaid.BlockAidNotificationUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangeUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangesUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
@ -36,8 +36,8 @@ internal fun TransactionCheckResultsItem(
if (item.isLoading) {
WcEstimatedWalletChangesLoadingItem()
} else {
if (item.notificationText != null) {
WcTransactionCheckErrorItem(item.notificationText.resolveReference())
if (item.notification != null) {
WcTransactionCheckErrorItem(item.notification)
}
if (item.estimatedWalletChanges != null) {
WcEstimatedWalletChangesItem(item.estimatedWalletChanges)
@ -70,7 +70,11 @@ private class TransactionCheckResultsItemProvider : PreviewParameterProvider<WcS
override val values = sequenceOf(
WcSendReceiveTransactionCheckResultsUM(
isLoading = false,
notificationText = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
notification = BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.ERROR,
title = TextReference.Res(R.string.wc_malicious_transaction),
text = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
),
estimatedWalletChanges = WcEstimatedWalletChangesUM(
items = persistentListOf(
WcEstimatedWalletChangeUM(

View file

@ -10,6 +10,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.walletconnect.model.WcApprovedAmount
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.blockaid.BlockAidNotificationUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangeUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangesUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
@ -22,18 +23,27 @@ import javax.inject.Inject
internal const val DECIMALS_AMOUNT = 2
@Suppress("CyclomaticComplexMethod")
@Suppress("CyclomaticComplexMethod", "LongMethod")
internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor(
private val estimatedWalletChangeUMConverter: WcEstimatedWalletChangeUMConverter,
private val spendAllowanceUMConverter: WcSpendAllowanceUMConverter,
) : Converter<WcSendAndReceiveBlockAidUiConverter.Input, WcSendReceiveTransactionCheckResultsUM> {
override fun convert(value: Input): WcSendReceiveTransactionCheckResultsUM {
val description = value.result.description
val description = value.result.description?.let { if (it.isNotEmpty()) TextReference.Str(it) else null }
return WcSendReceiveTransactionCheckResultsUM(
isLoading = false,
notificationText = when (value.result.validation) {
notification = when (value.result.validation) {
ValidationResult.SAFE, ValidationResult.FAILED_TO_VALIDATE -> null
ValidationResult.UNSAFE -> if (!description.isNullOrEmpty()) TextReference.Str(description) else null
ValidationResult.UNSAFE -> BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.ERROR,
title = TextReference.Res(R.string.wc_malicious_transaction),
text = description,
)
ValidationResult.WARNING -> BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.WARNING,
title = TextReference.Res(R.string.wc_warning_transaction),
text = description,
)
},
estimatedWalletChanges = (value.result.simulation as? SimulationResult.Success)?.data?.let { data ->
when (data) {

View file

@ -11,26 +11,42 @@ import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.blockaid.BlockAidNotificationUM
@Composable
internal fun WcTransactionCheckErrorItem(notificationText: String, modifier: Modifier = Modifier) {
internal fun WcTransactionCheckErrorItem(notification: BlockAidNotificationUM, modifier: Modifier = Modifier) {
Notification(
modifier = modifier
.fillMaxWidth(),
config = NotificationConfig(
title = resourceReference(R.string.wc_malicious_transaction),
subtitle = TextReference.Str(notificationText),
iconResId = R.drawable.ic_alert_circle_24,
title = notification.title,
subtitle = TextReference.Str(notification.text?.resolveReference() ?: ""),
iconResId = when (notification.type) {
BlockAidNotificationUM.Type.ERROR -> R.drawable.ic_alert_circle_24
BlockAidNotificationUM.Type.WARNING -> R.drawable.ic_alert_triangle_20
},
),
containerColor = TangemColorPalette.Amaranth.copy(alpha = 0.1f),
titleColor = TangemTheme.colors.text.warning,
subtitleColor = TangemTheme.colors.text.primary1,
iconTint = TangemTheme.colors.icon.warning,
containerColor = when (notification.type) {
BlockAidNotificationUM.Type.ERROR -> TangemColorPalette.Amaranth.copy(alpha = 0.1f)
BlockAidNotificationUM.Type.WARNING -> TangemColorPalette.Dark1.copy(alpha = 0.1f)
},
titleColor = when (notification.type) {
BlockAidNotificationUM.Type.ERROR -> TangemTheme.colors.text.warning
BlockAidNotificationUM.Type.WARNING -> TangemTheme.colors.text.primary1
},
subtitleColor = when (notification.type) {
BlockAidNotificationUM.Type.ERROR -> TangemTheme.colors.text.primary1
BlockAidNotificationUM.Type.WARNING -> TangemTheme.colors.text.tertiary
},
iconTint = when (notification.type) {
BlockAidNotificationUM.Type.ERROR -> TangemTheme.colors.icon.warning
BlockAidNotificationUM.Type.WARNING -> TangemTheme.colors.icon.attention
},
)
}
@ -43,7 +59,13 @@ private fun WcTransactionCheckErrorItemPreview() {
modifier = Modifier
.background(TangemTheme.colors.background.tertiary),
) {
WcTransactionCheckErrorItem("The transaction approves erc20 tokens to a known malicious address")
WcTransactionCheckErrorItem(
BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.ERROR,
title = TextReference.Res(R.string.wc_malicious_transaction),
text = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
),
)
}
}
}

View file

@ -29,6 +29,7 @@ import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState
import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.components.PreviewFeeSelectorBlockComponent
import com.tangem.features.walletconnect.transaction.entity.blockaid.BlockAidNotificationUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangeUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangesUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
@ -173,8 +174,10 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide
appSubtitle = "react-app.walletconnect.com",
),
estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(
notificationText = TextReference.Str(
"The transaction approves erc20 tokens to a known malicious address",
notification = BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.ERROR,
title = TextReference.Res(R.string.wc_malicious_transaction),
text = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
),
estimatedWalletChanges = WcEstimatedWalletChangesUM(
items = persistentListOf(
@ -210,8 +213,10 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide
appSubtitle = "react-app.walletconnect.com",
),
estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(
notificationText = TextReference.Str(
"The transaction approves erc20 tokens to a known malicious address",
notification = BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.ERROR,
title = TextReference.Res(R.string.wc_malicious_transaction),
text = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
),
estimatedWalletChanges = WcEstimatedWalletChangesUM(
items = persistentListOf(
@ -246,8 +251,10 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide
appSubtitle = "react-app.walletconnect.com",
),
estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(
notificationText = TextReference.Str(
"The transaction approves erc20 tokens to a known malicious address",
notification = BlockAidNotificationUM(
type = BlockAidNotificationUM.Type.ERROR,
title = TextReference.Res(R.string.wc_malicious_transaction),
text = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
),
estimatedWalletChanges = WcEstimatedWalletChangesUM(
items = persistentListOf(