diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 7c74264429..7dce9c8061 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -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( diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt new file mode 100644 index 0000000000..4907e8d40c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt @@ -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 + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt index a4654d6429..67dab3645d 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt @@ -88,7 +88,6 @@ data class SessionDao( session = session.session, peerMeta = session.peerMeta ) - } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 85ef0eaac6..35a1789f89 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -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 -> { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 16a3f638a4..1c19721743 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 2c1aab1596..06043d4df2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -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 } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt index c8b6f3b1b7..c96f72fc9e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index 2757d8cea9..e2713b92d1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -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 = 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 + } }