Updated on 2026-08-14
This commit is contained in:
parent
74c9f93bd3
commit
8ac903ba80
8 changed files with 200 additions and 68 deletions
|
|
@ -60,6 +60,15 @@ class WalletConnectManager {
|
|||
setupConnectionTimeoutCheck(session)
|
||||
}
|
||||
|
||||
fun updateSession(session: WalletConnectSession) {
|
||||
val updatedSession = sessions[session.session]?.copy(
|
||||
wallet = session.wallet
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupConnectionTimeoutCheck(session: WCSession) {
|
||||
scope.launch {
|
||||
delay(20_000)
|
||||
|
|
@ -81,7 +90,7 @@ class WalletConnectManager {
|
|||
client = WCClient(httpClient = okHttpClient),
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
wallet = session.wallet
|
||||
wallet = session.wallet,
|
||||
)
|
||||
.also {
|
||||
setListeners(it.client)
|
||||
|
|
@ -96,11 +105,12 @@ class WalletConnectManager {
|
|||
val activeData = sessions[session] ?: return
|
||||
removeSimilarSessions(activeData)
|
||||
|
||||
val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey
|
||||
val accounts = listOf(Blockchain.Ethereum.makeAddresses(key).first().value)
|
||||
val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey ?: return
|
||||
val blockchain = activeData.wallet.getBlockchainForSession()
|
||||
val accounts = listOf(blockchain.makeAddresses(key).first().value)
|
||||
val approved = activeData.client.approveSession(
|
||||
accounts = accounts,
|
||||
chainId = activeData.wallet.chainId
|
||||
chainId = blockchain.getChainId() ?: Blockchain.Ethereum.getChainId()!!
|
||||
)
|
||||
if (approved) {
|
||||
val walletConnectSession = WalletConnectSession(
|
||||
|
|
@ -119,7 +129,7 @@ class WalletConnectManager {
|
|||
|
||||
fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
val sessionsToRemove = sessions.filter {
|
||||
it.value.wallet.walletPublicKey == activeData.wallet.walletPublicKey
|
||||
it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true
|
||||
&& it.value.peerMeta?.url == activeData.peerMeta?.url
|
||||
&& it.value.session != activeData.session
|
||||
}
|
||||
|
|
@ -246,7 +256,9 @@ class WalletConnectManager {
|
|||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession(
|
||||
sessionData))
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull()
|
||||
))
|
||||
}
|
||||
FirebaseAnalyticsHandler.logWcEvent(
|
||||
FirebaseAnalyticsHandler.WcAnalyticsEvent.Session(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
||||
class WalletConnectNetworkUtils {
|
||||
|
||||
companion object {
|
||||
|
||||
fun parseBlockchain(
|
||||
chainId: Int?,
|
||||
peer: WCPeerMeta,
|
||||
isTestNet: Boolean? = null
|
||||
): Blockchain? {
|
||||
return when {
|
||||
chainId != null -> {
|
||||
Blockchain.fromChainId(chainId)
|
||||
}
|
||||
peer.url.contains("matic.network") || peer.name == "Polygon" -> {
|
||||
Blockchain.Polygon
|
||||
}
|
||||
peer.url.contains("binance.org") || peer.name.contains("Binance") -> {
|
||||
if (peer.icons.firstOrNull()?.contains("testnet") == true) {
|
||||
Blockchain.BinanceTestnet
|
||||
} else {
|
||||
Blockchain.Binance
|
||||
}
|
||||
}
|
||||
peer.name.contains("BSC") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
else -> {
|
||||
if (isTestNet == true) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,6 @@ data class SessionDao(
|
|||
session = session.session,
|
||||
peerMeta = session.peerMeta
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,17 +35,16 @@ class WalletConnectSdkHelper {
|
|||
): WcTransactionData? {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val publicKey = Wallet.PublicKey(
|
||||
session.wallet.walletPublicKey,
|
||||
session.wallet.walletPublicKey ?: return null,
|
||||
session.wallet.derivedPublicKey,
|
||||
session.wallet.derivationPath,
|
||||
)
|
||||
val walletManager = factory.makeEthereumWalletManager(
|
||||
session.wallet.cardId,
|
||||
publicKey,
|
||||
emptyList(),
|
||||
isTestNet = session.wallet.isTestNet
|
||||
) ?: return null
|
||||
|
||||
val blockchain = session.wallet.getBlockchainForSession()
|
||||
val walletManager = factory.makeWalletManager(
|
||||
session.wallet.cardId,
|
||||
blockchain,
|
||||
publicKey
|
||||
) ?: return null
|
||||
|
||||
try {
|
||||
walletManager.update()
|
||||
|
|
@ -54,8 +53,6 @@ class WalletConnectSdkHelper {
|
|||
return null
|
||||
}
|
||||
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
|
||||
val balance =
|
||||
walletManager.wallet.amounts[AmountType.Coin]?.value ?: return null
|
||||
|
||||
|
|
@ -213,12 +210,12 @@ class WalletConnectSdkHelper {
|
|||
|
||||
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
|
||||
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey
|
||||
val command = SignHashCommand(hashToSign, wallet.walletPublicKey, wallet.derivationPath)
|
||||
val command = SignHashCommand(hashToSign, wallet.walletPublicKey!!, wallet.derivationPath)
|
||||
return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
val hash = result.data.signature
|
||||
return EthereumUtils.prepareSignedMessageData(
|
||||
hash, hashToSign, CryptoUtils.decompressPublicKey(key)
|
||||
hash, hashToSign, CryptoUtils.decompressPublicKey(key!!)
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect
|
|||
|
||||
import android.app.Activity
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
|
|
@ -27,13 +28,16 @@ sealed class WalletConnectAction : Action {
|
|||
data class OpenSession(
|
||||
val wcUri: String,
|
||||
val wallet: WalletForSession,
|
||||
val scanResponse: ScanResponse
|
||||
) : WalletConnectAction()
|
||||
|
||||
object RefuseOpeningSession : WalletConnectAction()
|
||||
|
||||
data class OpeningSessionTimeout(val session: WCSession) : WalletConnectAction()
|
||||
|
||||
data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction()
|
||||
data class AcceptOpeningSession(
|
||||
val session: WalletConnectSession, val chainId: Int?,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class ApproveSession(
|
||||
val session: WCSession,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
|
|
@ -14,9 +13,12 @@ import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
|||
import com.tangem.tap.domain.isMultiwalletAllowed
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class WalletConnectMiddleware {
|
||||
private val walletConnectManager = WalletConnectManager()
|
||||
|
|
@ -48,8 +50,13 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
|
||||
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ClipboardOrScanQr(
|
||||
action.wcUri)))
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ClipboardOrScanQr(
|
||||
action.wcUri
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is WalletConnectAction.ScanCard -> {
|
||||
|
|
@ -78,14 +85,23 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
|
||||
is WalletConnectAction.RefuseOpeningSession -> {
|
||||
store.dispatch(GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.OpeningSessionRejected
|
||||
))
|
||||
store.dispatch(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.OpeningSessionRejected
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is WalletConnectAction.AcceptOpeningSession -> {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ApproveWcSession(
|
||||
action.session)))
|
||||
generateWalletKeys(action.session, action.chainId)
|
||||
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ApproveWcSession(
|
||||
action.session
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is WalletConnectAction.ApproveSession -> {
|
||||
|
|
@ -134,60 +150,79 @@ class WalletConnectMiddleware {
|
|||
return@ScanCard
|
||||
}
|
||||
|
||||
val walletManager = getWalletManager(scanResponse).guard {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return@ScanCard
|
||||
}
|
||||
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey = if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(
|
||||
wcUri = wcUri,
|
||||
wallet = WalletForSession(
|
||||
card.cardId,
|
||||
wallet.publicKey.seedKey,
|
||||
derivedKey,
|
||||
wallet.publicKey.derivationPath,
|
||||
card.isTestCard
|
||||
),
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.OpenSession(
|
||||
wcUri = wcUri,
|
||||
wallet = WalletForSession(
|
||||
scanResponse.card.cardId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
scanResponse.card.isTestCard
|
||||
),
|
||||
scanResponse = scanResponse
|
||||
)
|
||||
)
|
||||
}, {
|
||||
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
|
||||
}, R.string.wallet_connect_scan_card_message))
|
||||
}
|
||||
|
||||
private fun getWalletManager(scanResponse: ScanResponse): WalletManager? {
|
||||
private fun generateWalletKeys(session: WalletConnectSession, chainId: Int?) {
|
||||
val scanResponse = store.state.walletConnectState.scanResponse ?: return
|
||||
val walletManager = getWalletManager(scanResponse, session.peerMeta, chainId) ?: return
|
||||
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey =
|
||||
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
val updatedWallet = session.wallet.copy(
|
||||
walletPublicKey = wallet.publicKey.seedKey,
|
||||
derivedPublicKey = derivedKey,
|
||||
derivationPath = wallet.publicKey.derivationPath,
|
||||
blockchain = wallet.blockchain
|
||||
)
|
||||
val updatedSession = session.copy(wallet = updatedWallet)
|
||||
walletConnectManager.updateSession(updatedSession)
|
||||
}
|
||||
|
||||
private fun getWalletManager(
|
||||
scanResponse: ScanResponse, peer: WCPeerMeta, chainId: Int?
|
||||
): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
|
||||
val wcBlockchain = if (scanResponse.card.isTestCard) {
|
||||
Blockchain.EthereumTestnet
|
||||
} else {
|
||||
Blockchain.Ethereum
|
||||
}
|
||||
val blockchain = WalletConnectNetworkUtils.parseBlockchain(
|
||||
chainId = chainId, peer = peer, isTestNet = scanResponse.card.isTestCard
|
||||
) ?: if (scanResponse.card.isTestCard) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||
|
||||
Timber.d(blockchain.fullName)
|
||||
|
||||
|
||||
return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
|
||||
store.state.walletState.getWalletManager(wcBlockchain)
|
||||
?: factory.makeWalletManagerForApp(scanResponse, wcBlockchain)
|
||||
store.state.walletState.getWalletManager(blockchain)
|
||||
?: factory.makeWalletManagerForApp(scanResponse, blockchain)
|
||||
?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
|
||||
}
|
||||
} else {
|
||||
val walletManager = factory.makeWalletManagerForApp(scanResponse, blockchain)
|
||||
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
|
||||
wcBlockchain) == true
|
||||
blockchain
|
||||
) != true
|
||||
) {
|
||||
factory.makeWalletManagerForApp(scanResponse, wcBlockchain)
|
||||
} else {
|
||||
factory.makeWalletManagerForApp(scanResponse, wcBlockchain)
|
||||
?.also {
|
||||
currenciesRepository.saveAddedBlockchain(card.cardId, wcBlockchain)
|
||||
}
|
||||
walletManager?.let {
|
||||
currenciesRepository.saveAddedBlockchain(
|
||||
card.cardId,
|
||||
blockchain
|
||||
)
|
||||
}
|
||||
}
|
||||
return walletManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,9 @@ class WalletConnectReducer {
|
|||
sessions = state.sessions + action.session
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.OpenSession -> {
|
||||
state.copy(scanResponse = action.scanResponse)
|
||||
}
|
||||
is WalletConnectAction.SetSessionsRestored ->
|
||||
WalletConnectState(sessions = action.sessions)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
|
@ -13,6 +15,7 @@ import com.trustwallet.walletconnect.models.session.WCSession
|
|||
data class WalletConnectState(
|
||||
val loading: Boolean = false,
|
||||
val sessions: List<WalletConnectSession> = listOf(),
|
||||
val scanResponse: ScanResponse? = null
|
||||
)
|
||||
|
||||
data class WalletConnectSession(
|
||||
|
|
@ -21,18 +24,58 @@ data class WalletConnectSession(
|
|||
val wallet: WalletForSession,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta,
|
||||
)
|
||||
) {
|
||||
fun getAddress(): String? {
|
||||
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey ?: return null
|
||||
return wallet.blockchain?.makeAddresses(key)?.first()?.value
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class WalletForSession(
|
||||
val cardId: String,
|
||||
val walletPublicKey: ByteArray,
|
||||
val walletPublicKey: ByteArray?,
|
||||
val derivedPublicKey: ByteArray?,
|
||||
val derivationPath: DerivationPath?,
|
||||
val isTestNet: Boolean = false,
|
||||
val blockchain: Blockchain? = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||
) {
|
||||
val chainId
|
||||
get() = if (isTestNet) 4 else 1
|
||||
|
||||
fun getBlockchainForSession(): Blockchain {
|
||||
return blockchain ?: if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as WalletForSession
|
||||
|
||||
if (cardId != other.cardId) return false
|
||||
if (walletPublicKey != null) {
|
||||
if (other.walletPublicKey == null) return false
|
||||
if (!walletPublicKey.contentEquals(other.walletPublicKey)) return false
|
||||
} else if (other.walletPublicKey != null) return false
|
||||
if (derivedPublicKey != null) {
|
||||
if (other.derivedPublicKey == null) return false
|
||||
if (!derivedPublicKey.contentEquals(other.derivedPublicKey)) return false
|
||||
} else if (other.derivedPublicKey != null) return false
|
||||
if (derivationPath != other.derivationPath) return false
|
||||
if (isTestNet != other.isTestNet) return false
|
||||
if (blockchain != other.blockchain) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = cardId.hashCode()
|
||||
result = 31 * result + (walletPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (derivationPath?.hashCode() ?: 0)
|
||||
result = 31 * result + isTestNet.hashCode()
|
||||
result = 31 * result + (blockchain?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue