Updated on 2026-08-14
This commit is contained in:
commit
83ddf228a1
24 changed files with 465 additions and 53 deletions
|
|
@ -28,6 +28,8 @@ fun Blockchain.getRoundIconRes(): Int {
|
|||
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_round
|
||||
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_round
|
||||
Blockchain.Gnosis -> R.drawable.ic_gnosis_round
|
||||
Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> R.drawable.ic_ethereumpow_round
|
||||
Blockchain.EthereumFair -> R.drawable.ic_ethereumfair_round
|
||||
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_round
|
||||
Blockchain.Kusama -> R.drawable.ic_kusama_round
|
||||
Blockchain.Optimism, Blockchain.OptimismTestnet -> R.drawable.ic_optimism
|
||||
|
|
@ -59,6 +61,8 @@ fun Blockchain.getGreyedOutIconRes(): Int {
|
|||
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_no_color
|
||||
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color
|
||||
Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color
|
||||
Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> R.drawable.ic_ethereumpow_no_color
|
||||
Blockchain.EthereumFair -> R.drawable.ic_ethereumfair_no_color
|
||||
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_no_color
|
||||
Blockchain.Kusama -> R.drawable.ic_kusama_no_color
|
||||
Blockchain.Optimism, Blockchain.OptimismTestnet -> R.drawable.ic_optimism_no_color
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ sealed class TapError(
|
|||
|
||||
data class UnsupportedState(
|
||||
val stateError: String,
|
||||
val customMessage: String = "Unsupported state:"
|
||||
val customMessage: String = "Unsupported state:",
|
||||
) : TapError(R.string.common_custom_string, listOf("$customMessage $stateError"))
|
||||
|
||||
sealed class WalletManager {
|
||||
|
|
@ -53,9 +53,14 @@ sealed class TapError(
|
|||
object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
|
||||
}
|
||||
|
||||
sealed class WalletConnect {
|
||||
object UnsupportedDapp : TapError(R.string.wallet_connect_error_unsupported_dapp)
|
||||
object UnsupportedLink : TapError(R.string.wallet_connect_error_failed_to_connect)
|
||||
}
|
||||
|
||||
data class ValidateTransactionErrors(
|
||||
override val errorList: List<TapError>,
|
||||
override val builder: (List<String>) -> String
|
||||
override val builder: (List<String>) -> String,
|
||||
) : TapError(-1), MultiMessageError
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.domain.common.ScanResponse
|
|||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.walletconnect.extensions.isDappSupported
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
|
|
@ -57,7 +59,15 @@ class WalletConnectManager {
|
|||
private var sessions: MutableMap<WCSession, WalletConnectActiveData> = mutableMapOf()
|
||||
|
||||
fun connect(wcUri: String) {
|
||||
val session = WCSession.from(wcUri) ?: return
|
||||
val session = WCSession.from(wcUri).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
if (sessions[session] != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession)
|
||||
return
|
||||
|
|
@ -65,7 +75,19 @@ class WalletConnectManager {
|
|||
val client = WCClient(httpClient = okHttpClient)
|
||||
setListeners(client)
|
||||
val peerId = UUID.randomUUID().toString()
|
||||
client.connect(session, tangemPeerMeta, peerId)
|
||||
|
||||
try {
|
||||
client.connect(session, tangemPeerMeta, peerId)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
sessions[session] = WalletConnectActiveData(
|
||||
peerId = peerId,
|
||||
remotePeerId = null,
|
||||
|
|
@ -331,21 +353,30 @@ class WalletConnectManager {
|
|||
val session = client.session
|
||||
val data = sessions[session]?.copy(peerMeta = peer, remotePeerId = client.remotePeerId)
|
||||
if (data != null && session != null) {
|
||||
sessions[session] = data
|
||||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
if (!peer.isDappSupported()) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ScanCard(
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull()
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = session,
|
||||
error = TapError.WalletConnect.UnsupportedDapp,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
sessions[session] = data
|
||||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ScanCard(
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull(),
|
||||
),
|
||||
)
|
||||
}
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Session(
|
||||
Analytics.WcSessionEvent.Connect, peer.url,
|
||||
),
|
||||
)
|
||||
}
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Session(
|
||||
Analytics.WcSessionEvent.Connect, peer.url
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onSessionUpdate = { id: Long, update: WCSessionUpdate ->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.tap.domain.walletconnect.extensions
|
||||
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
||||
fun WCPeerMeta.isDappSupported(): Boolean {
|
||||
return !unsupportedDappsList.any { this.url.contains(it) }
|
||||
}
|
||||
|
||||
private val unsupportedDappsList: List<String> = listOf("dydx.exchange")
|
||||
|
|
@ -448,6 +448,33 @@ class DemoConfig {
|
|||
"AB02000000049533",
|
||||
"AB02000000049541",
|
||||
"AB02000000049830",
|
||||
// === more cids ===
|
||||
"AC03000000091418",
|
||||
"AC03000000091400",
|
||||
"AC03000000099007",
|
||||
"AC03000000098991",
|
||||
"AC03000000098942",
|
||||
"AC03000000091715",
|
||||
"AC03000000091301",
|
||||
"AC03000000091343",
|
||||
"AB01000000055705",
|
||||
"AB01000000052918",
|
||||
"AB01000000047710",
|
||||
"AB01000000052306",
|
||||
"AB01000000047645",
|
||||
"AB01000000048957",
|
||||
"AB01000000052900",
|
||||
"AB01000000050391",
|
||||
"AB01000000047363",
|
||||
"AB02000000053998",
|
||||
"AB02000000019809",
|
||||
"AB02000000020872",
|
||||
"AB02000000022027",
|
||||
"AB02000000058955",
|
||||
"AB02000000053253",
|
||||
"AB02000000048063",
|
||||
"AB02000000023736",
|
||||
"AB02000000058187",
|
||||
)
|
||||
|
||||
private val testDemoCardIds = listOf(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.details.redux.walletconnect
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
|
|
@ -52,7 +53,7 @@ sealed class WalletConnectAction : Action {
|
|||
val updatedSession: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction()
|
||||
data class FailureEstablishingSession(val session: WCSession?, val error: TapError? = null) : WalletConnectAction()
|
||||
data class SetSessionsRestored(val sessions: List<WalletConnectSession>) :
|
||||
WalletConnectAction()
|
||||
|
||||
|
|
@ -103,6 +104,6 @@ sealed class WalletConnectAction : Action {
|
|||
|
||||
data class Sign(
|
||||
val id: Long, val data: ByteArray, val sessionData: WCSession,
|
||||
) : WalletConnectAction()
|
||||
) : WalletConnectAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.common.ScanResponse
|
|||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -22,6 +23,7 @@ import com.tangem.tap.features.demo.DemoHelper
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -96,6 +98,16 @@ class WalletConnectMiddleware {
|
|||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout))
|
||||
}
|
||||
is WalletConnectAction.FailureEstablishingSession -> {
|
||||
if (action.error != null) {
|
||||
store.dispatch(
|
||||
GlobalAction.ShowDialog(
|
||||
AppDialog.SimpleOkDialogRes(
|
||||
headerId = R.string.common_warning,
|
||||
messageId = action.error.messageResource,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
if (action.session != null) {
|
||||
walletConnectManager.disconnect(action.session)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
|
|
@ -86,7 +87,7 @@ fun WalletConnectDetailsItem(
|
|||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(84.dp)
|
||||
.defaultMinSize(minHeight = 84.dp)
|
||||
.fillMaxWidth()
|
||||
.clickable { onItemsClick(SettingsElement.WalletConnect) },
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
|
|
@ -99,7 +100,7 @@ fun WalletConnectDetailsItem(
|
|||
tint = colorResource(id = R.color.all_colors_azure),
|
||||
)
|
||||
Column(
|
||||
modifier = modifier.height(56.dp),
|
||||
modifier = modifier.defaultMinSize(minHeight = 56.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -89,11 +91,14 @@ fun ResetCardView(
|
|||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
|
||||
Spacer(modifier = modifier.size(44.dp))
|
||||
Spacer(modifier = modifier.size(28.dp))
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 20.dp),
|
||||
.clickable(
|
||||
onClick = { state.onAcceptWarningToggleClick(!state.accepted) },
|
||||
)
|
||||
.padding(top = 16.dp, bottom = 16.dp),
|
||||
) {
|
||||
IconToggleButton(
|
||||
checked = state.accepted,
|
||||
|
|
@ -120,13 +125,15 @@ fun ResetCardView(
|
|||
text = stringResource(id = R.string.reset_card_to_factory_warning_message),
|
||||
style = TangemTypography.body2,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
modifier = modifier
|
||||
.padding(end = 20.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = modifier.size(32.dp))
|
||||
Spacer(modifier = modifier.size(16.dp))
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(start = 16.dp, end = 16.dp),
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 32.dp),
|
||||
) {
|
||||
DetailsMainButton(
|
||||
title = stringResource(id = R.string.reset_card_to_factory_button_title),
|
||||
|
|
|
|||
|
|
@ -140,13 +140,12 @@ private fun WalletConnectSessions(
|
|||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = session.description,
|
||||
style = TangemTypography.subtitle1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = session.description,
|
||||
style = TangemTypography.subtitle1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
modifier = modifier.weight(1f),
|
||||
)
|
||||
IconButton(
|
||||
onClick = { state.onRemoveSession(session.sessionId) },
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -193,21 +193,28 @@ data class WalletState(
|
|||
|
||||
fun removeWallet(walletData: WalletData?): WalletState {
|
||||
if (walletData == null) return this
|
||||
return if (walletData.currency is Currency.Blockchain) {
|
||||
val walletStores = wallets.filterNot {
|
||||
it.blockchainNetwork.blockchain == walletData.currency.blockchain
|
||||
&& it.blockchainNetwork.derivationPath == walletData.currency.derivationPath
|
||||
return when (val currency = walletData.currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val walletStores = wallets.filterNot {
|
||||
it.blockchainNetwork.blockchain == currency.blockchain
|
||||
&& it.blockchainNetwork.derivationPath == currency.derivationPath
|
||||
}
|
||||
copy(wallets = walletStores)
|
||||
.updateTotalBalance()
|
||||
.updateProgressState()
|
||||
}
|
||||
is Currency.Token -> {
|
||||
val walletStore = getWalletStore(walletData.currency)
|
||||
val walletDataList = walletStore?.walletsData
|
||||
?.filterNot { it.currency == walletData.currency }
|
||||
?: emptyList()
|
||||
val updatedWalletManager = walletStore?.walletManager?.also { it.removeToken(currency.token) }
|
||||
val updatedWalletStore = walletStore?.copy(
|
||||
walletsData = walletDataList,
|
||||
walletManager = updatedWalletManager,
|
||||
)
|
||||
updateWalletStore(updatedWalletStore)
|
||||
}
|
||||
copy(wallets = walletStores)
|
||||
.updateTotalBalance()
|
||||
.updateProgressState()
|
||||
} else {
|
||||
val walletStore = getWalletStore(walletData.currency)
|
||||
val walletDataList = walletStore?.walletsData
|
||||
?.filterNot { it.currency == walletData.currency }
|
||||
?: emptyList()
|
||||
val updatedWalletStore = walletStore?.copy(walletsData = walletDataList)
|
||||
updateWalletStore(updatedWalletStore)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,8 +110,12 @@ class WalletAdapter
|
|||
lContent.tvExchangeRate.text = wallet.fiatRateString
|
||||
?: root.getString(id = R.string.token_item_no_rate)
|
||||
|
||||
cardWallet.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet))
|
||||
if (wallet.walletAddresses != null) {
|
||||
cardWallet.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet))
|
||||
}
|
||||
} else {
|
||||
cardWallet.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue