Updated on 2026-08-14
This commit is contained in:
parent
9e932a5bee
commit
a667fcc955
10 changed files with 178 additions and 27 deletions
|
|
@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass
|
||||||
data class AccountSummaryResponse(
|
data class AccountSummaryResponse(
|
||||||
@Json(name = "assets_diffs") val assetsDiffs: List<AssetDiff>,
|
@Json(name = "assets_diffs") val assetsDiffs: List<AssetDiff>,
|
||||||
@Json(name = "exposures") val exposures: List<Exposure>,
|
@Json(name = "exposures") val exposures: List<Exposure>,
|
||||||
|
@Json(name = "traces") val traces: List<Trace>?,
|
||||||
)
|
)
|
||||||
|
|
@ -20,6 +20,6 @@ data class Asset(
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class Transfer(
|
data class Transfer(
|
||||||
@Json(name = "value") val value: String,
|
@Json(name = "value") val value: String?,
|
||||||
@Json(name = "raw_value") val rawValue: String,
|
@Json(name = "raw_value") val rawValue: String?,
|
||||||
)
|
)
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
@ -67,6 +67,7 @@ internal object BlockAidMapper {
|
||||||
return when {
|
return when {
|
||||||
from.assetsDiffs.isEmpty() && from.exposures.isNotEmpty() -> mapApproveTransaction(from.exposures)
|
from.assetsDiffs.isEmpty() && from.exposures.isNotEmpty() -> mapApproveTransaction(from.exposures)
|
||||||
from.assetsDiffs.isNotEmpty() && from.exposures.isEmpty() -> mapSendReceiveTransaction(from.assetsDiffs)
|
from.assetsDiffs.isNotEmpty() && from.exposures.isEmpty() -> mapSendReceiveTransaction(from.assetsDiffs)
|
||||||
|
!from.traces.isNullOrEmpty() -> mapNftSendReceiveTransaction(from.traces)
|
||||||
else -> SimulationResult.FailedToSimulate
|
else -> SimulationResult.FailedToSimulate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -107,13 +108,13 @@ internal object BlockAidMapper {
|
||||||
symbol = diff.asset.symbol,
|
symbol = diff.asset.symbol,
|
||||||
)
|
)
|
||||||
diff.outTransfer.orEmpty().forEach { transfer ->
|
diff.outTransfer.orEmpty().forEach { transfer ->
|
||||||
transfer.value.toBigDecimalOrNull()?.let { amount ->
|
transfer.value?.toBigDecimalOrNull()?.let { amount ->
|
||||||
sendInfo.add(AmountInfo(amount = amount, token = token))
|
sendInfo.add(AmountInfo.FungibleTokens(amount = amount, token = token))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diff.inTransfer.orEmpty().forEach { transfer ->
|
diff.inTransfer.orEmpty().forEach { transfer ->
|
||||||
transfer.value.toBigDecimalOrNull()?.let { amount ->
|
transfer.value?.toBigDecimalOrNull()?.let { amount ->
|
||||||
receiveInfo.add(AmountInfo(amount = amount, token = token))
|
receiveInfo.add(AmountInfo.FungibleTokens(amount = amount, token = token))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,4 +125,16 @@ internal object BlockAidMapper {
|
||||||
SimulationResult.FailedToSimulate
|
SimulationResult.FailedToSimulate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun mapNftSendReceiveTransaction(traces: List<Trace>?): 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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.blockaid
|
||||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||||
import com.domain.blockaid.models.transaction.SimulationResult
|
import com.domain.blockaid.models.transaction.SimulationResult
|
||||||
import com.domain.blockaid.models.transaction.ValidationResult
|
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.domain.blockaid.models.transaction.simultation.SimulationData
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import com.tangem.datasource.api.common.blockaid.models.response.*
|
import com.tangem.datasource.api.common.blockaid.models.response.*
|
||||||
|
|
@ -48,7 +49,11 @@ class BlockAidMapperTest {
|
||||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||||
simulation = SimulationResponse(
|
simulation = SimulationResponse(
|
||||||
status = "Success",
|
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"),
|
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||||
simulation = SimulationResponse(
|
simulation = SimulationResponse(
|
||||||
status = "Success",
|
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
|
val data = simulation?.data as? SimulationData.SendAndReceive
|
||||||
Truth.assertThat(data).isNotNull()
|
Truth.assertThat(data).isNotNull()
|
||||||
Truth.assertThat(data?.send?.first()?.amount).isEqualTo(BigDecimal("1.5"))
|
Truth.assertThat((data?.send?.first() as? AmountInfo.FungibleTokens)?.amount).isEqualTo(BigDecimal("1.5"))
|
||||||
Truth.assertThat(data?.receive?.first()?.amount).isEqualTo(BigDecimal("2.0"))
|
Truth.assertThat((data?.receive?.first() as? AmountInfo.FungibleTokens)?.amount).isEqualTo(BigDecimal("2.0"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -99,7 +108,7 @@ class BlockAidMapperTest {
|
||||||
validation = ValidationResponse(status = "Error", resultType = "Benign"),
|
validation = ValidationResponse(status = "Error", resultType = "Benign"),
|
||||||
simulation = SimulationResponse(
|
simulation = SimulationResponse(
|
||||||
status = "Success",
|
status = "Success",
|
||||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList()),
|
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -114,7 +123,7 @@ class BlockAidMapperTest {
|
||||||
validation = ValidationResponse(status = "Success", resultType = "Phishing"),
|
validation = ValidationResponse(status = "Success", resultType = "Phishing"),
|
||||||
simulation = SimulationResponse(
|
simulation = SimulationResponse(
|
||||||
status = "Success",
|
status = "Success",
|
||||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList()),
|
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -128,7 +137,7 @@ class BlockAidMapperTest {
|
||||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||||
simulation = SimulationResponse(
|
simulation = SimulationResponse(
|
||||||
status = "Error",
|
status = "Error",
|
||||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList()),
|
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -145,6 +154,7 @@ class BlockAidMapperTest {
|
||||||
accountSummary = AccountSummaryResponse(
|
accountSummary = AccountSummaryResponse(
|
||||||
assetsDiffs = emptyList(),
|
assetsDiffs = emptyList(),
|
||||||
exposures = emptyList(),
|
exposures = emptyList(),
|
||||||
|
traces = null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,15 @@ package com.domain.blockaid.models.transaction.simultation
|
||||||
|
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
data class AmountInfo(
|
sealed class AmountInfo {
|
||||||
val amount: BigDecimal,
|
|
||||||
val token: TokenInfo,
|
data class FungibleTokens(
|
||||||
)
|
val amount: BigDecimal,
|
||||||
|
val token: TokenInfo,
|
||||||
|
) : AmountInfo()
|
||||||
|
|
||||||
|
data class NonFungibleTokens(
|
||||||
|
val name: String,
|
||||||
|
val logoUrl: String?,
|
||||||
|
) : AmountInfo()
|
||||||
|
}
|
||||||
|
|
@ -12,5 +12,5 @@ internal data class WcEstimatedWalletChangeUM(
|
||||||
@DrawableRes val iconRes: Int,
|
@DrawableRes val iconRes: Int,
|
||||||
val title: TextReference,
|
val title: TextReference,
|
||||||
val description: String,
|
val description: String,
|
||||||
val tokenIconUrl: String,
|
val tokenIconUrl: String?,
|
||||||
)
|
)
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
@ -10,27 +10,33 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import androidx.compose.ui.unit.dp
|
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.extensions.resourceReference
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.features.walletconnect.impl.R
|
import com.tangem.features.walletconnect.impl.R
|
||||||
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcEstimatedWalletChangeUM
|
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.WcEstimatedWalletChangesUM
|
||||||
import com.tangem.features.walletconnect.transaction.entity.blockaid.TransactionCheckResultsUM
|
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@Composable
|
@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(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
if (item.notificationText != null) {
|
item.notificationText?.let {
|
||||||
WcTransactionCheckErrorItem(item.notificationText)
|
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)
|
||||||
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
private fun TransactionCheckResultsItemPreview(
|
private fun TransactionCheckResultsItemPreview(
|
||||||
@PreviewParameter(TransactionCheckResultsItemProvider::class) item: TransactionCheckResultsUM,
|
@PreviewParameter(TransactionCheckResultsItemProvider::class) item: WcSendReceiveTransactionCheckResultsUM,
|
||||||
) {
|
) {
|
||||||
TangemThemePreview {
|
TangemThemePreview {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -50,10 +56,10 @@ private fun TransactionCheckResultsItemPreview(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TransactionCheckResultsItemProvider : PreviewParameterProvider<TransactionCheckResultsUM> {
|
private class TransactionCheckResultsItemProvider : PreviewParameterProvider<WcSendReceiveTransactionCheckResultsUM> {
|
||||||
override val values = sequenceOf(
|
override val values = sequenceOf(
|
||||||
TransactionCheckResultsUM(
|
WcSendReceiveTransactionCheckResultsUM(
|
||||||
notificationText = "The transaction approves erc20 tokens to a known malicious address",
|
notificationText = TextReference.Str("The transaction approves erc20 tokens to a known malicious address"),
|
||||||
estimatedWalletChanges = WcEstimatedWalletChangesUM(
|
estimatedWalletChanges = WcEstimatedWalletChangesUM(
|
||||||
items = persistentListOf(
|
items = persistentListOf(
|
||||||
WcEstimatedWalletChangeUM(
|
WcEstimatedWalletChangeUM(
|
||||||
|
|
|
||||||
|
|
@ -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<CheckTransactionResult, WcSendReceiveTransactionCheckResultsUM> {
|
||||||
|
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<WcEstimatedWalletChangeUM> = (
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue