From a667fcc955451c37600021bfb78f0beb16f2590f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 May 2025 13:43:55 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../models/response/AccountSummaryResponse.kt | 1 + .../blockaid/models/response/AssetDiff.kt | 4 +- .../common/blockaid/models/response/Trace.kt | 21 +++++ .../tangem/data/blockaid/BlockAidMapper.kt | 21 ++++- .../data/blockaid/BlockAidMapperTest.kt | 24 ++++-- .../transaction/simultation/AmountInfo.kt | 16 +++- .../blockaid/WcEstimatedWalletChangesUM.kt | 2 +- .../WcSendReceiveTransactionCheckResultsUM.kt | 8 ++ .../blockaid/TransactionCheckResultsItem.kt | 24 ++++-- .../WcSendAndReceiveBlockAidUiConverter.kt | 84 +++++++++++++++++++ 10 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Trace.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcSendReceiveTransactionCheckResultsUM.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcSendAndReceiveBlockAidUiConverter.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AccountSummaryResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AccountSummaryResponse.kt index e0188fa7dc..9b3c4ff0c0 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AccountSummaryResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AccountSummaryResponse.kt @@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass data class AccountSummaryResponse( @Json(name = "assets_diffs") val assetsDiffs: List, @Json(name = "exposures") val exposures: List, + @Json(name = "traces") val traces: List?, ) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AssetDiff.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AssetDiff.kt index 645409ec89..b1c8894daa 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AssetDiff.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/AssetDiff.kt @@ -20,6 +20,6 @@ data class Asset( @JsonClass(generateAdapter = true) data class Transfer( - @Json(name = "value") val value: String, - @Json(name = "raw_value") val rawValue: String, + @Json(name = "value") val value: String?, + @Json(name = "raw_value") val rawValue: String?, ) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Trace.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Trace.kt new file mode 100644 index 0000000000..6ae1af201f --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Trace.kt @@ -0,0 +1,21 @@ +package com.tangem.datasource.api.common.blockaid.models.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class Trace( + @Json(name = "exposed") val exposed: Exposed, + @Json(name = "asset") val asset: NftAsset, +) + +@JsonClass(generateAdapter = true) +data class Exposed( + @Json(name = "token_id") val tokenId: String, + @Json(name = "logo_url") val logoUrl: String?, +) + +@JsonClass(generateAdapter = true) +data class NftAsset( + @Json(name = "name") val name: String, +) \ No newline at end of file diff --git a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt index 3b31a9b9a7..2a02e346ed 100644 --- a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt +++ b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt @@ -67,6 +67,7 @@ internal object BlockAidMapper { return when { from.assetsDiffs.isEmpty() && from.exposures.isNotEmpty() -> mapApproveTransaction(from.exposures) from.assetsDiffs.isNotEmpty() && from.exposures.isEmpty() -> mapSendReceiveTransaction(from.assetsDiffs) + !from.traces.isNullOrEmpty() -> mapNftSendReceiveTransaction(from.traces) else -> SimulationResult.FailedToSimulate } } @@ -107,13 +108,13 @@ internal object BlockAidMapper { symbol = diff.asset.symbol, ) diff.outTransfer.orEmpty().forEach { transfer -> - transfer.value.toBigDecimalOrNull()?.let { amount -> - sendInfo.add(AmountInfo(amount = amount, token = token)) + transfer.value?.toBigDecimalOrNull()?.let { amount -> + sendInfo.add(AmountInfo.FungibleTokens(amount = amount, token = token)) } } diff.inTransfer.orEmpty().forEach { transfer -> - transfer.value.toBigDecimalOrNull()?.let { amount -> - receiveInfo.add(AmountInfo(amount = amount, token = token)) + transfer.value?.toBigDecimalOrNull()?.let { amount -> + receiveInfo.add(AmountInfo.FungibleTokens(amount = amount, token = token)) } } } @@ -124,4 +125,16 @@ internal object BlockAidMapper { SimulationResult.FailedToSimulate } } + + private fun mapNftSendReceiveTransaction(traces: List?): SimulationResult { + val sendInfo = traces?.map { + AmountInfo.NonFungibleTokens(name = "${it.asset.name} #${it.exposed.tokenId}", logoUrl = it.exposed.logoUrl) + } + + return if (!sendInfo.isNullOrEmpty()) { + SimulationResult.Success(SimulationData.SendAndReceive(send = sendInfo, receive = listOf())) + } else { + SimulationResult.FailedToSimulate + } + } } \ No newline at end of file diff --git a/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt b/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt index 175cc033b9..5ff9caaee8 100644 --- a/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt +++ b/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt @@ -3,6 +3,7 @@ package com.tangem.data.blockaid import com.domain.blockaid.models.dapp.CheckDAppResult import com.domain.blockaid.models.transaction.SimulationResult import com.domain.blockaid.models.transaction.ValidationResult +import com.domain.blockaid.models.transaction.simultation.AmountInfo import com.domain.blockaid.models.transaction.simultation.SimulationData import com.google.common.truth.Truth import com.tangem.datasource.api.common.blockaid.models.response.* @@ -48,7 +49,11 @@ class BlockAidMapperTest { validation = ValidationResponse(status = "Success", resultType = "Benign"), simulation = SimulationResponse( status = "Success", - accountSummary = AccountSummaryResponse(assetsDiffs = emptyList(), exposures = listOf(exposure)), + accountSummary = AccountSummaryResponse( + assetsDiffs = emptyList(), + exposures = listOf(exposure), + traces = null, + ), ), ) @@ -77,7 +82,11 @@ class BlockAidMapperTest { validation = ValidationResponse(status = "Success", resultType = "Benign"), simulation = SimulationResponse( status = "Success", - accountSummary = AccountSummaryResponse(exposures = emptyList(), assetsDiffs = listOf(assetDiff)), + accountSummary = AccountSummaryResponse( + exposures = emptyList(), + assetsDiffs = listOf(assetDiff), + traces = null, + ), ), ) @@ -89,8 +98,8 @@ class BlockAidMapperTest { val data = simulation?.data as? SimulationData.SendAndReceive Truth.assertThat(data).isNotNull() - Truth.assertThat(data?.send?.first()?.amount).isEqualTo(BigDecimal("1.5")) - Truth.assertThat(data?.receive?.first()?.amount).isEqualTo(BigDecimal("2.0")) + Truth.assertThat((data?.send?.first() as? AmountInfo.FungibleTokens)?.amount).isEqualTo(BigDecimal("1.5")) + Truth.assertThat((data?.receive?.first() as? AmountInfo.FungibleTokens)?.amount).isEqualTo(BigDecimal("2.0")) } @Test @@ -99,7 +108,7 @@ class BlockAidMapperTest { validation = ValidationResponse(status = "Error", resultType = "Benign"), simulation = SimulationResponse( status = "Success", - accountSummary = AccountSummaryResponse(emptyList(), emptyList()), + accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null), ), ) @@ -114,7 +123,7 @@ class BlockAidMapperTest { validation = ValidationResponse(status = "Success", resultType = "Phishing"), simulation = SimulationResponse( status = "Success", - accountSummary = AccountSummaryResponse(emptyList(), emptyList()), + accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null), ), ) @@ -128,7 +137,7 @@ class BlockAidMapperTest { validation = ValidationResponse(status = "Success", resultType = "Benign"), simulation = SimulationResponse( status = "Error", - accountSummary = AccountSummaryResponse(emptyList(), emptyList()), + accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null), ), ) @@ -145,6 +154,7 @@ class BlockAidMapperTest { accountSummary = AccountSummaryResponse( assetsDiffs = emptyList(), exposures = emptyList(), + traces = null, ), ), ) diff --git a/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/AmountInfo.kt b/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/AmountInfo.kt index 14e8b4c6d6..5a3dffeaa2 100644 --- a/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/AmountInfo.kt +++ b/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/AmountInfo.kt @@ -2,7 +2,15 @@ package com.domain.blockaid.models.transaction.simultation import java.math.BigDecimal -data class AmountInfo( - val amount: BigDecimal, - val token: TokenInfo, -) \ No newline at end of file +sealed class AmountInfo { + + data class FungibleTokens( + val amount: BigDecimal, + val token: TokenInfo, + ) : AmountInfo() + + data class NonFungibleTokens( + val name: String, + val logoUrl: String?, + ) : AmountInfo() +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcEstimatedWalletChangesUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcEstimatedWalletChangesUM.kt index 59551a7bd9..45d109a2d2 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcEstimatedWalletChangesUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcEstimatedWalletChangesUM.kt @@ -12,5 +12,5 @@ internal data class WcEstimatedWalletChangeUM( @DrawableRes val iconRes: Int, val title: TextReference, val description: String, - val tokenIconUrl: String, + val tokenIconUrl: String?, ) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcSendReceiveTransactionCheckResultsUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcSendReceiveTransactionCheckResultsUM.kt new file mode 100644 index 0000000000..87e77e9bfd --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/blockaid/WcSendReceiveTransactionCheckResultsUM.kt @@ -0,0 +1,8 @@ +package com.tangem.features.walletconnect.transaction.entity.blockaid + +import com.tangem.core.ui.extensions.TextReference + +internal data class WcSendReceiveTransactionCheckResultsUM( + val estimatedWalletChanges: WcEstimatedWalletChangesUM?, + val notificationText: TextReference? = null, +) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/TransactionCheckResultsItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/TransactionCheckResultsItem.kt index a4cbbcc8cc..a07199a0f5 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/TransactionCheckResultsItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/TransactionCheckResultsItem.kt @@ -10,27 +10,33 @@ import androidx.compose.ui.tooling.preview.Preview 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.WcEstimatedWalletChangeUM import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangesUM -import com.tangem.features.walletconnect.transaction.entity.blockaid.TransactionCheckResultsUM +import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM import kotlinx.collections.immutable.persistentListOf @Composable -internal fun TransactionCheckResultsItem(item: TransactionCheckResultsUM, modifier: Modifier = Modifier) { +internal fun TransactionCheckResultsItem(item: WcSendReceiveTransactionCheckResultsUM, modifier: Modifier = Modifier) { + if (item.notificationText == null && item.estimatedWalletChanges == null) return + Column( modifier = modifier .padding(12.dp) .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - if (item.notificationText != null) { - WcTransactionCheckErrorItem(item.notificationText) + item.notificationText?.let { + WcTransactionCheckErrorItem(it.resolveReference()) + } + item.estimatedWalletChanges?.let { + WcEstimatedWalletChangesItem(it) } - WcEstimatedWalletChangesItem(item.estimatedWalletChanges) } } @@ -38,7 +44,7 @@ internal fun TransactionCheckResultsItem(item: TransactionCheckResultsUM, modifi @Preview(showBackground = true, device = Devices.PIXEL_7_PRO) @Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES) private fun TransactionCheckResultsItemPreview( - @PreviewParameter(TransactionCheckResultsItemProvider::class) item: TransactionCheckResultsUM, + @PreviewParameter(TransactionCheckResultsItemProvider::class) item: WcSendReceiveTransactionCheckResultsUM, ) { TangemThemePreview { Box( @@ -50,10 +56,10 @@ private fun TransactionCheckResultsItemPreview( } } -private class TransactionCheckResultsItemProvider : PreviewParameterProvider { +private class TransactionCheckResultsItemProvider : PreviewParameterProvider { override val values = sequenceOf( - TransactionCheckResultsUM( - notificationText = "The transaction approves erc20 tokens to a known malicious address", + WcSendReceiveTransactionCheckResultsUM( + notificationText = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"), estimatedWalletChanges = WcEstimatedWalletChangesUM( items = persistentListOf( WcEstimatedWalletChangeUM( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcSendAndReceiveBlockAidUiConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcSendAndReceiveBlockAidUiConverter.kt new file mode 100644 index 0000000000..7bc7b50afe --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcSendAndReceiveBlockAidUiConverter.kt @@ -0,0 +1,84 @@ +package com.tangem.features.walletconnect.transaction.ui.blockaid + +import com.domain.blockaid.models.transaction.CheckTransactionResult +import com.domain.blockaid.models.transaction.SimulationResult +import com.domain.blockaid.models.transaction.ValidationResult +import com.domain.blockaid.models.transaction.simultation.AmountInfo +import com.domain.blockaid.models.transaction.simultation.SimulationData +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM +import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangeUM +import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangesUM +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList + +private const val DECIMALS_AMOUNT = 2 + +internal class WcSendAndReceiveBlockAidUiConverter : + Converter { + override fun convert(value: CheckTransactionResult): WcSendReceiveTransactionCheckResultsUM { + return WcSendReceiveTransactionCheckResultsUM( + notificationText = when (value.validation) { + ValidationResult.SAFE, ValidationResult.FAILED_TO_VALIDATE -> null + ValidationResult.UNSAFE -> TextReference.Str("") // TODO("Add text after approve with designer") + }, + estimatedWalletChanges = (value.simulation as? SimulationResult.Success)?.data?.let { data -> + when (data) { + is SimulationData.Approve -> null + is SimulationData.SendAndReceive -> { + val items: ImmutableList = ( + data.send.map { + when (it) { + is AmountInfo.FungibleTokens -> it.toUM( + iconRes = R.drawable.ic_send_new_24, + titleRes = R.string.common_send, + ) + is AmountInfo.NonFungibleTokens -> it.toUM( + iconRes = R.drawable.ic_send_new_24, + titleRes = R.string.common_send, + ) + } + } + data.receive.map { + when (it) { + is AmountInfo.FungibleTokens -> it.toUM( + iconRes = R.drawable.ic_receive_new_24, + titleRes = R.string.common_receive, + ) + is AmountInfo.NonFungibleTokens -> it.toUM( + iconRes = R.drawable.ic_receive_new_24, + titleRes = R.string.common_receive, + ) + } + } + ).toImmutableList() + WcEstimatedWalletChangesUM(items) + } + } + }, + ) + } +} + +private fun AmountInfo.FungibleTokens.toUM(iconRes: Int, titleRes: Int): WcEstimatedWalletChangeUM { + val amountText = amount.format { crypto(token.symbol, DECIMALS_AMOUNT) } + return WcEstimatedWalletChangeUM( + iconRes = iconRes, + title = resourceReference(titleRes), + description = "- $amountText", + tokenIconUrl = token.logoUrl, + ) +} + +private fun AmountInfo.NonFungibleTokens.toUM(iconRes: Int, titleRes: Int): WcEstimatedWalletChangeUM { + return WcEstimatedWalletChangeUM( + iconRes = iconRes, + title = resourceReference(titleRes), + description = name, + tokenIconUrl = logoUrl, + ) +} \ No newline at end of file