From b0386eb54b7203ccaa0871e84a8f2f86be77b712 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 16:39:03 +0300 Subject: [PATCH 01/17] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index e3fa514003..9d9b4e4c32 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1125" +tangemBlockchainSdk = "releases-5.27.0-1126" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-505" +tangemCardSdk = "releases-5.27.0-507" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From 9c68c9ff00d89345da7a1e1ee8ef1c72ec7ff959 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 18:11:07 +0400 Subject: [PATCH 02/17] Updated on 2026-08-14 --- core/res/src/main/res/values-fr/strings.xml | 2 ++ core/res/src/main/res/values-ja/strings.xml | 12 +++++++ core/res/src/main/res/values-ru/strings.xml | 2 ++ .../src/main/res/values-uk-rUA/strings.xml | 2 ++ core/res/src/main/res/values/strings.xml | 12 +++++++ .../components/AlertsComponentV2.kt | 1 + .../connections/components/WcPairComponent.kt | 7 ++-- .../connections/model/WcPairModel.kt | 19 ++++++++--- .../connections/routes/WcAppInfoRoutes.kt | 1 + .../connections/utils/WcAlertsFactory.kt | 32 +++++++++++++++---- 10 files changed, 78 insertions(+), 12 deletions(-) diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 5d857cb793..075f05f465 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -1087,6 +1087,8 @@ de : %s à : %s validateur : %s + Les notifications sont activées, mais elles ne fonctionneront pas tant que vous n\'aurez pas autorisé les notifications dans les paramètres de votre appareil. + Notifications de transaction Minimum %s Le montant minimum pour effectuer cette transaction est %1$s. Réessayez diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 7af3b7a089..543d824195 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -61,6 +61,13 @@ システムのデフォルト テーマ アプリ設定 + ウォレットを追加 + ログインするウォレットを選択する + お帰りなさい! + + %d 枚のカード + + スマートフォンウォレット ウォレットのバックアップが正常に完了しました。 これらの単語は紛失した場合、復元できません。必ず安全な場所に保管してください。 バックアップが完了しました @@ -846,6 +853,7 @@ %1$s 、 %2$s 宛先タグ アドレスを入力 + ENS名またはアドレスを入力 アドレスはウォレットアドレスと同じです 最低額は%sです 最小の変更は%sです @@ -927,11 +935,15 @@ 無効な金額 手数料が残高を超えています 合計金額が残高を超えています + トークンを変更してもよろしいですか? 変更後、以前のデータはリセットされます。 + トークンの変更 スワップして送信 トークンを送信すれば、送信中に変換されます。受信者は必要なものをシームレスに受け取ります。 受信者は受け取ります 受信者に送信されます 受取金額 + 変換をキャンセルしてもよろしいですか? 変更後、以前のデータはリセットされます。 + キャンセルの確認 スワップして送信 取引が送信されました 設定したいカードまたはリングをスキャンするために準備してください。 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 071f03c35f..71e9bf29d3 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1105,6 +1105,8 @@ от: %s на: %s валидатор: %s + Уведомления включены, но не будут работать, пока вы не разрешите их в настройках устройства. + Уведомления о транзакциях Минимум %s Минимальная сумма транзакции равна %1$s. Комиссии сети Tron для популярных токенов могут быть выше. Стейкинг TRX может помочь снизить расходы на транзакции. diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index fe103942ab..9b3e3dfe52 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -1105,6 +1105,8 @@ від: %s до: %s валідатор: %s + Сповіщення ввімкнені, але не працюватимуть, доки ви не дозволите їх у налаштуваннях пристрою. + Сповіщення про транзакції Мінімум %s Мінімальна сума транзакції становить %1$s. Комісії мережі Tron для популярних токенів можуть бути вищими. Стейкінг TRX може допомогти знизити транзакційні витрати. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 2ee799d6f3..9afd794d7f 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -64,6 +64,10 @@ Add Wallet Select a wallet to log in Welcome back! + + %d card + %d cards + Phone Wallet You successfully backed up your wallet. These words can’t be recovered if lost. Make sure to keep it somewhere secure. @@ -426,6 +430,7 @@ To complete setup, back up your wallet and secure app access with a Access Code. Finish Now Finish Wallet Activation + To complete setup, secure app access with Access Code. Recover an existing wallet stored in your Google Drive backup Google Drive Backup Go to backup @@ -866,6 +871,7 @@ %1$s, %2$s Destination Tag Enter address + Enter ENS name or address Address is the same as wallet address Minimum amount is %s Minimum change is %s @@ -947,11 +953,15 @@ Invalid amount Fee exceeds balance Total amount exceeds balance + Are you sure you want to change the token? After changing, previous data will be reset. + Changing token Swap and send Send any token, and we’ll convert it on the way. Your recipient gets exactly what they need—seamlessly. Recipient will receive Will be sent a recipient Amount to receive + Are you sure you want to cancel the conversion? After changing, previous data will be reset. + Confirm cancelation Send with swap Transaction sent Prepare to scan card or ring you want to set up. @@ -1497,6 +1507,8 @@ Transaction request Transaction request Unlimited Amount + Ensure that each pairing attempt uses a fresh and unique URI + URI already used Wallet connect Discard You have an interrupted backup. Do you want to resume? diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt index 78ea021b35..db7a6f0f00 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt @@ -12,6 +12,7 @@ internal class AlertsComponentV2( ) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent { override fun dismiss() { + messageUM.onDismissRequest() router.pop() } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt index 675c063840..a171a0f38a 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt @@ -115,8 +115,11 @@ internal class WcPairComponent( is Alert.Type.Verified -> WcAlertsFactory.createVerifiedDomainAlert(alertType.appName) is Alert.Type.UnknownDomain -> WcAlertsFactory.createUnknownDomainAlert(model::connectFromAlert) is Alert.Type.UnsafeDomain -> WcAlertsFactory.createUnsafeDomainAlert(model::connectFromAlert) - is Alert.Type.UnsupportedDApp -> WcAlertsFactory.createUnsupportedDomainAlert(alertType.appName) - is Alert.Type.UnsupportedNetwork -> WcAlertsFactory.createUnsupportedChainAlert(alertType.appName) + is Alert.Type.UnsupportedDApp -> + WcAlertsFactory.createUnsupportedDomainAlert(alertType.appName, model::errorAlertOnDismiss) + is Alert.Type.UnsupportedNetwork -> + WcAlertsFactory.createUnsupportedChainAlert(alertType.appName, model::errorAlertOnDismiss) + is Alert.Type.UriAlreadyUsed -> WcAlertsFactory.createUriAlreadyUsedAlert(model::errorAlertOnDismiss) } } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt index f92ac32008..c745515dc4 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt @@ -149,6 +149,10 @@ internal class WcPairModel @Inject constructor( connect() } + fun errorAlertOnDismiss() { + router.pop() + } + private fun onConnect(securityStatus: CheckDAppResult) { when (securityStatus) { CheckDAppResult.SAFE -> connect() @@ -193,16 +197,23 @@ internal class WcPairModel @Inject constructor( } private fun processError(error: WcPairError) { - when (error) { + val alert = when (error) { is WcPairError.UnsupportedDApp -> { WcAppInfoRoutes.Alert.Type.UnsupportedDApp(error.appName) } is WcPairError.UnsupportedBlockchains -> { WcAppInfoRoutes.Alert.Type.UnsupportedNetwork(error.appName) } - else -> null - }?.let { stackNavigation.pushNew(WcAppInfoRoutes.Alert(it)) } - ?: run { messageSender.send(ToastMessage(message = stringReference(error.message))) } + is WcPairError.UriAlreadyUsed -> { + WcAppInfoRoutes.Alert.Type.UriAlreadyUsed + } + else -> { + messageSender.send(ToastMessage(message = stringReference(error.message))) + router.pop() + null + } + } + alert?.let { stackNavigation.pushNew(WcAppInfoRoutes.Alert(it)) } } override fun onWalletSelected(userWalletId: UserWalletId) { diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt index 56215acac5..cf5b226353 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt @@ -34,6 +34,7 @@ internal sealed class WcAppInfoRoutes : TangemBottomSheetConfigContent, Route { data object UnsafeDomain : Type() data class UnsupportedDApp(val appName: String) : Type() data class UnsupportedNetwork(val appName: String) : Type() + data object UriAlreadyUsed : Type() } } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt index 192c86c846..686b3ff2a7 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt @@ -74,11 +74,11 @@ internal object WcAlertsFactory { } } - fun createUnsupportedDomainAlert(appName: String): MessageBottomSheetUMV2 { + fun createUnsupportedDomainAlert(appName: String, onDismiss: () -> Unit): MessageBottomSheetUMV2 { return messageBottomSheetUM { infoBlock { icon(R.drawable.ic_wallet_connect_24) { - type = MessageBottomSheetUMV2.Icon.Type.Informative + type = Type.Informative backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint } title = resourceReference(R.string.wc_alert_unsupported_dapps_title) @@ -86,16 +86,34 @@ internal object WcAlertsFactory { } primaryButton { text = resourceReference(R.string.common_got_it) - onClick { closeBs() } + onClick { onDismiss() } + } + onDismissRequest = onDismiss + } + } + + fun createUriAlreadyUsedAlert(onDismiss: () -> Unit): MessageBottomSheetUMV2 { + return messageBottomSheetUM { + infoBlock { + icon(R.drawable.ic_wallet_connect_24) { + type = Type.Informative + backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint + } + title = resourceReference(R.string.wc_uri_already_used_title) + body = resourceReference(R.string.wc_uri_already_used_description) + } + primaryButton { + text = resourceReference(R.string.common_got_it) + onClick { onDismiss() } } } } - fun createUnsupportedChainAlert(appName: String): MessageBottomSheetUMV2 { + fun createUnsupportedChainAlert(appName: String, onDismiss: () -> Unit): MessageBottomSheetUMV2 { return messageBottomSheetUM { infoBlock { icon(R.drawable.ic_network_new_24) { - type = MessageBottomSheetUMV2.Icon.Type.Informative + type = Type.Informative backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint } title = resourceReference(R.string.wc_alert_unsupported_networks_title) @@ -103,8 +121,9 @@ internal object WcAlertsFactory { } primaryButton { text = resourceReference(R.string.common_got_it) - onClick { closeBs() } + onClick { onDismiss() } } + onDismissRequest = onDismiss } } @@ -128,6 +147,7 @@ internal object WcAlertsFactory { text = resourceReference(R.string.balance_hidden_got_it_button) onClick { onDismiss() } } + onDismissRequest = onDismiss } } From 4f499f41c7fd3f8d1d9ec87fd2e3fa2a750898dc Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 13:43:28 +0700 Subject: [PATCH 03/17] Updated on 2026-08-14 --- .../network/ethereum/WcEthNetwork.kt | 12 ---- .../ethereum/WcEthSendTransactionUseCase.kt | 15 +++-- .../ethereum/WcEthSignTransactionUseCase.kt | 15 +++-- .../network/solana/WcSolanaNetwork.kt | 25 ++------ .../pair/AssociateNetworksDelegate.kt | 8 ++- .../pair/CaipNamespaceDelegate.kt | 29 ++++++--- .../pair/DefaultWcPairUseCase.kt | 8 ++- .../walletconnect/pair/WcPairSdkDelegate.kt | 2 +- .../sessions/DefaultWcSessionsManager.kt | 18 ++++-- .../utils/WcNamespaceConverter.kt | 1 - .../usecase/method/WcTransactionUseCase.kt | 2 +- .../WcCommonTransactionUMConverter.kt | 62 ------------------- .../converter/WcSendTransactionUMConverter.kt | 30 ++++----- .../converter/WcSignTransactionUMConverter.kt | 13 ++-- .../converter/WcSignTypedDataUMConverter.kt | 13 ++-- .../model/WcSendTransactionModel.kt | 33 ++++++---- .../model/WcSignTransactionModel.kt | 52 +++++++++++----- 17 files changed, 159 insertions(+), 179 deletions(-) delete mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcCommonTransactionUMConverter.kt diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt index 7b7823fd7f..0f9e21543f 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt @@ -7,7 +7,6 @@ import arrow.core.right import com.squareup.moshi.Moshi import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.ExcludedBlockchains -import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.data.walletconnect.model.CAIP2 import com.tangem.data.walletconnect.model.NamespaceKey import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter @@ -15,7 +14,6 @@ import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter.Compani import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.data.walletconnect.utils.WcNetworksConverter -import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.walletconnect.model.* import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest @@ -156,16 +154,6 @@ internal class WcEthNetwork( val ethChainId = chainId.reference.toIntOrNull() ?: return null return Blockchain.fromChainId(ethChainId) } - - override fun toCAIP2(network: Network): CAIP2? { - val blockchain = network.toBlockchain() - if (!blockchain.isEvm()) return null - val chainId = blockchain.getChainId() ?: return null - return CAIP2( - namespace = namespaceKey.key, - reference = chainId.toString(), - ) - } } internal class Factories @Inject constructor( diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt index 0621f51855..5358c3a317 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt @@ -41,6 +41,9 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor( WcMutableFee { private var approvalAmount: WcApprovedAmount? = null + // in case of change TransactionExtras + // change approvalAmount for example + private var isIgnoreDAppFee: Boolean = false private var dAppFee: Fee? = null override val securityStatus: LceFlow = @@ -102,7 +105,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor( }, ) approvalAmount = action.amount - dAppFee = null + isIgnoreDAppFee = true uncompiled.copy(extras = extras.copy(callData = callData)) } is WcEthTxAction.UpdateFee -> uncompiled.copy(fee = action.fee) @@ -111,8 +114,11 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor( emit(newState) } - override fun dAppFee(): Fee? { - return dAppFee + override suspend fun dAppFee(): Fee? { + if (isIgnoreDAppFee) return null + if (dAppFee != null) return dAppFee + return ethTxHelper.getDAppFee(method.transaction, wallet, network) + .also { dAppFee = it } } override fun updateFee(fee: Fee) { @@ -120,9 +126,8 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor( } override fun invoke(): Flow> = flow { - dAppFee = ethTxHelper.getDAppFee(method.transaction, wallet, network) val transactionData = ethTxHelper.createTransactionData( - dAppFee = dAppFee, + dAppFee = dAppFee(), network = context.network, txParams = method.transaction, ) ?: return@flow diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt index eda9598750..b6795bf29b 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt @@ -41,6 +41,9 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor( WcMutableFee { private var approvalAmount: WcApprovedAmount? = null + // in case of change TransactionExtras + // change approvalAmount for example + private var isIgnoreDAppFee: Boolean = false private var dAppFee: Fee? = null override val securityStatus = blockAidDelegate.getSecurityStatus( @@ -101,7 +104,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor( }, ) approvalAmount = action.amount - dAppFee = null + isIgnoreDAppFee = true uncompiled.copy(extras = extras.copy(callData = callData)) } is WcEthTxAction.UpdateFee -> uncompiled.copy(fee = action.fee) @@ -115,17 +118,19 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor( } override fun invoke(): Flow> = flow { - dAppFee = ethTxHelper.getDAppFee(method.transaction, wallet, network) val transactionData = ethTxHelper.createTransactionData( - dAppFee = dAppFee, + dAppFee = dAppFee(), network = context.network, txParams = method.transaction, ) ?: return@flow emitAll(delegate.invoke(transactionData)) } - override fun dAppFee(): Fee? { - return dAppFee + override suspend fun dAppFee(): Fee? { + if (isIgnoreDAppFee) return null + if (dAppFee != null) return dAppFee + return ethTxHelper.getDAppFee(method.transaction, wallet, network) + .also { dAppFee = it } } override fun getAmount(): WcApprovedAmount? { diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt index 8fa3aa5469..0b47155291 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt @@ -8,7 +8,6 @@ import com.squareup.moshi.Moshi import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.extensions.decodeBase58 import com.tangem.blockchainsdk.utils.ExcludedBlockchains -import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.common.extensions.toHexString import com.tangem.data.walletconnect.model.CAIP2 import com.tangem.data.walletconnect.model.NamespaceKey @@ -17,7 +16,6 @@ import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter.Compani import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.data.walletconnect.utils.WcNetworksConverter -import com.tangem.domain.models.network.Network import com.tangem.domain.walletconnect.model.HandleMethodError import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.model.WcSolanaMethodName @@ -88,27 +86,14 @@ internal class WcSolanaNetwork( override val namespaceKey: NamespaceKey = NamespaceKey("solana") override fun toBlockchain(chainId: CAIP2): Blockchain? { + val isMainNet = MAINNET_CHAIN_ID.any { it.lowercase() == chainId.reference.lowercase() } if (chainId.namespace != namespaceKey.key) return null - return when (chainId.reference) { - MAINNET_CHAIN_ID -> Blockchain.Solana - TESTNET_CHAIN_ID -> Blockchain.SolanaTestnet + return when { + isMainNet -> Blockchain.Solana + chainId.reference.lowercase() == TESTNET_CHAIN_ID -> Blockchain.SolanaTestnet else -> null } } - - override fun toCAIP2(network: Network): CAIP2? { - val blockchain = network.toBlockchain() - val chainId = when (blockchain) { - Blockchain.Solana -> MAINNET_CHAIN_ID - Blockchain.SolanaTestnet -> TESTNET_CHAIN_ID - else -> null - } - chainId ?: return null - return CAIP2( - namespace = namespaceKey.key, - reference = chainId, - ) - } } private fun WcSolanaMethodName.toMethod(request: WcSdkSessionRequest): Either { @@ -140,7 +125,7 @@ internal class WcSolanaNetwork( ) companion object { - private const val MAINNET_CHAIN_ID = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" + private val MAINNET_CHAIN_ID = listOf("5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ") private const val TESTNET_CHAIN_ID = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z" } } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt index 7ecdf54de9..d0f06c74b2 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt @@ -110,8 +110,10 @@ internal class AssociateNetworksDelegate( .distinctBy { it.rawId } } - private fun Map.setOfChainId(): Set = - this.values.flatMap { proposal -> proposal.chains ?: listOf() }.toSet() - private fun missingNetworkName(chainId: String): String = chainId.replaceFirstChar(Char::titlecase) + + companion object { + internal fun Map.setOfChainId(): Set = + this.values.flatMap { proposal -> proposal.chains ?: listOf() }.toSet() + } } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt index 82c921611a..4b5d2c336a 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt @@ -2,6 +2,8 @@ package com.tangem.data.walletconnect.pair import com.reown.walletkit.client.Wallet import com.tangem.data.walletconnect.model.CAIP10 +import com.tangem.data.walletconnect.model.CAIP2 +import com.tangem.data.walletconnect.pair.AssociateNetworksDelegate.Companion.setOfChainId import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.data.walletconnect.utils.WcNetworksConverter import com.tangem.domain.models.network.Network @@ -22,6 +24,26 @@ internal class CaipNamespaceDelegate( val userWallet = sessionForApprove.wallet val result = mutableMapOf() + val requiredNamespaces = sessionProposal.requiredNamespaces.setOfChainId() + val optionalNamespaces = sessionProposal.optionalNamespaces.setOfChainId() + val allWcNetworks = (requiredNamespaces + optionalNamespaces) + .mapNotNull { chainId -> + val network = namespaceConverters + .firstNotNullOfOrNull { it.toNetwork(chainId, userWallet) } + ?: return@mapNotNull null + val caip2 = CAIP2.fromRaw(chainId) ?: return@mapNotNull null + network to caip2 + } + + suspend fun createCAIP10(userWalletId: UserWalletId, network: Network): CAIP10? { + val address = walletManagersFacade.getDefaultAddress(userWalletId, network) + val chainId = allWcNetworks + .find { (wcNetwork, _) -> network.rawId == wcNetwork.rawId } + ?.second + if (chainId == null || address == null) return null + return CAIP10(chainId = chainId, accountAddress = address) + } + wcNetworksConverter.convertNetworksForApprove(sessionForApprove) .mapNotNull { createCAIP10(userWallet.walletId, it) } .forEach { account -> @@ -52,13 +74,6 @@ internal class CaipNamespaceDelegate( } } - private suspend fun createCAIP10(userWalletId: UserWalletId, network: Network): CAIP10? { - val address = walletManagersFacade.getDefaultAddress(userWalletId, network) - val chainId = namespaceConverters.firstNotNullOfOrNull { it.toCAIP2(network) } - if (chainId == null || address == null) return null - return CAIP10(chainId = chainId, accountAddress = address) - } - private data class Session( val chains: MutableSet = mutableSetOf(), val accounts: MutableSet = mutableSetOf(), diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt index 91951d8f48..b12a31a1c0 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt @@ -115,6 +115,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( }.onCompletion { if (it != null) { Timber.tag(WC_TAG).e(it, "Completed with error $pairRequest") + emit(WcPairState.Error(WcPairError.Unknown(it.message.orEmpty()))) } else { Timber.tag(WC_TAG).i("Completed successfully $pairRequest") } @@ -134,7 +135,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( private suspend fun walletKitApproveSession( sessionForApprove: WcSessionApprove, sdkSessionProposal: Wallet.Model.SessionProposal, - ): Either { + ): Either = try { val namespaces = caipNamespaceDelegate.associate( sdkSessionProposal, sessionForApprove, @@ -143,7 +144,10 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( proposerPublicKey = sdkSessionProposal.proposerPublicKey, namespaces = namespaces, ) - return sdkDelegate.approve(sessionApprove) + sdkDelegate.approve(sessionApprove) + } catch (e: Throwable) { + Timber.tag(WC_TAG).e(e, "Failed to sdk approve session $pairRequest") + WcPairError.ApprovalFailed(e.message.orEmpty()).left() } private suspend fun buildProposalState( diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt index e115bb7807..e9665c0673 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt @@ -150,7 +150,7 @@ internal class WcPairSdkDelegate : WcSdkObserver { private fun Throwable.toApproveError() = WcPairError.ApprovalFailed(this.localizedMessage.orEmpty()).left() companion object { - private const val CALLBACK_TIMEOUT = 30 + private const val CALLBACK_TIMEOUT = 60 // com.reown.android.pairing.engine.domain.PairingEngine.pair private val pairingExpiredMessages = listOf( "Pairing URI expired", diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt index 614d12dc9c..38ed978e2d 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt @@ -52,7 +52,7 @@ internal class DefaultWcSessionsManager( if (someMigrated) return@transform // ignore emit, wait next one } val associatedSessions: List = associate(inSdk, inStore, wallets) - val someRemove = removeUnknownSessions(inStore, associatedSessions) + val someRemove = removeUnknownSessions(inStore, inSdk, associatedSessions) if (someRemove) return@transform // ignore emit, wait next one emit(associatedSessions.groupBy { it.wallet }) } @@ -79,7 +79,7 @@ internal class DefaultWcSessionsManager( override suspend fun removeSession(session: WcSession): Either { val topic = session.sdkModel.topic val sdkCall = sdkDisconnectSession(topic) - .onRight { onSessionDelete.trySend(Wallet.Model.SessionDelete.Success(topic = topic, reason = "")) } + onSessionDelete.trySend(Wallet.Model.SessionDelete.Success(topic = topic, reason = "")) analytics.send(WcAnalyticEvents.SessionDisconnected(session.sdkModel.appMetaData)) return sdkCall } @@ -142,10 +142,17 @@ internal class DefaultWcSessionsManager( return wcSessions } - private suspend fun removeUnknownSessions(storeSessions: Set, wcSessions: List): Boolean { + private suspend fun removeUnknownSessions( + storeSessions: Set, + inSdkSessions: List, + wcSessions: List, + ): Boolean { val unknownStoredSessions = storeSessions .filterNot { dto -> wcSessions.any { it.sdkModel.topic == dto.topic } } val haveSomeUnknown = unknownStoredSessions.isNotEmpty() + val unknownSdkSessions = inSdkSessions + .filterNot { sdkSession -> wcSessions.any { it.sdkModel.topic == sdkSession.topic } } + val haveSomeUnknownSdkSessions = unknownSdkSessions.isNotEmpty() if (haveSomeUnknown) { Timber.tag(WC_TAG).i("removeUnknownSessions $unknownStoredSessions") @@ -164,7 +171,10 @@ internal class DefaultWcSessionsManager( store.removeSessions(emptyNetworksDto) } if (haveEmptySessions) { - emptyNetworkSessions.map { scope.launch { sdkDisconnectSession(it.sdkModel.topic) } } + emptyNetworkSessions.forEach { scope.launch { sdkDisconnectSession(it.sdkModel.topic) } } + } + if (haveSomeUnknownSdkSessions) { + unknownSdkSessions.forEach { scope.launch { sdkDisconnectSession(it.topic) } } } return haveSomeUnknown || haveEmptyDto } diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcNamespaceConverter.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcNamespaceConverter.kt index 5d12294eed..a65c47d3df 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcNamespaceConverter.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcNamespaceConverter.kt @@ -16,7 +16,6 @@ internal interface WcNamespaceConverter { fun toBlockchain(chainId: CAIP2): Blockchain? fun toBlockchain(chainId: String): Blockchain? = toCAIP2(chainId)?.let { caip2 -> toBlockchain(caip2) } - fun toCAIP2(network: Network): CAIP2? fun toCAIP2(chainId: String): CAIP2? = CAIP2.fromRaw(chainId) fun toNetwork(chainId: String, wallet: UserWallet): Network? { diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcTransactionUseCase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcTransactionUseCase.kt index acc4686c4d..af7e9c585f 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcTransactionUseCase.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcTransactionUseCase.kt @@ -34,7 +34,7 @@ interface WcListTransactionUseCase : * [updateFee] triggered a new [TransactionData] emit */ interface WcMutableFee { - fun dAppFee(): Fee? + suspend fun dAppFee(): Fee? fun updateFee(fee: Fee) } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcCommonTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcCommonTransactionUMConverter.kt deleted file mode 100644 index 84d4f29ebc..0000000000 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcCommonTransactionUMConverter.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.tangem.features.walletconnect.transaction.converter - -import com.tangem.domain.walletconnect.model.WcEthMethod -import com.tangem.domain.walletconnect.model.WcSolanaMethod -import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase -import com.tangem.domain.walletconnect.usecase.method.WcSignState -import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase -import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase -import com.tangem.features.send.v2.api.entity.FeeSelectorUM -import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM -import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM -import com.tangem.utils.converter.Converter -import javax.inject.Inject - -internal class WcCommonTransactionUMConverter @Inject constructor( - private val signTypedDataUMConverter: WcSignTypedDataUMConverter, - private val signTransactionUMConverter: WcSignTransactionUMConverter, - private val sendTransactionUMConverter: WcSendTransactionUMConverter, -) : Converter { - - override fun convert(value: Input): WcCommonTransactionUM? { - return when (value.useCase) { - is WcMessageSignUseCase -> { - when (value.useCase.method) { - is WcEthMethod.SignTypedData -> signTypedDataUMConverter.convert( - WcSignTypedDataUMConverter.Input( - useCase = value.useCase, - signState = value.signState, - signModel = value.signState.signModel as WcMessageSignUseCase.SignModel, - actions = value.actions, - ), - ) - is WcEthMethod.MessageSign, is WcSolanaMethod.SignMessage -> signTransactionUMConverter.convert( - WcSignTransactionUMConverter.Input( - useCase = value.useCase, - signState = value.signState, - signModel = value.signState.signModel as WcMessageSignUseCase.SignModel, - actions = value.actions, - ), - ) - else -> null - } - } - is WcTransactionUseCase -> sendTransactionUMConverter.convert( - WcSendTransactionUMConverter.Input( - useCase = value.useCase, - signState = value.signState, - actions = value.actions, - feeSelectorUM = value.feeSelectorUM, - ), - ) - else -> null - } - } - - data class Input( - val useCase: WcSignUseCase<*>, - val signState: WcSignState<*>, - val actions: WcTransactionActionsUM, - val feeSelectorUM: FeeSelectorUM? = null, - ) -} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt index 9a2275f2d4..a41cf14c7d 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt @@ -2,7 +2,9 @@ package com.tangem.features.walletconnect.transaction.converter import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.model.WcSolanaMethod -import com.tangem.domain.walletconnect.usecase.method.* +import com.tangem.domain.walletconnect.usecase.method.WcMethodContext +import com.tangem.domain.walletconnect.usecase.method.WcSignState +import com.tangem.domain.walletconnect.usecase.method.WcSignStep import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM @@ -20,7 +22,7 @@ internal class WcSendTransactionUMConverter @Inject constructor( private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter, ) : Converter { - override fun convert(value: Input): WcSendTransactionUM? = when (value.useCase.method) { + override fun convert(value: Input): WcSendTransactionUM? = when (value.context.method) { is WcEthMethod.SendTransaction, is WcEthMethod.SignTransaction, is WcSolanaMethod.SignAllTransaction, @@ -31,23 +33,23 @@ internal class WcSendTransactionUMConverter @Inject constructor( onSend = value.actions.onSign, appInfo = appInfoContentUMConverter.convert( WcTransactionAppInfoContentUMConverter.Input( - session = value.useCase.session, + session = value.context.session, onShowVerifiedAlert = value.actions.onShowVerifiedAlert, ), ), - feeState = constructFeeState(useCase = value.useCase, actions = value.actions), - walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo }, - networkInfo = networkInfoUMConverter.convert(value.useCase.network), + feeState = value.feeState, + walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo }, + networkInfo = networkInfoUMConverter.convert(value.context.network), estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(), isLoading = value.signState.domainStep == WcSignStep.Signing, - address = WcAddressConverter.convert(value.useCase.derivationState), + address = WcAddressConverter.convert(value.context.derivationState), ), feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading, transactionRequestInfo = WcTransactionRequestInfoUM( blocks = buildList { addAll( requestBlockUMConverter.convert( - WcTransactionRequestBlockUMConverter.Input(value.useCase.rawSdkRequest), + WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest), ), ) }.toImmutableList(), @@ -57,17 +59,9 @@ internal class WcSendTransactionUMConverter @Inject constructor( else -> null } - private fun constructFeeState( - useCase: WcTransactionUseCase, - actions: WcTransactionActionsUM, - ): WcTransactionFeeState { - val mutableFee = useCase as? WcMutableFee ?: return WcTransactionFeeState.None - val dAppFee = mutableFee.dAppFee() - return WcTransactionFeeState.Success(dAppFee = dAppFee, onClick = actions.onShowFeeBottomSheet) - } - data class Input( - val useCase: WcTransactionUseCase, + val context: WcMethodContext, + val feeState: WcTransactionFeeState, val signState: WcSignState<*>, val actions: WcTransactionActionsUM, val feeSelectorUM: FeeSelectorUM?, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt index a1e30fdfad..28368845c9 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt @@ -1,6 +1,7 @@ package com.tangem.features.walletconnect.transaction.converter import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase +import com.tangem.domain.walletconnect.usecase.method.WcMethodContext import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcSignStep import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM @@ -23,25 +24,25 @@ internal class WcSignTransactionUMConverter @Inject constructor( onSign = value.actions.onSign, appInfo = appInfoContentUMConverter.convert( WcTransactionAppInfoContentUMConverter.Input( - session = value.useCase.session, + session = value.context.session, onShowVerifiedAlert = value.actions.onShowVerifiedAlert, ), ), - walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo }, - networkInfo = networkInfoUMConverter.convert(value.useCase.network), + walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo }, + networkInfo = networkInfoUMConverter.convert(value.context.network), isLoading = value.signState.domainStep == WcSignStep.Signing, - address = WcAddressConverter.convert(value.useCase.derivationState), + address = WcAddressConverter.convert(value.context.derivationState), ), transactionRequestInfo = WcTransactionRequestInfoUM( requestBlockUMConverter.convert( - WcTransactionRequestBlockUMConverter.Input(value.useCase.rawSdkRequest, value.signModel), + WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest, value.signModel), ).toImmutableList(), onCopy = value.actions.onCopy, ), ) data class Input( - val useCase: WcMessageSignUseCase, + val context: WcMethodContext, val signState: WcSignState<*>, val signModel: WcMessageSignUseCase.SignModel, val actions: WcTransactionActionsUM, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt index df8661fc77..faef1ed164 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt @@ -1,6 +1,7 @@ package com.tangem.features.walletconnect.transaction.converter import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase +import com.tangem.domain.walletconnect.usecase.method.WcMethodContext import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcSignStep import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM @@ -23,20 +24,20 @@ internal class WcSignTypedDataUMConverter @Inject constructor( onSign = value.actions.onSign, appInfo = appInfoContentUMConverter.convert( WcTransactionAppInfoContentUMConverter.Input( - session = value.useCase.session, + session = value.context.session, onShowVerifiedAlert = value.actions.onShowVerifiedAlert, ), ), - walletName = value.useCase.session.wallet.name.takeIf { value.useCase.session.showWalletInfo }, - networkInfo = networkInfoUMConverter.convert(value.useCase.network), - address = WcAddressConverter.convert(value.useCase.derivationState), + walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo }, + networkInfo = networkInfoUMConverter.convert(value.context.network), + address = WcAddressConverter.convert(value.context.derivationState), isLoading = value.signState.domainStep == WcSignStep.Signing, ), transactionRequestInfo = WcTransactionRequestInfoUM( blocks = buildList { addAll( requestBlockUMConverter.convert( - WcTransactionRequestBlockUMConverter.Input(value.useCase.rawSdkRequest, value.signModel), + WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest, value.signModel), ), ) }.toImmutableList(), @@ -45,7 +46,7 @@ internal class WcSignTypedDataUMConverter @Inject constructor( ) data class Input( - val useCase: WcMessageSignUseCase, + val context: WcMethodContext, val signState: WcSignState<*>, val signModel: WcMessageSignUseCase.SignModel, val actions: WcTransactionActionsUM, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index 463c89fc5a..396be3dd59 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -36,11 +36,12 @@ import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorRelo import com.tangem.features.send.v2.api.subcomponents.feeSelector.entity.FeeSelectorData import com.tangem.features.walletconnect.connections.routing.WcInnerRoute import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams -import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter +import com.tangem.features.walletconnect.transaction.converter.WcSendTransactionUMConverter import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiveBlockAidUiConverter @@ -62,7 +63,7 @@ internal class WcSendTransactionModel @Inject constructor( private val router: Router, private val clipboardManager: ClipboardManager, private val useCaseFactory: WcRequestUseCaseFactory, - private val converter: WcCommonTransactionUMConverter, + private val converter: WcSendTransactionUMConverter, private val blockAidUiConverter: WcSendAndReceiveBlockAidUiConverter, private val getFeeUseCase: GetFeeUseCase, private val getNetworkCoinUseCase: GetNetworkCoinStatusUseCase, @@ -198,20 +199,28 @@ internal class WcSendTransactionModel @Inject constructor( is Lce.Error -> WcSendReceiveTransactionCheckResultsUM(isLoading = false) is Lce.Loading -> WcSendReceiveTransactionCheckResultsUM(isLoading = true) } + + val feeState = when { + useCase is WcMutableFee -> + WcTransactionFeeState + .Success(dAppFee = useCase.dAppFee(), onClick = { onShowFeeBottomSheet() }) + else -> WcTransactionFeeState.None + } + val actions = WcTransactionActionsUM( + onShowVerifiedAlert = ::showVerifiedAlert, + onDismiss = { cancel(useCase) }, + onSign = { onSign(securityCheck.getOrNull()) }, + onCopy = { copyData(useCase.rawSdkRequest.request.params) }, + ) var transactionUM = converter.convert( - WcCommonTransactionUMConverter.Input( - useCase = useCase, + WcSendTransactionUMConverter.Input( + context = useCase, + feeState = feeState, signState = signState, - actions = WcTransactionActionsUM( - onShowVerifiedAlert = ::showVerifiedAlert, - onDismiss = { cancel(useCase) }, - onSign = { onSign(securityCheck.getOrNull()) }, - onCopy = { copyData(useCase.rawSdkRequest.request.params) }, - onShowFeeBottomSheet = ::onShowFeeBottomSheet, - ), + actions = actions, feeSelectorUM = uiState.value?.feeSelectorUM, ), - ) as? WcSendTransactionUM + ) transactionUM = transactionUM?.copy( transaction = transactionUM.transaction.copy(estimatedWalletChanges = blockAidState), spendAllowance = blockAidState.spendAllowance, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt index 4738a1abaa..cdcb80f9f4 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt @@ -11,13 +11,16 @@ import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.domain.walletconnect.WcRequestUseCaseFactory +import com.tangem.domain.walletconnect.model.WcEthMethod +import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcSignStep import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams -import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter +import com.tangem.features.walletconnect.transaction.converter.WcSignTransactionUMConverter +import com.tangem.features.walletconnect.transaction.converter.WcSignTypedDataUMConverter import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM @@ -40,7 +43,8 @@ internal class WcSignTransactionModel @Inject constructor( private val router: Router, private val clipboardManager: ClipboardManager, private val useCaseFactory: WcRequestUseCaseFactory, - private val converter: WcCommonTransactionUMConverter, + private val signTypedDataUMConverter: WcSignTypedDataUMConverter, + private val signTransactionUMConverter: WcSignTransactionUMConverter, ) : Model(), WcCommonTransactionModel { private val params = paramsContainer.require() @@ -58,24 +62,44 @@ internal class WcSignTransactionModel @Inject constructor( useCase.invoke() .onEach { signState -> if (signingIsDone(signState)) return@onEach - val signTransactionUM = converter.convert( - WcCommonTransactionUMConverter.Input( - useCase = useCase, - signState = signState, - actions = WcTransactionActionsUM( - onShowVerifiedAlert = ::showVerifiedAlert, - onDismiss = { cancel(useCase) }, - onSign = useCase::sign, - onCopy = { copyData(useCase.rawSdkRequest.request.params) }, - ), - ), - ) as? WcSignTransactionUM + val signTransactionUM = convertToUI(useCase, signState) _uiState.emit(signTransactionUM) } .launchIn(this) } } + private fun convertToUI( + useCase: WcMessageSignUseCase, + signState: WcSignState, + ): WcSignTransactionUM? { + val actions = WcTransactionActionsUM( + onShowVerifiedAlert = ::showVerifiedAlert, + onDismiss = { cancel(useCase) }, + onSign = useCase::sign, + onCopy = { copyData(useCase.rawSdkRequest.request.params) }, + ) + return when (useCase.method) { + is WcEthMethod.SignTypedData -> signTypedDataUMConverter.convert( + WcSignTypedDataUMConverter.Input( + context = useCase, + signState = signState, + signModel = signState.signModel, + actions = actions, + ), + ) + is WcEthMethod.MessageSign, is WcSolanaMethod.SignMessage -> signTransactionUMConverter.convert( + WcSignTransactionUMConverter.Input( + context = useCase, + signState = signState, + signModel = signState.signModel, + actions = actions, + ), + ) + else -> null + } + } + override fun dismiss() { _uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop() } From 2a858f8de0bfe629c72855520bf188219d9a71f1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 11:50:44 +0500 Subject: [PATCH 04/17] Updated on 2026-08-14 --- .../ui/components/tooltip/TangemTooltip.kt | 26 ++++++-- .../connections/ui/WcAppInfoBS.kt | 44 +++++++------ .../converter/WcAddressConverter.kt | 16 +---- .../transaction/entity/common/WcAddressUM.kt | 3 - .../entity/send/WcSendTransactionUM.kt | 3 +- .../entity/sign/WcSignTransactionUM.kt | 3 +- .../transaction/ui/common/WcAddressItem.kt | 28 +++++---- .../transaction/ui/common/WcNetworkItem.kt | 62 +++++++++++-------- .../ui/common/WcSendTransactionItems.kt | 3 +- .../transaction/ui/common/WcWalletItem.kt | 34 ++++++---- .../send/WcSendTransactionModalBottomSheet.kt | 7 +-- ...cSignTransactionModalBottomSheetContent.kt | 3 +- 12 files changed, 131 insertions(+), 101 deletions(-) delete mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcAddressUM.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt index b436ca3287..395b09aed0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt @@ -22,9 +22,15 @@ import com.tangem.core.ui.res.TangemThemePreview import kotlinx.coroutines.launch @Composable -fun TangemTooltip(text: String, content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier) { +fun TangemTooltip( + text: String, + content: @Composable (Modifier) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { InternalTangemTooltip( modifier = modifier, + enabled = enabled, tooltipContent = { Text( modifier = Modifier.background(TangemTheme.colors.icon.secondary), @@ -38,9 +44,15 @@ fun TangemTooltip(text: String, content: @Composable (Modifier) -> Unit, modifie } @Composable -fun TangemTooltip(text: AnnotatedString, content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier) { +fun TangemTooltip( + text: AnnotatedString, + content: @Composable (Modifier) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { InternalTangemTooltip( modifier = modifier, + enabled = enabled, tooltipContent = { Text( modifier = Modifier.background(TangemTheme.colors.icon.secondary), @@ -59,6 +71,7 @@ private fun InternalTangemTooltip( tooltipContent: @Composable () -> Unit, content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier, + enabled: Boolean = true, ) { val tooltipState = rememberTooltipState(isPersistent = true) val coroutineScope = rememberCoroutineScope() @@ -78,11 +91,14 @@ private fun InternalTangemTooltip( ) }, content = { - content( + val contentModifier = if (enabled) { Modifier.clickableSingle( onClick = { coroutineScope.launch { tooltipState.show() } }, - ), - ) + ) + } else { + Modifier + } + content(contentModifier) }, ) } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt index 9804255452..af9a95cdf2 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -311,23 +312,28 @@ private fun WalletRowItem(walletName: String, showEndIcon: Boolean, modifier: Mo contentDescription = null, tint = TangemTheme.colors.icon.accent, ) - Text( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing4) - .weight(1f), - text = stringResourceSafe(R.string.manage_tokens_network_selector_wallet), - style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.primary1, - ) - Text( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing16) - .weight(1f), - text = walletName, - textAlign = TextAlign.End, - style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.tertiary, - ) + Row( + modifier = Modifier.weight(1f), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), + text = stringResourceSafe(R.string.manage_tokens_network_selector_wallet), + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.primary1, + maxLines = 1, + ) + Text( + modifier = Modifier.padding(start = TangemTheme.dimens.spacing16), + text = walletName, + textAlign = TextAlign.End, + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } if (showEndIcon) { Icon( modifier = Modifier @@ -611,7 +617,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider { -internal object WcAddressConverter : Converter { - - override fun convert(value: WcNetworkDerivationState): WcAddressUM? { + override fun convert(value: WcNetworkDerivationState): String? { return when (value) { is WcNetworkDerivationState.Single -> null - is WcNetworkDerivationState.Multiple -> WcAddressUM( - fullAddress = value.walletAddress, - shortAddress = value.walletAddress.toShortAddressText(), - ) + is WcNetworkDerivationState.Multiple -> value.walletAddress } } - - private fun String.toShortAddressText() = - "${take(ADDRESS_FIRST_PART_LENGTH)}...${takeLast(ADDRESS_SECOND_PART_LENGTH)}" } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcAddressUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcAddressUM.kt deleted file mode 100644 index a43b29458a..0000000000 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcAddressUM.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.tangem.features.walletconnect.transaction.entity.common - -data class WcAddressUM(val fullAddress: String, val shortAddress: String) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt index b1461cbfd5..616da6cfc5 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt @@ -4,7 +4,6 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.features.send.v2.api.entity.FeeSelectorUM 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.WcAddressUM import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM @@ -26,6 +25,6 @@ internal data class WcSendTransactionItemUM( val estimatedWalletChanges: WcSendReceiveTransactionCheckResultsUM?, val walletName: String?, val networkInfo: WcNetworkInfoUM, - val address: WcAddressUM?, + val address: String?, val isLoading: Boolean = false, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt index fd69bf9cb7..54c08cf874 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt @@ -1,7 +1,6 @@ package com.tangem.features.walletconnect.transaction.entity.sign import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent -import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM @@ -18,6 +17,6 @@ internal data class WcSignTransactionItemUM( val appInfo: WcTransactionAppInfoContentUM, val walletName: String?, val networkInfo: WcNetworkInfoUM, - val address: WcAddressUM?, + val address: String?, val isLoading: Boolean = false, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcAddressItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcAddressItem.kt index f14eb7984b..35e9a28c76 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcAddressItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcAddressItem.kt @@ -5,20 +5,23 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.atoms.text.EllipsisText +import com.tangem.core.ui.components.atoms.text.TextEllipsis import com.tangem.core.ui.components.tooltip.TangemTooltip import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.features.walletconnect.impl.R -import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM @Composable -internal fun WcAddressItem(address: WcAddressUM, modifier: Modifier = Modifier) { +internal fun WcAddressItem(address: String, modifier: Modifier = Modifier) { Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { + var isTooltipEnabled by remember { mutableStateOf(false) } Icon( modifier = Modifier.size(TangemTheme.dimens.size24), painter = painterResource(R.drawable.ic_user_square_24), @@ -26,25 +29,26 @@ internal fun WcAddressItem(address: WcAddressUM, modifier: Modifier = Modifier) tint = TangemTheme.colors.icon.accent, ) Text( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing8) - .weight(1f), + modifier = Modifier.padding(start = TangemTheme.dimens.spacing8), text = stringResourceSafe(R.string.wc_common_address), style = TangemTheme.typography.body1, color = TangemTheme.colors.text.primary1, + maxLines = 1, ) + SpacerWMax() TangemTooltip( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing16) - .weight(1f), - text = address.fullAddress, + modifier = Modifier.padding(start = TangemTheme.dimens.spacing16), + text = address, + enabled = isTooltipEnabled, content = { contentModifier -> - Text( + EllipsisText( + onTextLayout = { isTooltipEnabled = !it.hasVisualOverflow }, modifier = contentModifier, - text = address.shortAddress, + text = address, textAlign = TextAlign.End, style = TangemTheme.typography.body1, color = TangemTheme.colors.text.tertiary, + ellipsis = TextEllipsis.Middle, ) }, ) diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt index fe95a18f69..bc1a75f3fa 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt @@ -1,16 +1,21 @@ package com.tangem.features.walletconnect.transaction.ui.common import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.Text -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 import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.core.ui.components.tooltip.TangemTooltip import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.features.walletconnect.impl.R @@ -22,39 +27,46 @@ internal fun WcNetworkItem(networkInfo: WcNetworkInfoUM, modifier: Modifier = Mo modifier = modifier, verticalAlignment = Alignment.CenterVertically, ) { + var isTooltipEnabled by remember { mutableStateOf(false) } Icon( modifier = Modifier.size(TangemTheme.dimens.size24), painter = painterResource(R.drawable.ic_network_new_24), contentDescription = null, tint = TangemTheme.colors.icon.accent, ) - Text( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing8) - .weight(1f), - text = stringResourceSafe(R.string.wc_common_network), - style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.primary1, - ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.End, - modifier = Modifier.weight(1f), - ) { + Row(modifier = Modifier.weight(1f), horizontalArrangement = Arrangement.SpaceBetween) { Text( - text = networkInfo.name, + modifier = Modifier.padding(start = TangemTheme.dimens.spacing8), + text = stringResourceSafe(R.string.wc_common_network), style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.tertiary, + color = TangemTheme.colors.text.primary1, + maxLines = 1, ) - Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing6)) - Image( - painter = painterResource(id = networkInfo.iconRes), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .clip(CircleShape) - .size(TangemTheme.dimens.size20), + TangemTooltip( + modifier = Modifier.padding(start = TangemTheme.dimens.spacing16), + text = networkInfo.name, + enabled = isTooltipEnabled, + content = { contentModifier -> + Text( + modifier = contentModifier, + text = networkInfo.name, + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + onTextLayout = { isTooltipEnabled = it.hasVisualOverflow }, + ) + }, ) } + Image( + painter = painterResource(id = networkInfo.iconRes), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing6) + .clip(CircleShape) + .size(TangemTheme.dimens.size20), + ) } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt index 6b703bf94d..b8cd6c5c85 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt @@ -15,7 +15,6 @@ import com.tangem.core.ui.components.divider.DividerWithPadding import com.tangem.core.ui.extensions.clickableSingle import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.v2.api.FeeSelectorBlockComponent -import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState @@ -25,7 +24,7 @@ internal fun WcSendTransactionItems( networkInfo: WcNetworkInfoUM, feeState: WcTransactionFeeState, feeSelectorBlockComponent: FeeSelectorBlockComponent?, - address: WcAddressUM?, + address: String?, modifier: Modifier = Modifier, ) { val onFeeBlockClicked = remember(feeState) { diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt index 18babacc67..3a416412ee 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt @@ -5,11 +5,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.tooltip.TangemTooltip import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.features.walletconnect.impl.R @@ -17,6 +19,7 @@ import com.tangem.features.walletconnect.impl.R @Composable internal fun WcWalletItem(walletName: String, modifier: Modifier = Modifier) { Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { + var isTooltipEnabled by remember { mutableStateOf(false) } Icon( modifier = Modifier.size(TangemTheme.dimens.size24), painter = painterResource(R.drawable.ic_wallet_new_24), @@ -24,21 +27,28 @@ internal fun WcWalletItem(walletName: String, modifier: Modifier = Modifier) { tint = TangemTheme.colors.icon.accent, ) Text( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing8) - .weight(1f), + modifier = Modifier.padding(start = TangemTheme.dimens.spacing8), text = stringResourceSafe(R.string.manage_tokens_network_selector_wallet), style = TangemTheme.typography.body1, color = TangemTheme.colors.text.primary1, + maxLines = 1, ) - Text( - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing16) - .weight(1f), + SpacerWMax() + TangemTooltip( + modifier = Modifier.padding(start = TangemTheme.dimens.spacing16), text = walletName, - textAlign = TextAlign.End, - style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.tertiary, + enabled = isTooltipEnabled, + content = { contentModifier -> + Text( + modifier = contentModifier, + text = walletName, + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + onTextLayout = { isTooltipEnabled = it.hasVisualOverflow }, + ) + }, ) } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt index 08a5ea45b0..e05ddf9fac 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt @@ -32,7 +32,6 @@ import com.tangem.features.walletconnect.transaction.components.PreviewFeeSelect 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 -import com.tangem.features.walletconnect.transaction.entity.common.WcAddressUM import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState @@ -194,8 +193,8 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide ), isLoading = false, ), - walletName = "Tangem 2.0", - networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), + walletName = "Tangem 2.0 Tangem 2.0 Tangem 2", + networkInfo = WcNetworkInfoUM(name = "Optimistic Ethereum Network", iconRes = R.drawable.img_eth_22), feeState = WcTransactionFeeState.Success(null, {}), address = null, ), @@ -232,7 +231,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide walletName = "Tangem 2.0", networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), feeState = WcTransactionFeeState.Success(null, {}), - address = WcAddressUM("0xdac17f958d2ee523a2206206994597c13d831ec7", "0x345FF...34FA"), + address = "0xdac17f958d2ee523a2206206994597c13d831ec7", ), WcSendTransactionItemUM( onDismiss = {}, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt index b6aba3ab28..86e7fad3f4 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt @@ -28,7 +28,6 @@ import com.tangem.core.ui.res.TangemThemePreview 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.entity.common.WcAddressUM import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM @@ -196,7 +195,7 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide ), walletName = null, networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), - address = WcAddressUM("0xdac17f958d2ee523a2206206994597c13d831ec7", "0x345FF...34FA"), + address = "0xdac17f958d2ee523a2206206994597c13d831ec7", ), ), ) \ No newline at end of file From 9e74a355b58f196cb08441a6fa9196cc85d2f45a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 14:21:09 +0500 Subject: [PATCH 05/17] Updated on 2026-08-14 --- .../converter/WcSendTransactionUMConverter.kt | 1 + .../transaction/entity/send/WcSendTransactionUM.kt | 1 + .../transaction/model/WcSendTransactionModel.kt | 14 ++++++++++---- .../ui/common/WcTransactionRequestButtons.kt | 2 ++ .../ui/send/WcSendTransactionModalBottomSheet.kt | 4 ++++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt index a41cf14c7d..c5f6ec3ed8 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt @@ -43,6 +43,7 @@ internal class WcSendTransactionUMConverter @Inject constructor( estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(), isLoading = value.signState.domainStep == WcSignStep.Signing, address = WcAddressConverter.convert(value.context.derivationState), + sendEnabled = value.feeSelectorUM is FeeSelectorUM.Content, ), feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading, transactionRequestInfo = WcTransactionRequestInfoUM( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt index 616da6cfc5..271f0cd292 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt @@ -26,5 +26,6 @@ internal data class WcSendTransactionItemUM( val walletName: String?, val networkInfo: WcNetworkInfoUM, val address: String?, + val sendEnabled: Boolean, val isLoading: Boolean = false, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index 396be3dd59..2455b74700 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -157,7 +157,12 @@ internal class WcSendTransactionModel @Inject constructor( * Also handles fee results from FeeSelectorBlockComponent */ fun updateFee(feeSelectorUM: FeeSelectorUM) { - _uiState.update { it?.copy(feeSelectorUM = feeSelectorUM) } + _uiState.update { + it?.copy( + feeSelectorUM = feeSelectorUM, + transaction = it.transaction.copy(sendEnabled = feeSelectorUM is FeeSelectorUM.Content), + ) + } val fee = (feeSelectorUM as? FeeSelectorUM.Content)?.selectedFeeItem?.fee ?: return (useCase as? WcMutableFee)?.updateFee(fee) } @@ -201,9 +206,10 @@ internal class WcSendTransactionModel @Inject constructor( } val feeState = when { - useCase is WcMutableFee -> - WcTransactionFeeState - .Success(dAppFee = useCase.dAppFee(), onClick = { onShowFeeBottomSheet() }) + useCase is WcMutableFee -> WcTransactionFeeState.Success( + dAppFee = useCase.dAppFee(), + onClick = ::onShowFeeBottomSheet, + ) else -> WcTransactionFeeState.None } val actions = WcTransactionActionsUM( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt index 9d8ec8d0fb..ed0217aee9 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt @@ -20,6 +20,7 @@ internal fun WcTransactionRequestButtons( onDismiss: () -> Unit, onClickActiveButton: () -> Unit, modifier: Modifier = Modifier, + enabled: Boolean = true, ) { Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { SecondaryButton( @@ -37,6 +38,7 @@ internal fun WcTransactionRequestButtons( onClick = onClickActiveButton, iconResId = R.drawable.ic_tangem_24, showProgress = isLoading, + enabled = enabled, ) } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt index e05ddf9fac..6d0df9a6db 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt @@ -118,6 +118,7 @@ internal fun WcSendTransactionModalBottomSheet( onClickActiveButton = state.onSend, activeButtonText = resourceReference(R.string.common_send), isLoading = state.isLoading, + enabled = state.sendEnabled, ) }, ) @@ -197,6 +198,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide networkInfo = WcNetworkInfoUM(name = "Optimistic Ethereum Network", iconRes = R.drawable.img_eth_22), feeState = WcTransactionFeeState.Success(null, {}), address = null, + sendEnabled = true, ), WcSendTransactionItemUM( onDismiss = {}, @@ -232,6 +234,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), feeState = WcTransactionFeeState.Success(null, {}), address = "0xdac17f958d2ee523a2206206994597c13d831ec7", + sendEnabled = true, ), WcSendTransactionItemUM( onDismiss = {}, @@ -267,6 +270,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), feeState = WcTransactionFeeState.None, address = null, + sendEnabled = false, ), ), ) \ No newline at end of file From eb5c2b604ed51dc58e8e1c055736334cba71de14 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 13:01:32 +0300 Subject: [PATCH 06/17] Updated on 2026-08-14 --- .../src/main/assets/configs/feature_toggles_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index d33f1e1360..6ecb2f5ac6 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -21,7 +21,7 @@ }, { "name": "WALLET_CONNECT_REDESIGN_ENABLED", - "version": "undefined" + "version": "5.27.0" }, { "name": "PUSH_NOTIFICATIONS_ENABLED", From 75a96e0d300cd3bb6b51bd28fa4e5bb2ced6e29f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 13:17:19 +0300 Subject: [PATCH 07/17] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index a4cff08e57..3ac868e93f 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit a4cff08e572fdd4e51d75a5e1a3deef14cfb1f0b +Subproject commit 3ac868e93f88498258867d457f8b8c4577b40f98 From bccf17effef9539644ff7e98fca762d7fd2675c1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 15:27:24 +0700 Subject: [PATCH 08/17] Updated on 2026-08-14 --- .../di/WalletConnectDataModule.kt | 8 +++-- .../request/DefaultWcRequestUseCaseFactory.kt | 22 +++++++++++++- .../sign/WcSignUseCaseDelegate.kt | 3 -- .../utils/WcSdkSessionRequestConverter.kt | 2 ++ .../WcSignUseCaseDelegateTest.kt | 7 +++++ .../model/sdkcopy/WcAppMetaData.kt | 3 ++ .../model/sdkcopy/WcSdkSessionRequest.kt | 1 + .../domain/walletconnect/WcAnalyticEvents.kt | 30 +++++++++++++------ 8 files changed, 61 insertions(+), 15 deletions(-) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt index 40593c27e5..0ee7dab28e 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt @@ -225,8 +225,12 @@ internal object WalletConnectDataModule { @Provides @Singleton - fun wcRequestUseCaseFactory(diHelperBox: DiHelperBox): WcRequestUseCaseFactory { - return DefaultWcRequestUseCaseFactory(diHelperBox.handlers) + fun wcRequestUseCaseFactory( + diHelperBox: DiHelperBox, + namespaceConverters: Set<@JvmSuppressWildcards WcNamespaceConverter>, + analytics: AnalyticsEventHandler, + ): WcRequestUseCaseFactory { + return DefaultWcRequestUseCaseFactory(diHelperBox.handlers, namespaceConverters, analytics) } @Provides diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt index 53ad5c917a..31791fd91c 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt @@ -3,10 +3,14 @@ package com.tangem.data.walletconnect.request import arrow.core.Either import arrow.core.left import arrow.core.right +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.data.walletconnect.utils.WC_TAG +import com.tangem.data.walletconnect.utils.WcNamespaceConverter +import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcRequestUseCaseFactory import com.tangem.domain.walletconnect.model.HandleMethodError import com.tangem.domain.walletconnect.model.WcMethod +import com.tangem.domain.walletconnect.model.WcRequestError.Companion.code import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase import timber.log.Timber @@ -14,6 +18,8 @@ import javax.inject.Inject internal class DefaultWcRequestUseCaseFactory @Inject constructor( private val requestConverters: Set, + private val namespaceConverters: Set, + private val analytics: AnalyticsEventHandler, ) : WcRequestUseCaseFactory { @Suppress("UNCHECKED_CAST") @@ -25,12 +31,26 @@ internal class DefaultWcRequestUseCaseFactory @Inject constructor( ?.toUseCase(request) ?: HandleMethodError.UnknownError("Failed to create WcUseCase").left() - return useCase.fold( + val result = useCase.fold( ifLeft = { Timber.tag(WC_TAG).e("$it") it.left() }, ifRight = { (it as? T)?.right() ?: HandleMethodError.Unsupported(WcMethod.Unsupported(request)).left() }, ) + result.onLeft { logError(it, request) } + return result + } + + private fun logError(error: HandleMethodError, request: WcSdkSessionRequest) { + val blockchainName = namespaceConverters + .firstNotNullOfOrNull { it.toBlockchain(request.chainId.orEmpty()) } + ?.getCoinName().orEmpty() + val event = WcAnalyticEvents.SignatureRequestReceivedFailed( + rawRequest = request, + errorCode = error.code().orEmpty(), + blockchain = blockchainName, + ) + analytics.send(event) } } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt index 2dea8ba415..af4f00d088 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt @@ -43,7 +43,6 @@ internal class WcSignUseCaseDelegate( val state = MutableStateFlow(WcSignState(initModel, WcSignStep.PreSign)) analytics.send( WcAnalyticEvents.SignatureRequestReceived( - session = context.session, rawRequest = context.rawSdkRequest, network = context.network, ), @@ -73,7 +72,6 @@ internal class WcSignUseCaseDelegate( val event = step.result.fold( ifLeft = { error -> WcAnalyticEvents.SignatureRequestFailed( - session = context.session, rawRequest = context.rawSdkRequest, network = context.network, errorCode = error.code() ?: error::class.simpleName.orEmpty(), @@ -81,7 +79,6 @@ internal class WcSignUseCaseDelegate( }, ifRight = { WcAnalyticEvents.SignatureRequestHandled( - session = context.session, rawRequest = context.rawSdkRequest, network = context.network, ) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcSdkSessionRequestConverter.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcSdkSessionRequestConverter.kt index 603699848e..3c0fd55daa 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcSdkSessionRequestConverter.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/WcSdkSessionRequestConverter.kt @@ -10,6 +10,8 @@ internal object WcSdkSessionRequestConverter : Converter Date: Tue, 29 Jul 2025 20:53:48 +0400 Subject: [PATCH 09/17] Updated on 2026-08-14 --- core/res/src/main/res/values-ja/strings.xml | 2 + core/res/src/main/res/values-ru/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 2 + .../components/notifications/Notification.kt | 13 +++--- .../tangem/data/blockaid/BlockAidMapper.kt | 2 + .../models/transaction/ValidationResult.kt | 5 +++ .../connections/utils/WcAlertsFactory.kt | 9 ++-- .../WcSendReceiveTransactionCheckResultsUM.kt | 14 ++++++- .../model/WcSendTransactionModel.kt | 28 ++++++++++--- .../transaction/routes/WcTransactionRoutes.kt | 8 +++- .../blockaid/TransactionCheckResultsItem.kt | 12 ++++-- .../WcSendAndReceiveBlockAidUiConverter.kt | 18 ++++++-- .../blockaid/WcTransactionCheckErrorItem.kt | 42 ++++++++++++++----- .../send/WcSendTransactionModalBottomSheet.kt | 19 ++++++--- 14 files changed, 135 insertions(+), 40 deletions(-) diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 543d824195..af6731c66e 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -1440,6 +1440,8 @@ 取引リクエスト 取引リクエスト 無制限 + 各ペアリング試行で、新しくユニークなURIが使用されていることを確認します + URIはすでに使用されています ウォレットコネクト 破棄 バックアップが中断されました。再開しますか? diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 71e9bf29d3..5a430645a7 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1184,6 +1184,7 @@ Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменивайте и стейкайте в один клик. Свяжите до трех карт для резервного копирования. Откройте Tangem Wallet Получайте уведомления о входящих транзакциях в кошельке и обновлениях Tangem. + В настоящий момент push-уведомления могут не работать на устройствах Huawei. Мы уже работаем над решением этой проблемы и планируем исправление в ближайших обновлениях. Спасибо за понимание! Уведомления о транзакциях Настройки кошелька Tangem diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 9afd794d7f..18834e5cbc 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1351,6 +1351,7 @@ Secret code to protect this wallet. Used for login and signatures. Set/Change Access Code Stay notified on wallet incoming transactions and Tangem updates. + 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! Transaction Notifications Wallet settings Tangem @@ -1510,6 +1511,7 @@ Ensure that each pairing attempt uses a fresh and unique URI URI already used Wallet connect + Suspicious transaction Discard You have an interrupted backup. Do you want to resume? Yes, resume diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index 10bfdbba1d..569384917c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -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, + ) + } } } 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 e245687538..857ec25986 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 @@ -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) { diff --git a/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/ValidationResult.kt b/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/ValidationResult.kt index 0adde02aa8..7f3e1a141c 100644 --- a/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/ValidationResult.kt +++ b/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/ValidationResult.kt @@ -15,6 +15,11 @@ enum class ValidationResult { */ UNSAFE, + /** + * Transaction was confirmed suspicious + */ + WARNING, + /** * Validation wasn't performed, BlockAid cannot guarantee transaction's safety */ diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt index 686b3ff2a7..587d3936b5 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt @@ -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()) { 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 index 096af30724..57b9c03f54 100644 --- 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 @@ -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, -) \ No newline at end of file +) + +internal data class BlockAidNotificationUM( + val type: Type, + val title: TextReference, + val text: TextReference? = null, +) { + internal enum class Type { + ERROR, WARNING + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index 2455b74700..965e4430b8 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -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)) } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt index 4c23abfde5..44b4ae7531 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt @@ -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() } } 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 0ff92d4802..b70cf31f20 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 @@ -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 { 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) { diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcTransactionCheckErrorItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcTransactionCheckErrorItem.kt index a8431340b3..a5d01a0246 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcTransactionCheckErrorItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcTransactionCheckErrorItem.kt @@ -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"), + ), + ) } } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt index 6d0df9a6db..4fd316dde9 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt @@ -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( From c8c61bb9d6d9cf38f64d38fc590257743236423a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 21:30:46 +0400 Subject: [PATCH 10/17] Updated on 2026-08-14 --- .../connections/utils/WcAlertsFactory.kt | 14 +++++++++++--- .../transaction/model/WcSendTransactionModel.kt | 10 ++++++---- .../transaction/routes/WcTransactionRoutes.kt | 6 +++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt index 587d3936b5..095ccd662e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcAlertsFactory.kt @@ -20,7 +20,7 @@ internal object WcAlertsFactory { is WcTransactionRoutes.Alert.Type.BlockAidErrorInfo -> createMaliciousDAppAlert(alertType.description, alertType.onClick, alertType.iconType, alertType.iconBgType) is WcTransactionRoutes.Alert.Type.UnknownError -> - createUnknownErrorAlert(alertType.errorMessage, alertType.onDismiss) + createUnknownErrorAlert(alertType.errorMessage, alertType.onDismiss, alertType.onRetry) } fun createUnknownDomainAlert(activeButtonOnClick: (() -> Unit)? = null): MessageBottomSheetUMV2 { @@ -127,7 +127,11 @@ internal object WcAlertsFactory { } } - private fun createUnknownErrorAlert(errorMessage: String?, onDismiss: () -> Unit): MessageBottomSheetUMV2 { + private fun createUnknownErrorAlert( + errorMessage: String?, + onDismiss: () -> Unit, + onRetry: () -> Unit, + ): MessageBottomSheetUMV2 { return messageBottomSheetUM { infoBlock { icon(R.drawable.img_attention_20) { @@ -143,8 +147,12 @@ internal object WcAlertsFactory { ) } } + primaryButton { + text = resourceReference(R.string.alert_button_try_again) + onClick { onRetry() } + } secondaryButton { - text = resourceReference(R.string.balance_hidden_got_it_button) + text = resourceReference(R.string.common_cancel) onClick { onDismiss() } } onDismissRequest = onDismiss diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index 965e4430b8..a5e0db4044 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -317,22 +317,24 @@ internal class WcSendTransactionModel @Inject constructor( private fun signingIsDone(signState: WcSignState<*>, useCase: WcSignUseCase<*>): Boolean { (signState.domainStep as? WcSignStep.Result)?.result?.let { - handleSigningError(it, useCase) - return true + return handleSigningError(it, useCase) } return false } - private fun handleSigningError(result: Either, useCase: WcSignUseCase<*>) { - if (result.isLeft()) { + private fun handleSigningError(result: Either, useCase: WcSignUseCase<*>): Boolean { + return if (result.isLeft()) { val error = WcTransactionRoutes.Alert.Type.UnknownError( errorMessage = result.leftOrNull()?.message(), onDismiss = { cancel(useCase) }, + onRetry = { signFromAlert() }, ) stackNavigation.pushNew(WcTransactionRoutes.Alert(error)) + false } else { showSuccessSignMessage() router.pop() + true } } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt index 44b4ae7531..843e0e291d 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt @@ -34,7 +34,11 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout val iconType: MessageBottomSheetUMV2.Icon.Type, val iconBgType: MessageBottomSheetUMV2.Icon.BackgroundType, ) : Type() - data class UnknownError(val errorMessage: String?, val onDismiss: () -> Unit) : Type() + data class UnknownError( + val errorMessage: String?, + val onDismiss: () -> Unit, + val onRetry: () -> Unit, + ) : Type() } } } \ No newline at end of file From 4089fe43ac58b106dbcbf8c30fb34da30f5fd98f Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 12:33:41 +0500 Subject: [PATCH 11/17] Updated on 2026-08-14 --- .../converter/WcSendTransactionUMConverter.kt | 81 +++++++++++-------- .../entity/send/WcSendTransactionUM.kt | 2 + .../model/WcSendTransactionModel.kt | 12 ++- .../ui/common/WcSendTransactionItems.kt | 4 +- .../send/WcSendTransactionModalBottomSheet.kt | 30 ++++++- .../utils/WcNotificationsFactory.kt | 39 +++++++++ 6 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt index c5f6ec3ed8..ccd8f0a447 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt @@ -1,5 +1,6 @@ package com.tangem.features.walletconnect.transaction.converter +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.usecase.method.WcMethodContext @@ -12,6 +13,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionItemUM import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM +import com.tangem.features.walletconnect.utils.WcNotificationsFactory import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject @@ -20,44 +22,52 @@ internal class WcSendTransactionUMConverter @Inject constructor( private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter, private val networkInfoUMConverter: WcNetworkInfoUMConverter, private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter, + private val notificationsFactory: WcNotificationsFactory, ) : Converter { - override fun convert(value: Input): WcSendTransactionUM? = when (value.context.method) { - is WcEthMethod.SendTransaction, - is WcEthMethod.SignTransaction, - is WcSolanaMethod.SignAllTransaction, - is WcSolanaMethod.SignTransaction, - -> WcSendTransactionUM( - transaction = WcSendTransactionItemUM( - onDismiss = value.actions.onDismiss, - onSend = value.actions.onSign, - appInfo = appInfoContentUMConverter.convert( - WcTransactionAppInfoContentUMConverter.Input( - session = value.context.session, - onShowVerifiedAlert = value.actions.onShowVerifiedAlert, - ), - ), - feeState = value.feeState, - walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo }, - networkInfo = networkInfoUMConverter.convert(value.context.network), - estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(), - isLoading = value.signState.domainStep == WcSignStep.Signing, - address = WcAddressConverter.convert(value.context.derivationState), - sendEnabled = value.feeSelectorUM is FeeSelectorUM.Content, - ), - feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading, - transactionRequestInfo = WcTransactionRequestInfoUM( - blocks = buildList { - addAll( - requestBlockUMConverter.convert( - WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest), - ), - ) - }.toImmutableList(), - onCopy = value.actions.onCopy, - ), + override fun convert(value: Input): WcSendTransactionUM? { + val feeExceedsBalance = notificationsFactory.createFeeExceedsBalance( + cryptoCurrencyStatus = value.cryptoCurrencyStatus, + feeSelectorUM = value.feeSelectorUM, ) - else -> null + return when (value.context.method) { + is WcEthMethod.SendTransaction, + is WcEthMethod.SignTransaction, + is WcSolanaMethod.SignAllTransaction, + is WcSolanaMethod.SignTransaction, + -> WcSendTransactionUM( + transaction = WcSendTransactionItemUM( + onDismiss = value.actions.onDismiss, + onSend = value.actions.onSign, + appInfo = appInfoContentUMConverter.convert( + WcTransactionAppInfoContentUMConverter.Input( + session = value.context.session, + onShowVerifiedAlert = value.actions.onShowVerifiedAlert, + ), + ), + feeState = value.feeState, + walletName = value.context.session.wallet.name.takeIf { value.context.session.showWalletInfo }, + networkInfo = networkInfoUMConverter.convert(value.context.network), + estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(), + isLoading = value.signState.domainStep == WcSignStep.Signing, + address = WcAddressConverter.convert(value.context.derivationState), + sendEnabled = value.feeSelectorUM is FeeSelectorUM.Content && feeExceedsBalance == null, + feeExceedsBalanceNotification = feeExceedsBalance, + ), + feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading, + transactionRequestInfo = WcTransactionRequestInfoUM( + blocks = buildList { + addAll( + requestBlockUMConverter.convert( + WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest), + ), + ) + }.toImmutableList(), + onCopy = value.actions.onCopy, + ), + ) + else -> null + } } data class Input( @@ -66,5 +76,6 @@ internal class WcSendTransactionUMConverter @Inject constructor( val signState: WcSignState<*>, val actions: WcTransactionActionsUM, val feeSelectorUM: FeeSelectorUM?, + val cryptoCurrencyStatus: CryptoCurrencyStatus, ) } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt index 271f0cd292..b6ace6d0ba 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt @@ -1,5 +1,6 @@ package com.tangem.features.walletconnect.transaction.entity.send +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM @@ -27,5 +28,6 @@ internal data class WcSendTransactionItemUM( val networkInfo: WcNetworkInfoUM, val address: String?, val sendEnabled: Boolean, + val feeExceedsBalanceNotification: NotificationUM.Info?, val isLoading: Boolean = false, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index a5e0db4044..ed77d9578a 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -47,6 +47,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiveBlockAidUiConverter +import com.tangem.features.walletconnect.utils.WcNotificationsFactory import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -69,6 +70,7 @@ internal class WcSendTransactionModel @Inject constructor( private val blockAidUiConverter: WcSendAndReceiveBlockAidUiConverter, private val getFeeUseCase: GetFeeUseCase, private val getNetworkCoinUseCase: GetNetworkCoinStatusUseCase, + private val notificationsFactory: WcNotificationsFactory, private val analytics: AnalyticsEventHandler, ) : Model(), WcCommonTransactionModel, FeeSelectorModelCallback { @@ -159,10 +161,17 @@ internal class WcSendTransactionModel @Inject constructor( * Also handles fee results from FeeSelectorBlockComponent */ fun updateFee(feeSelectorUM: FeeSelectorUM) { + val feeExceedsBalance = notificationsFactory.createFeeExceedsBalance( + cryptoCurrencyStatus = cryptoCurrencyStatus, + feeSelectorUM = feeSelectorUM, + ) _uiState.update { it?.copy( feeSelectorUM = feeSelectorUM, - transaction = it.transaction.copy(sendEnabled = feeSelectorUM is FeeSelectorUM.Content), + transaction = it.transaction.copy( + sendEnabled = feeSelectorUM is FeeSelectorUM.Content && feeExceedsBalance == null, + feeExceedsBalanceNotification = feeExceedsBalance, + ), ) } val fee = (feeSelectorUM as? FeeSelectorUM.Content)?.selectedFeeItem?.fee ?: return @@ -227,6 +236,7 @@ internal class WcSendTransactionModel @Inject constructor( signState = signState, actions = actions, feeSelectorUM = uiState.value?.feeSelectorUM, + cryptoCurrencyStatus = cryptoCurrencyStatus, ), ) transactionUM = transactionUM?.copy( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt index b8cd6c5c85..50f7f9e106 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt @@ -18,12 +18,14 @@ import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState +@Suppress("LongParameterList") @Composable internal fun WcSendTransactionItems( walletName: String?, networkInfo: WcNetworkInfoUM, feeState: WcTransactionFeeState, feeSelectorBlockComponent: FeeSelectorBlockComponent?, + feeExceedsBalance: Boolean, address: String?, modifier: Modifier = Modifier, ) { @@ -62,7 +64,7 @@ internal fun WcSendTransactionItems( address = address, ) } - if (feeState != WcTransactionFeeState.None) { + if (feeState != WcTransactionFeeState.None && !feeExceedsBalance) { DividerWithPadding(start = 40.dp, end = 12.dp) feeSelectorBlockComponent?.Content( modifier = if (onFeeBlockClicked != null) { diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt index 4fd316dde9..1a28add4aa 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt @@ -4,7 +4,9 @@ import android.content.res.Configuration import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -14,14 +16,17 @@ 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 com.tangem.common.ui.notifications.NotificationUM 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.components.divider.DividerWithPadding +import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.send.v2.api.FeeSelectorBlockComponent @@ -101,14 +106,22 @@ internal fun WcSendTransactionModalBottomSheet( if (state.estimatedWalletChanges != null) { TransactionCheckResultsItem(state.estimatedWalletChanges, onClickAllowToSpend) } - Spacer(Modifier.height(16.dp)) WcSendTransactionItems( + modifier = Modifier.padding(top = 16.dp), walletName = state.walletName, networkInfo = state.networkInfo, feeState = state.feeState, feeSelectorBlockComponent = feeSelectorBlockComponent, + feeExceedsBalance = state.feeExceedsBalanceNotification != null, address = state.address, ) + if (state.feeExceedsBalanceNotification != null) { + Notification( + modifier = Modifier.padding(top = 14.dp), + config = state.feeExceedsBalanceNotification.config, + iconTint = TangemTheme.colors.icon.warning, + ) + } } } }, @@ -199,9 +212,10 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide ), walletName = "Tangem 2.0 Tangem 2.0 Tangem 2", networkInfo = WcNetworkInfoUM(name = "Optimistic Ethereum Network", iconRes = R.drawable.img_eth_22), - feeState = WcTransactionFeeState.Success(null, {}), + feeState = WcTransactionFeeState.Success(dAppFee = null, onClick = {}), address = null, sendEnabled = true, + feeExceedsBalanceNotification = null, ), WcSendTransactionItemUM( onDismiss = {}, @@ -237,9 +251,13 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide ), walletName = "Tangem 2.0", networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), - feeState = WcTransactionFeeState.Success(null, {}), + feeState = WcTransactionFeeState.Success(dAppFee = null, onClick = {}), address = "0xdac17f958d2ee523a2206206994597c13d831ec7", sendEnabled = true, + feeExceedsBalanceNotification = NotificationUM.Info( + title = stringReference("Insufficient Ethereum"), + subtitle = stringReference("Top up your balance to cover the network fee"), + ), ), WcSendTransactionItemUM( onDismiss = {}, @@ -278,6 +296,10 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide feeState = WcTransactionFeeState.None, address = null, sendEnabled = false, + feeExceedsBalanceNotification = NotificationUM.Info( + title = stringReference("Insufficient Ethereum"), + subtitle = stringReference("Top up your balance to cover the network fee"), + ), ), ), ) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt new file mode 100644 index 0000000000..db5e1c5ed9 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt @@ -0,0 +1,39 @@ +package com.tangem.features.walletconnect.utils + +import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.FeeSelectorUM +import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils +import javax.inject.Inject + +internal class WcNotificationsFactory @Inject constructor() { + + fun createFeeExceedsBalance( + cryptoCurrencyStatus: CryptoCurrencyStatus, + feeSelectorUM: FeeSelectorUM?, + ): NotificationUM.Info? { + // TODO: [REDACTED_TASK_KEY] localization + return NotificationUM.Info( + title = stringReference("Insufficient ${cryptoCurrencyStatus.currency.name}"), + subtitle = stringReference("Top up your balance to cover the network fee"), + ).takeIf { isFeeExceedsBalance(cryptoCurrencyStatus = cryptoCurrencyStatus, feeSelectorUM = feeSelectorUM) } + } + + private fun isFeeExceedsBalance( + cryptoCurrencyStatus: CryptoCurrencyStatus, + feeSelectorUM: FeeSelectorUM?, + ): Boolean { + val feeSelectorContent = feeSelectorUM as? FeeSelectorUM.Content ?: return false + val lowestFee = when (val fees = feeSelectorContent.fees) { + is TransactionFee.Choosable -> fees.minimum + is TransactionFee.Single -> fees.normal + } + + return FeeCalculationUtils.checkExceedBalance( + feeBalance = cryptoCurrencyStatus.value.amount, + feeAmount = lowestFee.amount.value, + ) + } +} \ No newline at end of file From 779aaab34275fad45f0f9d1c36bb8605e67e3ac7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 31 Jul 2025 19:28:46 +0700 Subject: [PATCH 12/17] Updated on 2026-08-14 --- .../domain/tokens/actions/BaseActionsFactory.kt | 2 +- .../tokens/actions/CommonActionsFactory.kt | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt index 6feda808cf..9f6481cf9f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt @@ -173,7 +173,7 @@ internal open class BaseActionsFactory( } } - private fun getReceiveScenario(requirements: AssetRequirementsCondition?): ScenarioUnavailabilityReason { + protected fun getReceiveScenario(requirements: AssetRequirementsCondition?): ScenarioUnavailabilityReason { return when (requirements) { AssetRequirementsCondition.PaidTransaction, is AssetRequirementsCondition.PaidTransactionWithFee, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt index 41890a7320..bd735df30c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt @@ -2,13 +2,14 @@ package com.tangem.domain.tokens.actions import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState.ActionState +import com.tangem.domain.transaction.models.AssetRequirementsCondition import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope @@ -65,6 +66,7 @@ internal class CommonActionsFactory( getSwapUnavailabilityReason( userWalletId = userWallet.walletId, currency = cryptoCurrencyStatus.currency, + requirementsDeferred = requirementsDeferred, ) } } else { @@ -170,7 +172,16 @@ internal class CommonActionsFactory( private suspend fun getSwapUnavailabilityReason( userWalletId: UserWalletId, currency: CryptoCurrency, + requirementsDeferred: Deferred?, ): ScenarioUnavailabilityReason { - return rampStateManager.availableForSwap(userWalletId = userWalletId, cryptoCurrency = currency) + val swapUnavailabilityReason = rampStateManager + .availableForSwap(userWalletId = userWalletId, cryptoCurrency = currency) + val shouldCheckAssetRequirements = + swapUnavailabilityReason == ScenarioUnavailabilityReason.None && requirementsDeferred != null + return if (shouldCheckAssetRequirements) { + getReceiveScenario(requirementsDeferred.await()) + } else { + swapUnavailabilityReason + } } } \ No newline at end of file From aa2439e3faf0c87898ebda4a10a58b29e0a6982f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 13:48:29 +0700 Subject: [PATCH 13/17] Updated on 2026-08-14 --- .../tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt | 4 ++-- .../com/tangem/domain/walletconnect/WcAnalyticEvents.kt | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt index b12a31a1c0..bfd1192118 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt @@ -49,7 +49,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( val pairResult = sdkDelegate.pair(uri) .onLeft { Timber.tag(WC_TAG).e(it, "Failed to call pair $pairRequest") - analytics.send(WcAnalyticEvents.PairFailed) + analytics.send(WcAnalyticEvents.PairFailed(it.code)) emit(WcPairState.Error(it)) } .getOrNull() ?: return@flow @@ -65,7 +65,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( val proposalState = buildProposalState(sdkSessionProposal, sdkVerifyContext) .onLeft { - analytics.send(WcAnalyticEvents.PairFailed) + analytics.send(WcAnalyticEvents.PairFailed(it.code)) emit(WcPairState.Error(it)) } .getOrNull() ?: return@flow diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt index 0062c528cc..fa4b050106 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt @@ -49,8 +49,13 @@ sealed class WcAnalyticEvents( ), ) - data object PairFailed : WcAnalyticEvents( + class PairFailed( + errorCode: String, + ) : WcAnalyticEvents( event = "Session Failed", + params = mapOf( + AnalyticsParam.Key.ERROR_CODE to errorCode, + ), ) class DAppConnected( From e803ab0d7d08ebe0377ceafa204d1de28ffa8f01 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 12:11:22 +0400 Subject: [PATCH 14/17] Updated on 2026-08-14 --- .../blockaid/models/response/AssetDiff.kt | 4 +- .../blockaid/models/response/Exposure.kt | 6 ++- core/res/src/main/res/values-fr/strings.xml | 52 +++++++++++++++++++ core/res/src/main/res/values/strings.xml | 2 + .../tangem/data/blockaid/BlockAidMapper.kt | 20 +++---- .../data/blockaid/BlockAidMapperTest.kt | 4 +- .../network/ethereum/WcEthTxHelper.kt | 2 +- .../transaction/simultation/SimulationData.kt | 5 ++ .../WcSendReceiveTransactionCheckResultsUM.kt | 1 + .../blockaid/TransactionCheckResultsItem.kt | 5 +- ...EstimatedWalletChangesNotificationItem.kt} | 12 +++-- .../WcSendAndReceiveBlockAidUiConverter.kt | 14 +++-- 12 files changed, 103 insertions(+), 24 deletions(-) rename features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/{WcEstimatedWalletChangesNotLoadedItem.kt => WcEstimatedWalletChangesNotificationItem.kt} (82%) 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 51e5374ae8..be70cd4a52 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 @@ -15,8 +15,8 @@ data class AssetDiff( data class Asset( @Json(name = "chain_id") val chainId: Int? = null, @Json(name = "logo_url") val logoUrl: String? = null, - @Json(name = "symbol") val symbol: String, - @Json(name = "decimals") val decimals: Int, + @Json(name = "symbol") val symbol: String? = null, + @Json(name = "decimals") val decimals: Int? = null, ) @JsonClass(generateAdapter = true) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Exposure.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Exposure.kt index 0ed36e6a25..e776056d4c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Exposure.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/blockaid/models/response/Exposure.kt @@ -18,6 +18,8 @@ data class SpenderDetails( @JsonClass(generateAdapter = true) data class ExposureDetail( - @Json(name = "value") val value: String, - @Json(name = "raw_value") val rawValue: String, + @Json(name = "value") val value: String? = null, + @Json(name = "raw_value") val rawValue: String? = null, + @Json(name = "token_id") val tokenId: String? = null, + @Json(name = "logo_url") val logoUrl: String? = null, ) \ No newline at end of file diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 075f05f465..b28e3174c2 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -66,6 +66,10 @@ %d carte %d cartes + + %s token + %s tokens + Désactivez cette option si vous ne voulez pas que cette carte soit utilisée pour réinitialiser les codes d\'accès sur d\'autres cartes de ce portefeuille. Veuillez noter que cela vous empêchera également de réinitialiser le code d\'accès sur cette carte. Vous permet d\'utiliser cette carte pour réinitialiser le code d\'accès sur d\'autres cartes de ce portefeuille Récupération du code d\'accès @@ -111,6 +115,7 @@ Annuler Changez Choisissez une action + Choisissez le réseau Choisir le jeton Réclamer Réclamez des récompenses @@ -152,6 +157,7 @@ Synchroniser les adresses Aller au fournisseur Aller au jeton + Compris Cacher heure Importez @@ -196,6 +202,7 @@ Le serveur n\'est pas disponible, veuillez réessayer plus tard Partager Partager le lien + Afficher moins Afficher plus Signez Signez et envoyez @@ -205,6 +212,7 @@ Soumettre Avec succès Support + Réseaux pris en charge Échanger termes et conditions Conditions d\'utilisation @@ -265,6 +273,7 @@ %s hashes ID de l\'appareil Contactez l\'équipe de support + Ouvrir le chat d\'assistance Lier plus de cartes Monnaie de l\'application Retourner pour masquer les soldes @@ -281,6 +290,7 @@ Ne peut pas être échangé contre %s Fourni par Statut + Choisir un fournisseur Tangem propose des échanges de jetons via des fournisseurs tiers selon les conditions de chaque fournisseur Fournisseur Une erreur s\'est produite. Code : %s @@ -340,6 +350,9 @@ Masquer cette transaction Une fois masqué, le statut de la transaction ne peut plus être consulté. Vous pouvez simplement faire glisser votre doigt pour le fermer. Masquer le statut de la transaction ? + Ce jeton n\'est pas pris en charge. Veuillez choisir un autre jeton à échanger. + %s n\'est pas pris en charge + Échanger avec Aucun jeton trouvé. Veuillez essayer une autre demande ID : %s ID de transaction copié @@ -367,6 +380,8 @@ Pour continuer, accordez aux smart contracts de %1s l\'autorisation d\'utiliser votre %2s Donner l\'autorisation Illimité + Ajouter un Portefeuille existant + Créer un nouveau Portefeuille Commandez Scannez à %s @@ -423,6 +438,7 @@ Cet actif n\'est pas pris en charge dans le portefeuille Cet actif n\'est pas disponible pour ce portefeuille Ajoutez un jeton + APY %s Réseaux disponibles Mon portfolio Marché @@ -709,6 +725,7 @@ Paramètres Activer les notifications Recevez des alertes pour les transactions entrantes sur les réseaux pris en charge. + Notifications de transaction Sélectionnez dans la galerie Paramètres Vous n\'avez pas donné accès à votre caméra @@ -818,6 +835,9 @@ Mémo : %s Mémo invalide Couverture des frais de réseau + Nonce + Numéro unique pour chaque transaction. Utilisez-le pour renvoyer ou annuler une transaction en attente. + Entrez le nonce… Fonds insuffisants pour le transfert, car le total des frais et du montant du transfert dépasse le solde existant Le total dépasse le solde Un solde d\'au moins %s est requis pour conserver votre compte sur la blockchain afin d\'éviter les risques de sécurité. Ce montant restera sur votre solde et ne pourra pas être retiré. @@ -847,6 +867,7 @@ Adresse non valide Assurez-vous que l\'adresse du portefeuille de réception est sur le réseau %s pour éviter de perdre vos jetons envoyer à %s + Assurez-vous de %s une adresse réseau, car des erreurs peuvent entraîner des transferts perdus Envoyer à Un Memo/Destination Tag est un identifiant unique permettant de différencier les transactions envoyées au même destinataire sur le même réseau. Attention : L\'omission d\'un mémo peut entraîner des fonds mal placés Le Memo / Destination Tag est un code qui distingue les transactions envoyées à un destinataire partagé sur un réseau crypto. @@ -873,7 +894,11 @@ Les frais de commissions dépassent le solde Le total dépasse le solde Échanger et envoyer + Envoyez n\'importe quel jeton et nous le convertirons en cours de route. Votre destinataire reçoit exactement ce dont il a besoin, en toute simplicité. + Le destinataire recevra + Un destinataire sera envoyé Montant à recevoir + Envoyer avec swap Transaction envoyée Scannez la carte/ bague que vous souhaitez configurer. Oublier le portefeuille @@ -1014,6 +1039,7 @@ Compatible avec Web 3.0 Une transaction entrante d\'au moins de %1$s est requise pour continuer Fonds insuffisants + Taux fixe Le réseau facturera des frais d\'approbation de jeton pour vérifier que vous autorisez l\'utilisation de votre jeton pour l\'échange. Échangez plus de jetons à de meilleurs taux directement dans votre portefeuille. Nouveau fournisseur d\'échange disponible ! @@ -1091,6 +1117,8 @@ Notifications de transaction Minimum %s Le montant minimum pour effectuer cette transaction est %1$s. + Les frais de réseau Tron pour les jetons populaires peuvent être plus élevés. Le staking de TRX peut contribuer à réduire les coûts de transaction. + Économisez sur les frais de réseau Tron Réessayez Vous avez scanné la même carte. Pour créer un portefeuille jumeau, vous devez scanner la carte portant le numéro %d Vous avez scanné une mauvaise carte jumelle. S\'il vous plaît, essayez-en un autre @@ -1103,6 +1131,8 @@ Tangem Twin Cette action est irréversible. Vous n\'aurez plus accès à l\'ancien portefeuille. Appuyez sur la carte jumelle avec le numéro %s et ne la retirez pas jusqu\'à la fin de l\'opération + Veuillez réessayer ultérieurement. Si le problème persiste, veuillez contacter le support. + Une erreur s\'est produite ! Nous avons rencontré une erreur. Code d\'erreur : %s. Veuillez contacter notre équipe de support. Utilisez %s ou scannez une carte/bague pour avoir accès à votre portefeuille Échec de la connexion : Cette dApp utilise Wallet Connect version1.0, qui n\'est pas prise en charge. Veuillez vous assurer que la dApp prend en charge Wallet Connect version2.0 pour réussir la connexion. @@ -1253,6 +1283,7 @@ Votre avis nous motive à améliorer encore le Portefeuille Tangem Vous appréciez Tangem ? Vous devez associer votre jeton avant de recevoir des jetons + Vous devez ouvrir la ligne de confiance pour votre jeton avant de le recevoir Frais de location de réseau requis Action requise Avez-vous contacté l\'assistance via l\'application ou par e-mail dans les 7 jours suivant la création d\'un portefeuille ? Si vous l\'avez fait ou si vous n\'êtes pas sûr, suivez et complétez les instructions. @@ -1273,17 +1304,38 @@ À des fins de test uniquement Le solde peut être obsolète. Rafraîchissez la page. Pas assez de %1$s. Rechargez votre compte %2$s pour associer ce jeton. + Activer Trustline + Une Trustline doit être activée pour recevoir ce jeton. Le réseau requiert une réserve de %1$s %2$s + Trustline requise Domaine malveillant + Signer quand même Si le problème persiste, n’hésitez pas à contacter notre support. Le portefeuille Tangem ne prend actuellement pas en charge %ss dApp non prise en charge + Code d\'erreur : 8 005. Si le problème persiste, n\'hésitez pas à contacter notre support. + Nous avons rencontré une erreur inconnue + Autoriser à dépenser + Adresse + Chargement + Illimité Connexions + Contenu + Copier les données Déconnecter tout Texte sur la déconnexion de toutes les dApps Déconnecter toutes les dApps + Modifications estimées du portefeuille + La transaction n\'a pas pu être simulée. Veuillez procéder avec prudence. + Transaction malveillante Nouvelle connexion Connectez votre portefeuille à différentes dApps Aucune séance + Demande de + Type de signature + À + Demande de transaction + Demande de transaction + Montant illimité Ignorer Vous avez une sauvegarde interrompue. Voulez-vous la reprendre ? Oui, reprendre diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 18834e5cbc..704c48f319 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Set a %s-digit Access Code to unlock your wallet. Create Access Code Access code + Account name Can’t find your token? Go to the Market section on the main page and add it to your portfolio for purchase Can’t find your token? Go to the Market section on the main page and add it to your portfolio for selling. Sell @@ -1500,6 +1501,7 @@ New connection Connect your wallet to a different dApps No sessions + No wallet changes detected This domain is flagged as unsafe by multiple security providers. Leave immediately to protect your assets Known security risk Request from 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 857ec25986..128d1cfd56 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 @@ -85,7 +85,7 @@ internal object BlockAidMapper { from.exposures, ) !from.traces.isNullOrEmpty() -> mapNftSendReceiveTransaction(from.traces) - else -> SimulationResult.FailedToSimulate + else -> SimulationResult.Success(data = SimulationData.NoWalletChangesDetected) } } @@ -94,15 +94,15 @@ internal object BlockAidMapper { val tokenInfo = TokenInfo( chainId = exposure.asset.chainId, logoUrl = exposure.asset.logoUrl, - symbol = exposure.asset.symbol, - decimals = exposure.asset.decimals, + symbol = exposure.asset.symbol ?: "", + decimals = exposure.asset.decimals ?: 0, ) exposure.spenders.flatMap { (_, spender) -> val isUnlimited = spender.isApprovedForAll == true val approval = spender.approval?.hexToBigDecimal() - spender.exposure.mapNotNull { detail -> + spender.exposure.map { detail -> ApprovedAmount( - approvedAmount = detail.value.toBigDecimalOrNull() ?: approval ?: return@mapNotNull null, + approvedAmount = detail.value?.toBigDecimalOrNull() ?: approval ?: 1.toBigDecimal(), isUnlimited = isUnlimited, tokenInfo = tokenInfo, ) @@ -112,7 +112,7 @@ internal object BlockAidMapper { return if (!amounts.isNullOrEmpty()) { SimulationResult.Success(SimulationData.Approve(amounts)) } else { - SimulationResult.FailedToSimulate + SimulationResult.Success(SimulationData.NoWalletChangesDetected) } } @@ -124,8 +124,8 @@ internal object BlockAidMapper { val token = TokenInfo( chainId = diff.asset.chainId, logoUrl = diff.asset.logoUrl, - symbol = diff.asset.symbol, - decimals = diff.asset.decimals, + symbol = diff.asset.symbol ?: "", + decimals = diff.asset.decimals ?: 0, ) diff.outTransfer.orEmpty().forEach { transfer -> transfer.value?.toBigDecimalOrNull()?.let { amount -> @@ -142,7 +142,7 @@ internal object BlockAidMapper { return if (sendInfo.isNotEmpty() || receiveInfo.isNotEmpty()) { SimulationResult.Success(SimulationData.SendAndReceive(send = sendInfo, receive = receiveInfo)) } else { - SimulationResult.FailedToSimulate + SimulationResult.Success(SimulationData.NoWalletChangesDetected) } } @@ -156,7 +156,7 @@ internal object BlockAidMapper { return if (!sendInfo.isNullOrEmpty()) { SimulationResult.Success(SimulationData.SendAndReceive(send = sendInfo, receive = listOf())) } else { - SimulationResult.FailedToSimulate + SimulationResult.Success(SimulationData.NoWalletChangesDetected) } } } \ 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 1980d63605..00dd54e198 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 @@ -114,7 +114,7 @@ class BlockAidMapperTest { val result = mapper.mapToDomain(response) Truth.assertThat(result.validation).isEqualTo(ValidationResult.FAILED_TO_VALIDATE) - Truth.assertThat(result.simulation is SimulationResult.FailedToSimulate).isTrue() + Truth.assertThat(result.simulation is SimulationResult.Success).isTrue() } @Test @@ -160,6 +160,6 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(txResponse) - Truth.assertThat(result.simulation is SimulationResult.FailedToSimulate).isTrue() + Truth.assertThat(result.simulation is SimulationResult.Success).isTrue() } } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthTxHelper.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthTxHelper.kt index ebf796f991..057afaa2e9 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthTxHelper.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthTxHelper.kt @@ -76,7 +76,7 @@ internal class WcEthTxHelper @Inject constructor( ?: return null val approves = (simulation.data as? SimulationData.Approve)?.approvedAmounts ?: return null - if (approves.size != 1) return null + if (approves.isEmpty()) return null val amount = approves.first() return amount } diff --git a/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/SimulationData.kt b/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/SimulationData.kt index afd8fea9e9..13c28ebba4 100644 --- a/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/SimulationData.kt +++ b/domain/blockaid/models/src/main/kotlin/com/domain/blockaid/models/transaction/simultation/SimulationData.kt @@ -19,4 +19,9 @@ sealed class SimulationData { data class Approve( val approvedAmounts: List, ) : SimulationData() + + /** + * Simulation was successfully performed and no changes detected + */ + data object NoWalletChangesDetected : SimulationData() } \ 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 index 57b9c03f54..850cfb201e 100644 --- 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 @@ -7,6 +7,7 @@ internal data class WcSendReceiveTransactionCheckResultsUM( val estimatedWalletChanges: WcEstimatedWalletChangesUM? = null, val spendAllowance: WcSpendAllowanceUM? = null, val notification: BlockAidNotificationUM? = null, + val additionalNotification: TextReference? = null, val isLoading: Boolean = true, ) 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 b70cf31f20..2b7aca4340 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 @@ -11,6 +11,7 @@ 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.isNullOrEmpty import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -43,8 +44,10 @@ internal fun TransactionCheckResultsItem( WcEstimatedWalletChangesItem(item.estimatedWalletChanges) } else if (item.spendAllowance != null) { WcSpendAllowanceItem(item.spendAllowance, onClickAllowToSpend) + } else if (!item.additionalNotification.isNullOrEmpty()) { + WcEstimatedWalletChangesNotificationItem(description = item.additionalNotification) } else { - WcEstimatedWalletChangesNotLoadedItem() + WcEstimatedWalletChangesNotificationItem() } } } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcEstimatedWalletChangesNotLoadedItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcEstimatedWalletChangesNotificationItem.kt similarity index 82% rename from features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcEstimatedWalletChangesNotLoadedItem.kt rename to features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcEstimatedWalletChangesNotificationItem.kt index de2a38ce6a..603839d220 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcEstimatedWalletChangesNotLoadedItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/blockaid/WcEstimatedWalletChangesNotificationItem.kt @@ -12,6 +12,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview 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.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -19,7 +21,10 @@ import com.tangem.features.walletconnect.impl.R import com.tangem.features.walletconnect.transaction.ui.common.WcSmallTitleItem @Composable -internal fun WcEstimatedWalletChangesNotLoadedItem(modifier: Modifier = Modifier) { +internal fun WcEstimatedWalletChangesNotificationItem( + modifier: Modifier = Modifier, + description: TextReference? = null, +) { Column( modifier = modifier .clip(RoundedCornerShape(14.dp)) @@ -36,7 +41,8 @@ internal fun WcEstimatedWalletChangesNotLoadedItem(modifier: Modifier = Modifier modifier = modifier .fillMaxWidth() .padding(start = 12.dp, end = 12.dp, bottom = 14.dp), - text = stringResourceSafe(R.string.wc_estimated_wallet_changes_not_simulated), + text = description?.resolveReference() + ?: stringResourceSafe(R.string.wc_estimated_wallet_changes_not_simulated), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, ) @@ -52,7 +58,7 @@ private fun WcEstimatedWalletChangesNotLoadedItems() { modifier = Modifier .background(TangemTheme.colors.background.tertiary), ) { - WcEstimatedWalletChangesNotLoadedItem() + WcEstimatedWalletChangesNotificationItem() } } } \ No newline at end of file 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 index d42cf5e233..1eda478462 100644 --- 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 @@ -30,6 +30,7 @@ internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor( ) : Converter { override fun convert(value: Input): WcSendReceiveTransactionCheckResultsUM { val description = value.result.description?.let { if (it.isNotEmpty()) TextReference.Str(it) else null } + val simulation = value.result.simulation return WcSendReceiveTransactionCheckResultsUM( isLoading = false, notification = when (value.result.validation) { @@ -45,9 +46,16 @@ internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor( text = description, ) }, - estimatedWalletChanges = (value.result.simulation as? SimulationResult.Success)?.data?.let { data -> + additionalNotification = (simulation as? SimulationResult.Success)?.data?.let { data -> + if (data is SimulationData.NoWalletChangesDetected) { + TextReference.Res(R.string.wc_no_wallet_changes_detected) + } else { + null + } + }, + estimatedWalletChanges = (simulation as? SimulationResult.Success)?.data?.let { data -> when (data) { - is SimulationData.Approve -> null + is SimulationData.Approve, SimulationData.NoWalletChangesDetected -> null is SimulationData.SendAndReceive -> { val items: ImmutableList = ( data.send.map { @@ -94,7 +102,7 @@ internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor( }, spendAllowance = (value.result.simulation as? SimulationResult.Success)?.data?.let { data -> when (data) { - is SimulationData.SendAndReceive -> null + is SimulationData.SendAndReceive, SimulationData.NoWalletChangesDetected -> null is SimulationData.Approve -> value.approvedAmount?.let { spendAllowanceUMConverter.convert(it) } From 955da46b077766aa2da0d2f24b84852e45e096f5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 13:42:29 +0500 Subject: [PATCH 15/17] Updated on 2026-08-14 --- .../send/WcSendTransactionComponent.kt | 2 ++ .../converter/WcSendTransactionUMConverter.kt | 8 ++++-- .../entity/send/WcSendTransactionUM.kt | 2 +- .../model/WcSendTransactionModel.kt | 10 ++++--- .../ui/common/WcSendTransactionItems.kt | 14 ++++++---- .../send/WcSendTransactionModalBottomSheet.kt | 16 +++++++---- .../utils/WcNotificationsFactory.kt | 28 ++++++++++++++++++- 7 files changed, 59 insertions(+), 21 deletions(-) diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/send/WcSendTransactionComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/send/WcSendTransactionComponent.kt index eb105f2216..62f838c9b7 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/send/WcSendTransactionComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/send/WcSendTransactionComponent.kt @@ -7,6 +7,7 @@ import com.arkivanov.essenty.lifecycle.doOnResume import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.features.send.v2.api.FeeSelectorBlockComponent +import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.params.FeeSelectorParams import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel @@ -58,6 +59,7 @@ internal class WcSendTransactionComponent( WcSendTransactionModalBottomSheet( state = state, feeSelectorBlockComponent = feeSelectorBlock, + feeSelectorUM = content?.feeSelectorUM ?: FeeSelectorUM.Loading, onClickTransactionRequest = model::showTransactionRequest, onBack = router::pop, onDismiss = ::dismiss, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt index ccd8f0a447..43c5b87851 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt @@ -26,9 +26,10 @@ internal class WcSendTransactionUMConverter @Inject constructor( ) : Converter { override fun convert(value: Input): WcSendTransactionUM? { - val feeExceedsBalance = notificationsFactory.createFeeExceedsBalance( + val feeErrorNotification = notificationsFactory.createFeeNotifications( cryptoCurrencyStatus = value.cryptoCurrencyStatus, feeSelectorUM = value.feeSelectorUM, + onFeeReload = value.onFeeReload, ) return when (value.context.method) { is WcEthMethod.SendTransaction, @@ -51,8 +52,8 @@ internal class WcSendTransactionUMConverter @Inject constructor( estimatedWalletChanges = WcSendReceiveTransactionCheckResultsUM(), isLoading = value.signState.domainStep == WcSignStep.Signing, address = WcAddressConverter.convert(value.context.derivationState), - sendEnabled = value.feeSelectorUM is FeeSelectorUM.Content && feeExceedsBalance == null, - feeExceedsBalanceNotification = feeExceedsBalance, + sendEnabled = value.feeSelectorUM is FeeSelectorUM.Content && feeErrorNotification == null, + feeErrorNotification = feeErrorNotification, ), feeSelectorUM = value.feeSelectorUM ?: FeeSelectorUM.Loading, transactionRequestInfo = WcTransactionRequestInfoUM( @@ -77,5 +78,6 @@ internal class WcSendTransactionUMConverter @Inject constructor( val actions: WcTransactionActionsUM, val feeSelectorUM: FeeSelectorUM?, val cryptoCurrencyStatus: CryptoCurrencyStatus, + val onFeeReload: () -> Unit, ) } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt index b6ace6d0ba..86ef260951 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt @@ -28,6 +28,6 @@ internal data class WcSendTransactionItemUM( val networkInfo: WcNetworkInfoUM, val address: String?, val sendEnabled: Boolean, - val feeExceedsBalanceNotification: NotificationUM.Info?, + val feeErrorNotification: NotificationUM.Info?, val isLoading: Boolean = false, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index ed77d9578a..b86107b35c 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -142,7 +142,7 @@ internal class WcSendTransactionModel @Inject constructor( feeReloadState.value = false modelScope.launch { feeSelectorReloadTrigger.triggerUpdate( - feeData = FeeSelectorData(removeSuggestedFee = true), + FeeSelectorData(removeSuggestedFee = feeStateConfiguration !is FeeStateConfiguration.Suggestion), ) } } @@ -161,16 +161,17 @@ internal class WcSendTransactionModel @Inject constructor( * Also handles fee results from FeeSelectorBlockComponent */ fun updateFee(feeSelectorUM: FeeSelectorUM) { - val feeExceedsBalance = notificationsFactory.createFeeExceedsBalance( + val feeErrorNotification = notificationsFactory.createFeeNotifications( cryptoCurrencyStatus = cryptoCurrencyStatus, feeSelectorUM = feeSelectorUM, + onFeeReload = ::triggerFeeReload, ) _uiState.update { it?.copy( feeSelectorUM = feeSelectorUM, transaction = it.transaction.copy( - sendEnabled = feeSelectorUM is FeeSelectorUM.Content && feeExceedsBalance == null, - feeExceedsBalanceNotification = feeExceedsBalance, + sendEnabled = feeSelectorUM is FeeSelectorUM.Content && feeErrorNotification == null, + feeErrorNotification = feeErrorNotification, ), ) } @@ -237,6 +238,7 @@ internal class WcSendTransactionModel @Inject constructor( actions = actions, feeSelectorUM = uiState.value?.feeSelectorUM, cryptoCurrencyStatus = cryptoCurrencyStatus, + onFeeReload = ::triggerFeeReload, ), ) transactionUM = transactionUM?.copy( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt index 50f7f9e106..fe0f4cfc06 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcSendTransactionItems.kt @@ -15,6 +15,7 @@ import com.tangem.core.ui.components.divider.DividerWithPadding import com.tangem.core.ui.extensions.clickableSingle import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.v2.api.FeeSelectorBlockComponent +import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState @@ -25,14 +26,15 @@ internal fun WcSendTransactionItems( networkInfo: WcNetworkInfoUM, feeState: WcTransactionFeeState, feeSelectorBlockComponent: FeeSelectorBlockComponent?, - feeExceedsBalance: Boolean, + feeSelectorUM: FeeSelectorUM, address: String?, modifier: Modifier = Modifier, ) { - val onFeeBlockClicked = remember(feeState) { - when (feeState) { - WcTransactionFeeState.None -> null - is WcTransactionFeeState.Success -> feeState.onClick + val onFeeBlockClicked = remember(feeState, feeSelectorUM) { + if (feeState is WcTransactionFeeState.Success && feeSelectorUM is FeeSelectorUM.Content) { + feeState.onClick + } else { + null } } Column( @@ -64,7 +66,7 @@ internal fun WcSendTransactionItems( address = address, ) } - if (feeState != WcTransactionFeeState.None && !feeExceedsBalance) { + if (feeState != WcTransactionFeeState.None) { DividerWithPadding(start = 40.dp, end = 12.dp) feeSelectorBlockComponent?.Content( modifier = if (onFeeBlockClicked != null) { diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt index 1a28add4aa..0a0a1f1eac 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt @@ -30,6 +30,7 @@ import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.send.v2.api.FeeSelectorBlockComponent +import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem import com.tangem.features.walletconnect.impl.R @@ -54,6 +55,7 @@ import kotlinx.collections.immutable.persistentListOf internal fun WcSendTransactionModalBottomSheet( state: WcSendTransactionItemUM, feeSelectorBlockComponent: FeeSelectorBlockComponent?, + feeSelectorUM: FeeSelectorUM, onClickTransactionRequest: () -> Unit, onBack: () -> Unit, onDismiss: () -> Unit, @@ -112,14 +114,15 @@ internal fun WcSendTransactionModalBottomSheet( networkInfo = state.networkInfo, feeState = state.feeState, feeSelectorBlockComponent = feeSelectorBlockComponent, - feeExceedsBalance = state.feeExceedsBalanceNotification != null, + feeSelectorUM = feeSelectorUM, address = state.address, ) - if (state.feeExceedsBalanceNotification != null) { + if (state.feeErrorNotification != null) { Notification( modifier = Modifier.padding(top = 14.dp), - config = state.feeExceedsBalanceNotification.config, + config = state.feeErrorNotification.config, iconTint = TangemTheme.colors.icon.warning, + containerColor = TangemTheme.colors.button.disabled, ) } } @@ -169,6 +172,7 @@ private fun WcSendTransactionBottomSheetPreview( onBack = {}, onDismiss = {}, onClickAllowToSpend = {}, + feeSelectorUM = FeeSelectorUM.Loading, ) }, ) @@ -215,7 +219,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide feeState = WcTransactionFeeState.Success(dAppFee = null, onClick = {}), address = null, sendEnabled = true, - feeExceedsBalanceNotification = null, + feeErrorNotification = null, ), WcSendTransactionItemUM( onDismiss = {}, @@ -254,7 +258,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide feeState = WcTransactionFeeState.Success(dAppFee = null, onClick = {}), address = "0xdac17f958d2ee523a2206206994597c13d831ec7", sendEnabled = true, - feeExceedsBalanceNotification = NotificationUM.Info( + feeErrorNotification = NotificationUM.Info( title = stringReference("Insufficient Ethereum"), subtitle = stringReference("Top up your balance to cover the network fee"), ), @@ -296,7 +300,7 @@ private class WcSendTransactionStateProvider : CollectionPreviewParameterProvide feeState = WcTransactionFeeState.None, address = null, sendEnabled = false, - feeExceedsBalanceNotification = NotificationUM.Info( + feeErrorNotification = NotificationUM.Info( title = stringReference("Insufficient Ethereum"), subtitle = stringReference("Top up your balance to cover the network fee"), ), diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt index db5e1c5ed9..0b1796ebec 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/utils/WcNotificationsFactory.kt @@ -2,15 +2,30 @@ package com.tangem.features.walletconnect.utils import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils +import com.tangem.features.walletconnect.impl.R import javax.inject.Inject internal class WcNotificationsFactory @Inject constructor() { - fun createFeeExceedsBalance( + fun createFeeNotifications( + cryptoCurrencyStatus: CryptoCurrencyStatus, + feeSelectorUM: FeeSelectorUM?, + onFeeReload: () -> Unit, + ): NotificationUM.Info? { + return when (feeSelectorUM) { + is FeeSelectorUM.Content -> createFeeExceedsBalance(cryptoCurrencyStatus, feeSelectorUM) + is FeeSelectorUM.Error -> createFeeErrorNotification(onFeeReload) + FeeSelectorUM.Loading, null -> null + } + } + + private fun createFeeExceedsBalance( cryptoCurrencyStatus: CryptoCurrencyStatus, feeSelectorUM: FeeSelectorUM?, ): NotificationUM.Info? { @@ -21,6 +36,17 @@ internal class WcNotificationsFactory @Inject constructor() { ).takeIf { isFeeExceedsBalance(cryptoCurrencyStatus = cryptoCurrencyStatus, feeSelectorUM = feeSelectorUM) } } + private fun createFeeErrorNotification(onFeeReload: () -> Unit): NotificationUM.Info { + return NotificationUM.Info( + title = resourceReference(R.string.send_fee_unreachable_error_title), + subtitle = resourceReference(R.string.send_fee_unreachable_error_text), + buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = resourceReference(R.string.warning_button_refresh), + onClick = onFeeReload, + ), + ) + } + private fun isFeeExceedsBalance( cryptoCurrencyStatus: CryptoCurrencyStatus, feeSelectorUM: FeeSelectorUM?, From 6b2fa7b9be36df3bcc2dd38a82dd24082e3104c1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 15:58:43 +0500 Subject: [PATCH 16/17] Updated on 2026-08-14 --- .../tap/common/url/CustomTabsUrlOpener.kt | 41 +++++++++++-------- .../core/navigation/url/DummyUrlOpener.kt | 4 ++ .../tangem/core/navigation/url/UrlOpener.kt | 2 + .../redirect/model/OnrampRedirectModel.kt | 12 +++++- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/url/CustomTabsUrlOpener.kt b/app/src/main/java/com/tangem/tap/common/url/CustomTabsUrlOpener.kt index 965d6caacd..de65ad7aa7 100644 --- a/app/src/main/java/com/tangem/tap/common/url/CustomTabsUrlOpener.kt +++ b/app/src/main/java/com/tangem/tap/common/url/CustomTabsUrlOpener.kt @@ -3,12 +3,12 @@ package com.tangem.tap.common.url import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.net.Uri import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsClient import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK import androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT +import androidx.core.net.toUri import com.tangem.core.navigation.url.UrlOpener import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder import com.tangem.tap.common.extensions.getColorCompat @@ -25,30 +25,37 @@ internal class CustomTabsUrlOpener : UrlOpener { } } + override fun openUrlExternalBrowser(url: String) { + foregroundActivityObserver.withForegroundActivity { context -> + if (url.isEmpty()) return@withForegroundActivity + val browserIntent = Intent(Intent.ACTION_VIEW, url.toUri()) + context.startActivity(browserIntent) + } + } + private fun openUrl(url: String, context: Context) { if (url.isEmpty()) return - val customTabsIntent = CustomTabsIntent.Builder() - .setDefaultColorSchemeParams( - CustomTabColorSchemeParams.Builder() - .setNavigationBarColor(context.getColorCompat(R.color.toolbarColor)) - .build(), - ) - .setColorScheme( - if (MutableAppThemeModeHolder.isDarkThemeActive) COLOR_SCHEME_DARK else COLOR_SCHEME_LIGHT, - ) - .build() - - customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP - - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + val browserIntent = Intent(Intent.ACTION_VIEW, url.toUri()) runCatching { if (checkCustomTabsAvailability(context, browserIntent)) { context.startActivity(browserIntent) } else { - customTabsIntent.launchUrl(context, Uri.parse(url)) + val customTabsIntent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams( + CustomTabColorSchemeParams.Builder() + .setNavigationBarColor(context.getColorCompat(R.color.toolbarColor)) + .build(), + ) + .setColorScheme( + if (MutableAppThemeModeHolder.isDarkThemeActive) COLOR_SCHEME_DARK else COLOR_SCHEME_LIGHT, + ) + .build() + + customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP + customTabsIntent.launchUrl(context, url.toUri()) } }.onFailure { - Timber.e(it.message) + Timber.e(it) } } diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/url/DummyUrlOpener.kt b/core/navigation/src/main/java/com/tangem/core/navigation/url/DummyUrlOpener.kt index cff4946b40..d47667b064 100644 --- a/core/navigation/src/main/java/com/tangem/core/navigation/url/DummyUrlOpener.kt +++ b/core/navigation/src/main/java/com/tangem/core/navigation/url/DummyUrlOpener.kt @@ -5,4 +5,8 @@ class DummyUrlOpener : UrlOpener { override fun openUrl(url: String) { /* no-op */ } + + override fun openUrlExternalBrowser(url: String) { + /* no-op */ + } } \ No newline at end of file diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/url/UrlOpener.kt b/core/navigation/src/main/java/com/tangem/core/navigation/url/UrlOpener.kt index d78ba1b1eb..df94cdfacf 100644 --- a/core/navigation/src/main/java/com/tangem/core/navigation/url/UrlOpener.kt +++ b/core/navigation/src/main/java/com/tangem/core/navigation/url/UrlOpener.kt @@ -3,4 +3,6 @@ package com.tangem.core.navigation.url interface UrlOpener { fun openUrl(url: String) + + fun openUrlExternalBrowser(url: String) } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt index ce08252b7b..fec988b9d6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt @@ -86,7 +86,13 @@ internal class OnrampRedirectModel @Inject constructor( .onLeft(::handleError) .onRight { latestOnrampTransaction = it - urlOpener.openUrl(it.redirectUrl) + + // Workaround to open Unlimit provider in external browser instead of chrome custom tabs + if (params.onrampProviderWithQuote.provider.id.equals(UNLIMIT_PROVIDER_ID, ignoreCase = true)) { + urlOpener.openUrlExternalBrowser(it.redirectUrl) + } else { + urlOpener.openUrl(it.redirectUrl) + } } } } @@ -127,4 +133,8 @@ internal class OnrampRedirectModel @Inject constructor( messageSender.send(message) } + + private companion object { + const val UNLIMIT_PROVIDER_ID = "unlimit" + } } \ No newline at end of file From 1713cfbf76ff6d95ce9e06ae044c7785907b263f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 16:57:13 +0000 Subject: [PATCH 17/17] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 7ab02a4628..78d485789e 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.27.0-1126" +tangemBlockchainSdk = "develop-1129" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.27.0-510" +tangemCardSdk = "develop-509" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^