Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-17 12:17:29 +04:00
parent 717b8e2df2
commit 422e406d15
21 changed files with 258 additions and 112 deletions

View file

@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass
data class ValidationResponse(
@Json(name = "status") val status: String,
@Json(name = "result_type") val resultType: String,
@Json(name = "description") val description: String?,
)

View file

@ -39,6 +39,7 @@ internal object BlockAidMapper {
} else {
mapSimulationSuccessResult(from.simulation.accountSummary)
},
description = from.validation.description,
)
}

View file

@ -46,7 +46,7 @@ class BlockAidMapperTest {
spenders = mapOf("spender" to spenderDetails),
)
val response = TransactionScanResponse(
validation = ValidationResponse(status = "Success", resultType = "Benign"),
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
simulation = SimulationResponse(
status = "Success",
accountSummary = AccountSummaryResponse(
@ -79,7 +79,7 @@ class BlockAidMapperTest {
outTransfer = listOf(Transfer(value = "1.5", rawValue = "0x2")),
)
val response = TransactionScanResponse(
validation = ValidationResponse(status = "Success", resultType = "Benign"),
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
simulation = SimulationResponse(
status = "Success",
accountSummary = AccountSummaryResponse(
@ -105,7 +105,7 @@ class BlockAidMapperTest {
@Test
fun `when response error validation rhen returns failed to validate`() {
val response = TransactionScanResponse(
validation = ValidationResponse(status = "Error", resultType = "Benign"),
validation = ValidationResponse(status = "Error", resultType = "Benign", description = ""),
simulation = SimulationResponse(
status = "Success",
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
@ -120,7 +120,7 @@ class BlockAidMapperTest {
@Test
fun `when response not benign then returns validation unsafe`() {
val response = TransactionScanResponse(
validation = ValidationResponse(status = "Success", resultType = "Phishing"),
validation = ValidationResponse(status = "Success", resultType = "Phishing", description = ""),
simulation = SimulationResponse(
status = "Success",
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
@ -134,7 +134,7 @@ class BlockAidMapperTest {
@Test
fun `when response simulation not success then returns simulation failed ro simulate`() {
val response = TransactionScanResponse(
validation = ValidationResponse(status = "Success", resultType = "Benign"),
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
simulation = SimulationResponse(
status = "Error",
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
@ -148,7 +148,7 @@ class BlockAidMapperTest {
@Test
fun `when response simulation is empty then returns failed to simulate`() {
val txResponse = TransactionScanResponse(
validation = ValidationResponse(status = "Success", resultType = "Benign"),
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
simulation = SimulationResponse(
status = "Success",
accountSummary = AccountSummaryResponse(

View file

@ -119,7 +119,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
emitAll(delegate.invoke(transactionData))
}
override suspend fun getAmount(): Amount? {
override fun getAmount(): Amount? {
return approvalAmount
}

View file

@ -127,7 +127,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
return dAppFee
}
override suspend fun getAmount(): Amount? {
override fun getAmount(): Amount? {
return approvalAmount
}

View file

@ -8,5 +8,6 @@ package com.domain.blockaid.models.transaction
*/
data class CheckTransactionResult(
val validation: ValidationResult,
val description: String? = null,
val simulation: SimulationResult,
)

View file

@ -44,6 +44,6 @@ interface WcMutableFee {
* [updateAmount] triggered a new [TransactionData] emit
*/
interface WcApproval {
suspend fun getAmount(): Amount?
fun getAmount(): Amount?
fun updateAmount(amount: Amount?)
}

View file

@ -60,6 +60,7 @@ internal abstract class WcCommonTransactionComponentDelegate(
is WcTransactionRoutes.Transaction,
is WcTransactionRoutes.TransactionRequestInfo,
is WcTransactionRoutes.Alert,
is WcTransactionRoutes.CustomAllowance,
-> stackNavigation?.pop()
else -> Unit
}

View file

@ -4,7 +4,9 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.connections.components.AlertsComponentV2
import com.tangem.features.walletconnect.connections.utils.WcAlertsFactory.createCommonTransactionAppInfoAlertUM
import com.tangem.features.walletconnect.transaction.components.send.WcCustomAllowanceComponent
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
internal fun getWcCommonScreen(
@ -23,5 +25,12 @@ internal fun getWcCommonScreen(
appComponentContext = appComponentContext,
model = model,
)
is WcTransactionRoutes.CustomAllowance -> WcCustomAllowanceComponent(
appComponentContext = appComponentContext,
model = model,
onClickDoneCustomAllowance = { value, isUnlimited ->
(model as? WcSendTransactionModel)?.onClickDoneCustomAllowance(value, isUnlimited)
},
)
}
}

View file

@ -0,0 +1,39 @@
package com.tangem.features.walletconnect.transaction.components.send
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
import com.tangem.features.walletconnect.transaction.ui.approve.WcCustomAllowanceContent
import java.math.BigDecimal
internal class WcCustomAllowanceComponent(
private val appComponentContext: AppComponentContext,
private val model: WcCommonTransactionModel,
private val onClickDoneCustomAllowance: (BigDecimal, Boolean) -> Unit,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
override fun dismiss() {
model.dismiss()
}
@Composable
override fun BottomSheet() {
val content by model.uiState.collectAsStateWithLifecycle()
val spendAllowance = (content as? WcSendTransactionUM)?.spendAllowance
if (spendAllowance != null) {
WcCustomAllowanceContent(
state = spendAllowance,
onClickDone = { value, isUnlimited ->
router.pop()
onClickDoneCustomAllowance(value, isUnlimited)
},
onBack = router::pop,
onDismiss = ::dismiss,
)
}
}
}

View file

@ -27,6 +27,7 @@ internal class WcSendTransactionComponent(
onClickTransactionRequest = model::showTransactionRequest,
onBack = router::pop,
onDismiss = ::dismiss,
onClickAllowToSpend = model::onClickAllowToSpend,
)
}
}

View file

@ -1,9 +1,14 @@
package com.tangem.features.walletconnect.transaction.entity.approve
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.extensions.TextReference
import java.math.BigDecimal
internal data class WcSpendAllowanceUM(
val amountValue: BigDecimal,
val isUnlimited: Boolean,
val amountText: TextReference,
val tokenSymbol: String,
val tokenImageUrl: String?,
)
val networkIconRes: Int?,
) : TangemBottomSheetConfigContent

View file

@ -1,6 +1,7 @@
package com.tangem.features.walletconnect.transaction.entity.send
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
@ -10,6 +11,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
internal data class WcSendTransactionUM(
val transaction: WcSendTransactionItemUM,
override val transactionRequestInfo: WcTransactionRequestInfoUM,
val spendAllowance: WcSpendAllowanceUM? = null,
) : WcCommonTransactionUM
internal data class WcSendTransactionItemUM(

View file

@ -24,6 +24,7 @@ import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiv
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.math.BigDecimal
import javax.inject.Inject
@Suppress("LongParameterList")
@ -46,6 +47,8 @@ internal class WcSendTransactionModel @Inject constructor(
val stackNavigation = StackNavigation<WcTransactionRoutes>()
private var wcApproval: WcApproval? = null
init {
@Suppress("UnusedPrivateMember")
modelScope.launch {
@ -77,6 +80,7 @@ internal class WcSendTransactionModel @Inject constructor(
val isSecurityCheckContent = securityCheck is Lce.Content
var isApprovalMethod = isSecurityCheckContent &&
securityCheck.content is BlockAidTransactionCheck.Result.Approval
wcApproval = useCase as? WcApproval
buildUiState(securityCheck, useCase, signState)
}
else -> unknownMethodRunnable()
@ -90,7 +94,9 @@ internal class WcSendTransactionModel @Inject constructor(
signState: WcSignState<*>,
) {
val blockAidState = when (securityCheck) {
is Lce.Content -> blockAidUiConverter.convert(securityCheck.content.result)
is Lce.Content -> blockAidUiConverter.convert(
WcSendAndReceiveBlockAidUiConverter.Input(securityCheck.content.result, wcApproval?.getAmount()),
)
is Lce.Error -> WcSendReceiveTransactionCheckResultsUM(isLoading = false)
is Lce.Loading -> WcSendReceiveTransactionCheckResultsUM(isLoading = true)
}
@ -108,6 +114,7 @@ internal class WcSendTransactionModel @Inject constructor(
) as? WcSendTransactionUM
transactionUM = transactionUM?.copy(
transaction = transactionUM.transaction.copy(estimatedWalletChanges = blockAidState),
spendAllowance = blockAidState.spendAllowance,
)
_uiState.emit(transactionUM)
}
@ -120,6 +127,17 @@ internal class WcSendTransactionModel @Inject constructor(
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
}
fun onClickDoneCustomAllowance(value: BigDecimal, isUnlimited: Boolean) {
val maxValue = if (isUnlimited) Double.MAX_VALUE.toBigDecimal() else value
wcApproval?.getAmount()?.let { currentAmount ->
wcApproval?.updateAmount(currentAmount.copy(maxValue = maxValue))
}
}
fun onClickAllowToSpend() {
wcApproval?.let { stackNavigation.pushNew(WcTransactionRoutes.CustomAllowance) }
}
private fun showVerifiedAlert(appName: String) {
stackNavigation.pushNew(WcTransactionRoutes.Alert(WcTransactionRoutes.Alert.Type.Verified(appName)))
}

View file

@ -14,6 +14,9 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout
@Serializable
data object TransactionRequestInfo : WcTransactionRoutes()
@Serializable
data object CustomAllowance : WcTransactionRoutes()
@Serializable
data class Alert(val type: Type) : WcTransactionRoutes() {
@Serializable

View file

@ -12,7 +12,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -23,91 +23,126 @@ import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.TangemSwitch
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
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.approve.WcCustomAllowanceUM
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
import java.math.BigDecimal
@Composable
internal fun WcCustomAllowanceContent(
allowance: WcCustomAllowanceUM,
onAmountChange: (String) -> Unit,
onToggleChange: (Boolean) -> Unit,
onClickDone: () -> Unit,
modifier: Modifier = Modifier,
state: WcSpendAllowanceUM,
onClickDone: (BigDecimal, Boolean) -> Unit,
onBack: () -> Unit,
onDismiss: () -> Unit,
) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.tertiary)
.fillMaxWidth()
.padding(TangemTheme.dimens.size16),
) {
AmountTextField(
allowance = allowance,
onAmountChange = onAmountChange,
)
var amountText by remember { mutableStateOf(state.amountValue.toPlainString()) }
var isUnlimited by remember { mutableStateOf(state.isUnlimited) }
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
.background(color = TangemTheme.colors.background.action)
.padding(TangemTheme.dimens.spacing16)
.fillMaxWidth(),
) {
Text(
text = stringResourceSafe(R.string.wc_unlimited_amount),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.weight(1f),
)
TangemSwitch(
checked = allowance.isUnlimited,
onCheckedChange = onToggleChange,
)
}
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing60))
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = stringResourceSafe(R.string.common_done),
onClick = onClickDone,
)
val amount: BigDecimal by remember(amountText) {
mutableStateOf(amountText.toBigDecimalOrNull() ?: state.amountValue)
}
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
containerColor = TangemTheme.colors.background.tertiary,
onBack = onBack,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.wc_custom_allowance_title),
startIconRes = R.drawable.ic_back_24,
onStartClick = onBack,
)
},
content = {
Column(
modifier = Modifier
.background(TangemTheme.colors.background.tertiary)
.fillMaxWidth()
.padding(16.dp),
) {
AmountTextField(
amountText = amountText,
onAmountChange = { amountText = it },
isEnabled = !isUnlimited,
tokenImageUrl = state.tokenImageUrl,
networkIconRes = state.networkIconRes,
)
Spacer(modifier = Modifier.height(16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(16.dp)
.fillMaxWidth(),
) {
Text(
text = stringResourceSafe(R.string.wc_unlimited_amount),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.weight(1f),
)
TangemSwitch(
checked = isUnlimited,
onCheckedChange = { isUnlimited = it },
)
}
Spacer(modifier = Modifier.height(60.dp))
}
},
footer = {
PrimaryButton(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.common_done),
onClick = { onClickDone(amount, isUnlimited) },
)
},
)
}
@Composable
private fun AmountTextField(
allowance: WcCustomAllowanceUM,
amountText: String,
onAmountChange: (String) -> Unit,
isEnabled: Boolean,
tokenImageUrl: String?,
@DrawableRes networkIconRes: Int?,
modifier: Modifier = Modifier,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
.background(color = TangemTheme.colors.background.action)
.padding(
end = TangemTheme.dimens.size8,
top = TangemTheme.dimens.size4,
bottom = TangemTheme.dimens.size4,
)
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(end = 8.dp, top = 4.dp, bottom = 4.dp)
.fillMaxWidth(),
) {
TextField(
value = allowance.amountText,
value = amountText,
onValueChange = onAmountChange,
enabled = isEnabled,
label = {
Text(
text = stringResourceSafe(R.string.send_amount_label),
@ -127,6 +162,9 @@ private fun AmountTextField(
unfocusedTextColor = TangemTheme.colors.text.primary1,
focusedPlaceholderColor = TangemTheme.colors.text.disabled,
unfocusedPlaceholderColor = TangemTheme.colors.text.disabled,
disabledContainerColor = Color.Transparent,
disabledTextColor = TangemTheme.colors.text.disabled,
disabledIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
cursorColor = TangemTheme.colors.icon.primary1,
@ -134,40 +172,44 @@ private fun AmountTextField(
)
TokenWithNetworkIcon(
tokenIconUrl = allowance.tokenIconUrl,
networkIconRes = allowance.networkIconRes,
tokenIconUrl = tokenImageUrl,
networkIconRes = networkIconRes,
)
}
}
@Composable
private fun TokenWithNetworkIcon(
tokenIconUrl: String,
@DrawableRes networkIconRes: Int,
tokenIconUrl: String?,
@DrawableRes networkIconRes: Int?,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.size(TangemTheme.dimens.size48),
modifier = modifier.size(48.dp),
) {
AsyncImage(
model = tokenIconUrl,
contentDescription = null,
modifier = Modifier
.aspectRatio(1f)
.clip(CircleShape)
.align(Alignment.Center)
.padding(TangemTheme.dimens.spacing8),
model = tokenIconUrl,
contentDescription = null,
)
Image(
modifier = Modifier
.align(Alignment.TopEnd)
.size(TangemTheme.dimens.size18)
.border(TangemTheme.dimens.size2, TangemTheme.colors.background.action, CircleShape)
.padding(TangemTheme.dimens.spacing2),
painter = painterResource(id = networkIconRes),
contentDescription = null,
.padding(8.dp),
placeholder = painterResource(R.drawable.ic_nft_placeholder_20),
error = painterResource(R.drawable.ic_nft_placeholder_20),
)
if (networkIconRes != null) {
Image(
modifier = Modifier
.align(Alignment.TopEnd)
.size(18.dp)
.border(2.dp, TangemTheme.colors.background.action, CircleShape)
.padding(2.dp),
painter = painterResource(networkIconRes),
contentDescription = null,
)
}
}
}
@ -175,10 +217,10 @@ private fun TokenWithNetworkIcon(
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
internal fun PreviewWcCustomAllowanceContent(
@PreviewParameter(WcCustomAllowanceStateProvider::class) state: WcCustomAllowanceUM,
@PreviewParameter(WcCustomAllowanceStateProvider::class) state: WcSpendAllowanceUM,
) {
TangemThemePreview {
TangemModalBottomSheet<WcCustomAllowanceUM>(
TangemModalBottomSheet<WcSpendAllowanceUM>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
@ -196,22 +238,24 @@ internal fun PreviewWcCustomAllowanceContent(
},
content = {
WcCustomAllowanceContent(
allowance = state,
onAmountChange = {},
onToggleChange = {},
onClickDone = {},
state = state,
onClickDone = { _, _ -> },
onBack = {},
onDismiss = {},
)
},
)
}
}
private class WcCustomAllowanceStateProvider : CollectionPreviewParameterProvider<WcCustomAllowanceUM>(
private class WcCustomAllowanceStateProvider : CollectionPreviewParameterProvider<WcSpendAllowanceUM>(
listOf(
WcCustomAllowanceUM(
WcSpendAllowanceUM(
networkIconRes = R.drawable.img_eth_22,
tokenIconUrl = "https://tangem.com",
amountText = "100",
tokenImageUrl = "https://tangem.com",
amountText = TextReference.Str("100"),
amountValue = BigDecimal("100"),
tokenSymbol = "ETH",
isUnlimited = false,
),
),

View file

@ -2,6 +2,7 @@ package com.tangem.features.walletconnect.transaction.ui.approve
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
@ -22,13 +23,18 @@ import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllow
import com.tangem.features.walletconnect.transaction.ui.common.WcSmallTitleItem
@Composable
internal fun WcSpendAllowanceItem(spendAllowance: WcSpendAllowanceUM, modifier: Modifier = Modifier) {
internal fun WcSpendAllowanceItem(
spendAllowance: WcSpendAllowanceUM,
onClickAllowToSpend: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.fillMaxWidth()
.padding(end = 12.dp, bottom = 12.dp),
.padding(end = 12.dp, bottom = 12.dp)
.clickable { onClickAllowToSpend() },
) {
WcSmallTitleItem(R.string.wc_allow_to_spend)

View file

@ -23,7 +23,11 @@ import com.tangem.features.walletconnect.transaction.ui.approve.WcSpendAllowance
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun TransactionCheckResultsItem(item: WcSendReceiveTransactionCheckResultsUM, modifier: Modifier = Modifier) {
internal fun TransactionCheckResultsItem(
item: WcSendReceiveTransactionCheckResultsUM,
onClickAllowToSpend: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth(),
@ -38,7 +42,7 @@ internal fun TransactionCheckResultsItem(item: WcSendReceiveTransactionCheckResu
if (item.estimatedWalletChanges != null) {
WcEstimatedWalletChangesItem(item.estimatedWalletChanges)
} else if (item.spendAllowance != null) {
WcSpendAllowanceItem(item.spendAllowance)
WcSpendAllowanceItem(item.spendAllowance, onClickAllowToSpend)
} else {
WcEstimatedWalletChangesNotLoadedItem()
}
@ -57,7 +61,7 @@ private fun TransactionCheckResultsItemPreview(
modifier = Modifier
.background(TangemTheme.colors.background.tertiary),
) {
TransactionCheckResultsItem(item = item)
TransactionCheckResultsItem(item = item, {})
}
}
}

View file

@ -5,6 +5,7 @@ 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.blockchain.common.Amount
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
@ -21,21 +22,20 @@ import javax.inject.Inject
internal const val DECIMALS_AMOUNT = 2
@Suppress("CyclomaticComplexMethod")
internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor(
private val estimatedWalletChangeUMConverter: WcEstimatedWalletChangeUMConverter,
private val spendAllowanceUMConverter: WcSpendAllowanceUMConverter,
) :
Converter<CheckTransactionResult, WcSendReceiveTransactionCheckResultsUM> {
override fun convert(value: CheckTransactionResult): WcSendReceiveTransactionCheckResultsUM {
) : Converter<WcSendAndReceiveBlockAidUiConverter.Input, WcSendReceiveTransactionCheckResultsUM> {
override fun convert(value: Input): WcSendReceiveTransactionCheckResultsUM {
val description = value.result.description
return WcSendReceiveTransactionCheckResultsUM(
isLoading = false,
notificationText = when (value.validation) {
notificationText = when (value.result.validation) {
ValidationResult.SAFE, ValidationResult.FAILED_TO_VALIDATE -> null
// TODO("Add text after approve with designer")
ValidationResult.UNSAFE ->
TextReference.Str("The transaction approves tokens to a known malicious address")
ValidationResult.UNSAFE -> if (!description.isNullOrEmpty()) TextReference.Str(description) else null
},
estimatedWalletChanges = (value.simulation as? SimulationResult.Success)?.data?.let { data ->
estimatedWalletChanges = (value.result.simulation as? SimulationResult.Success)?.data?.let { data ->
when (data) {
is SimulationData.Approve -> null
is SimulationData.SendAndReceive -> {
@ -82,15 +82,21 @@ internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor(
}
}
},
spendAllowance = (value.simulation as? SimulationResult.Success)?.data?.let { data ->
spendAllowance = (value.result.simulation as? SimulationResult.Success)?.data?.let { data ->
when (data) {
is SimulationData.SendAndReceive -> null
is SimulationData.Approve -> data.approvedAmounts.map { spendAllowanceUMConverter.convert(it) }
.firstOrNull()
is SimulationData.Approve -> value.approvedAmount?.let {
data.approvedAmounts.map { spendAllowanceUMConverter.convert(it) }.firstOrNull()
}
}
},
)
}
data class Input(
val result: CheckTransactionResult,
val approvedAmount: Amount?,
)
}
internal fun BigDecimal.amountText() = format { crypto("", DECIMALS_AMOUNT) }

View file

@ -2,6 +2,7 @@ package com.tangem.features.walletconnect.transaction.ui.blockaid
import com.domain.blockaid.models.transaction.simultation.ApprovedAmount
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
import com.tangem.utils.converter.Converter
@ -10,12 +11,15 @@ import javax.inject.Inject
internal class WcSpendAllowanceUMConverter @Inject constructor() : Converter<ApprovedAmount, WcSpendAllowanceUM> {
override fun convert(value: ApprovedAmount) = WcSpendAllowanceUM(
amountValue = value.approvedAmount,
amountText = if (value.isUnlimited) {
TextReference.Res(R.string.wc_common_unlimited)
} else {
TextReference.Str(value.approvedAmount.amountText())
},
isUnlimited = value.isUnlimited,
tokenSymbol = value.tokenInfo.symbol,
tokenImageUrl = value.tokenInfo.logoUrl,
networkIconRes = value.tokenInfo.chainId?.toString()?.let { getActiveIconRes(it) },
)
}

View file

@ -46,6 +46,7 @@ internal fun WcSendTransactionModalBottomSheet(
onClickTransactionRequest: () -> Unit,
onBack: () -> Unit,
onDismiss: () -> Unit,
onClickAllowToSpend: () -> Unit,
) {
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
@ -92,7 +93,7 @@ internal fun WcSendTransactionModalBottomSheet(
}
Column(modifier = Modifier.padding(top = 16.dp)) {
if (state.estimatedWalletChanges != null) {
TransactionCheckResultsItem(state.estimatedWalletChanges)
TransactionCheckResultsItem(state.estimatedWalletChanges, onClickAllowToSpend)
}
Spacer(Modifier.height(16.dp))
WcSendTransactionItems(
@ -140,7 +141,7 @@ private fun WcSendTransactionBottomSheetPreview(
)
},
content = {
WcSendTransactionModalBottomSheet(state, {}, {}, {})
WcSendTransactionModalBottomSheet(state, {}, {}, {}, {})
},
)
}