Updated on 2026-08-14
This commit is contained in:
parent
3e5bf1d8c3
commit
afe5ec2c57
23 changed files with 23 additions and 1383 deletions
|
|
@ -42,7 +42,6 @@ configurations.all {
|
|||
|
||||
|
||||
dependencies {
|
||||
implementation(files("libs/walletconnect-1.5.6.aar"))
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.core)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ import com.tangem.tap.common.redux.appReducer
|
|||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.tasks.product.DerivationsFinder
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
|
|
@ -76,7 +75,6 @@ lateinit var store: Store<AppState>
|
|||
|
||||
lateinit var foregroundActivityObserver: ForegroundActivityObserver
|
||||
lateinit var activityResultCaller: ActivityResultCaller
|
||||
lateinit var walletConnectRepository: WalletConnectRepository
|
||||
internal lateinit var derivationsFinder: DerivationsFinder
|
||||
|
||||
@HiltAndroidApp
|
||||
|
|
@ -191,8 +189,6 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
activityResultCaller = foregroundActivityObserver
|
||||
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
|
||||
|
||||
walletConnectRepository = WalletConnectRepository(this)
|
||||
|
||||
// TODO: Try to performance and user experience.
|
||||
// [REDACTED_JIRA]
|
||||
runBlocking {
|
||||
|
|
|
|||
|
|
@ -86,10 +86,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
context = context,
|
||||
)
|
||||
}
|
||||
is WalletConnectDialog.ApproveWcSession ->
|
||||
ApproveWcSessionDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ChooseNetwork ->
|
||||
ChooseNetworkDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ClipboardOrScanQr ->
|
||||
ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
|
||||
is WalletConnectDialog.RequestTransaction -> TransactionDialog.create(state.dialog.data, context)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
|
|
@ -58,9 +57,7 @@ class TapWalletManager(
|
|||
// Order is important
|
||||
store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer()))
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
store.dispatch(WalletConnectAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
|
||||
store.dispatch(WalletConnectAction.RestoreSessions(scanResponse))
|
||||
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@ import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTrans
|
|||
import com.tangem.tap.domain.walletconnect2.domain.models.binance.tradeOrderSerializer
|
||||
import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.TradeData
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import timber.log.Timber
|
||||
|
||||
internal object BnbHelper {
|
||||
|
||||
fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
fun createMessageData(order: WcBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
val input = order.msgs.first().inputs.first()
|
||||
val output = order.msgs.first().inputs.first()
|
||||
|
||||
|
|
@ -42,51 +40,7 @@ internal object BnbHelper {
|
|||
)
|
||||
}
|
||||
|
||||
fun WcBinanceTradeOrder.toWCBinanceTradeOrder(): WCBinanceTradeOrder {
|
||||
return WCBinanceTradeOrder(
|
||||
account_number = accountNumber,
|
||||
chain_id = chainId,
|
||||
data = data,
|
||||
memo = memo,
|
||||
sequence = sequence,
|
||||
source = source,
|
||||
msgs = msgs.map { it.toWCBinanceTradeOrderMessage() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTradeOrder.Message.toWCBinanceTradeOrderMessage(): WCBinanceTradeOrder.Message {
|
||||
return WCBinanceTradeOrder.Message(id, orderType, price, quantity, sender, side, symbol, timeInforce)
|
||||
}
|
||||
|
||||
fun WcBinanceTransferOrder.toWCBinanceTransferOrder(): WCBinanceTransferOrder {
|
||||
return WCBinanceTransferOrder(
|
||||
account_number = accountNumber,
|
||||
chain_id = chainId,
|
||||
data = data,
|
||||
memo = memo,
|
||||
sequence = sequence,
|
||||
source = source,
|
||||
msgs = msgs.map { it.toWCBinanceTransferOrderMessage() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTransferOrder.Message.toWCBinanceTransferOrderMessage(): WCBinanceTransferOrder.Message {
|
||||
return WCBinanceTransferOrder.Message(
|
||||
inputs.map { it.toWCBinanceItem() },
|
||||
outputs.map { it.toWCBinanceItem() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTransferOrder.Message.Item.toWCBinanceItem(): WCBinanceTransferOrder.Message.Item {
|
||||
return WCBinanceTransferOrder.Message.Item(
|
||||
address,
|
||||
coins.map {
|
||||
WCBinanceTransferOrder.Message.Item.Coin(it.amount, it.denom)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
fun createMessageData(order: WcBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
val address = order.msgs.first().sender
|
||||
|
||||
val tradeData = order.msgs.map {
|
||||
|
|
|
|||
|
|
@ -1,562 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
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.domain.walletconnect.extensions.toWcEthereumSignMessage
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.*
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.walletConnectRepository
|
||||
import com.trustwallet.walletconnect.WCClient
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceCancelOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTxConfirmParam
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import com.trustwallet.walletconnect.models.session.WCAddNetwork
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import com.trustwallet.walletconnect.models.session.WCSessionUpdate
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.set
|
||||
|
||||
@Suppress("LargeClass")
|
||||
internal class WalletConnectManager {
|
||||
|
||||
private var cardId: String? = null
|
||||
|
||||
private val okHttpClient: OkHttpClient by lazy {
|
||||
OkHttpClient.Builder()
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(20, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.addInterceptor(interceptor)
|
||||
.addInterceptor(RetryInterceptor())
|
||||
.build()
|
||||
}
|
||||
|
||||
private val interceptor by lazy {
|
||||
createNetworkLoggingInterceptor()
|
||||
}
|
||||
|
||||
private var sessions: MutableMap<Topic, WalletConnectActiveData> = mutableMapOf()
|
||||
|
||||
fun connect(wcUri: String) {
|
||||
val session = WCSession.from(wcUri).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
if (sessions[session.topic] != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession)
|
||||
return
|
||||
}
|
||||
val client = WCClient(httpClient = okHttpClient)
|
||||
setListeners(client)
|
||||
val peerId = UUID.randomUUID().toString()
|
||||
|
||||
try {
|
||||
client.connect(session, tangemPeerMeta, peerId)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
sessions[session.topic] = WalletConnectActiveData(
|
||||
peerId = peerId,
|
||||
remotePeerId = null,
|
||||
session = session,
|
||||
client = client,
|
||||
wallet = WalletForSession(),
|
||||
)
|
||||
setupConnectionTimeoutCheck(session)
|
||||
}
|
||||
|
||||
fun updateSession(session: WalletConnectSession) {
|
||||
val updatedSession = sessions[session.session.topic]?.copy(
|
||||
wallet = session.wallet,
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session.topic] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
fun updateBlockchain(session: WalletConnectSession) {
|
||||
sessions[session.session.topic]?.client?.updateSession(
|
||||
accounts = listOfNotNull(session.getAddress()),
|
||||
chainId = session.wallet.blockchain?.getChainId(),
|
||||
approved = true,
|
||||
)
|
||||
|
||||
val updatedSession = sessions[session.session.topic]?.copy(
|
||||
wallet = session.wallet,
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session.topic] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun setupConnectionTimeoutCheck(session: WCSession) {
|
||||
scope.launch {
|
||||
delay(20_000)
|
||||
val data = sessions[session.topic]
|
||||
if (data != null && data.peerMeta == null) {
|
||||
disconnect(session)
|
||||
store.dispatchOnMain(WalletConnectAction.OpeningSessionTimeout(session))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreSessions(scanResponse: ScanResponse) {
|
||||
val walletPublicKey = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }?.publicKey
|
||||
?: return
|
||||
if (scanResponse.card.backupStatus?.isActive != true) cardId = scanResponse.card.cardId
|
||||
val sessions = walletConnectRepository.loadSavedSessions()
|
||||
// filter sessions for this particular card
|
||||
.filter { it.wallet.walletPublicKey.contentEquals(walletPublicKey) }
|
||||
this.sessions = sessions
|
||||
.map { session ->
|
||||
WalletConnectActiveData(
|
||||
peerId = session.peerId,
|
||||
remotePeerId = session.remotePeerId,
|
||||
client = WCClient(httpClient = okHttpClient),
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
wallet = session.wallet,
|
||||
)
|
||||
.also {
|
||||
setListeners(it.client)
|
||||
it.client.connect(it.session, tangemPeerMeta, it.peerId, it.remotePeerId)
|
||||
}
|
||||
}.associateBy { it.session.topic }.toMutableMap()
|
||||
|
||||
store.dispatchOnMain(WalletConnectAction.SetSessionsRestored(sessions))
|
||||
}
|
||||
|
||||
fun approve(session: WCSession) {
|
||||
val activeData = sessions[session.topic] ?: return
|
||||
removeSimilarSessions(activeData)
|
||||
|
||||
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 = blockchain.getChainId() ?: Blockchain.Ethereum.getChainId()!!,
|
||||
)
|
||||
if (approved) {
|
||||
val walletConnectSession = WalletConnectSession(
|
||||
peerId = activeData.peerId,
|
||||
remotePeerId = activeData.remotePeerId,
|
||||
wallet = activeData.wallet,
|
||||
session = session,
|
||||
peerMeta = activeData.peerMeta!!,
|
||||
)
|
||||
walletConnectRepository.saveSession(walletConnectSession)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ApproveSession.Success(
|
||||
walletConnectSession,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
val sessionsToRemove = sessions.filter {
|
||||
it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true &&
|
||||
it.value.peerMeta?.url == activeData.peerMeta?.url &&
|
||||
it.value.session != activeData.session
|
||||
}
|
||||
Timber.d("RemoveSimilarSessions: ${sessionsToRemove.values.map { it.client.session }}")
|
||||
sessionsToRemove.forEach { disconnect(it.value.session) }
|
||||
}
|
||||
|
||||
fun rejectRequest(topic: String, id: Long) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.rejectRequest(id)
|
||||
}
|
||||
|
||||
private fun acceptRequest(topic: String, id: Long, data: String) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.approveRequest(id, data)
|
||||
}
|
||||
|
||||
fun disconnect(session: WCSession) {
|
||||
val activeData = sessions[session.topic] ?: return
|
||||
val disconnected = if (activeData.client.isConnected) {
|
||||
activeData.client.killSession()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
if (disconnected) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSessionClosed(session: WCSession) {
|
||||
sessions.remove(session.topic)
|
||||
walletConnectRepository.removeSession(session)
|
||||
store.dispatchOnMain(WalletConnectAction.RemoveSession(session))
|
||||
}
|
||||
|
||||
fun handleTransactionRequest(
|
||||
transaction: WcEthereumTransaction,
|
||||
session: WalletConnectSession,
|
||||
id: Long,
|
||||
type: WcEthTransactionType,
|
||||
) {
|
||||
val activeData = sessions[session.session.topic] ?: return
|
||||
scope.launch {
|
||||
val data = WalletConnectSdkHelper().prepareTransactionData(
|
||||
EthTransactionData(
|
||||
transaction = transaction,
|
||||
networkId = session.wallet.blockchain?.toNetworkId() ?: "",
|
||||
rawDerivationPath = session.wallet.derivationPath?.rawPath,
|
||||
id = id,
|
||||
topic = session.session.topic,
|
||||
type = type,
|
||||
metaName = session.peerMeta.name,
|
||||
metaUrl = session.peerMeta.url,
|
||||
),
|
||||
).guard {
|
||||
sessions[session.session.topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
session.session.topic,
|
||||
id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[session.session.topic] = activeData.copy(transactionData = data)
|
||||
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.RequestTransaction(
|
||||
WcPreparedRequest.EthTransaction(
|
||||
preparedRequestData = data,
|
||||
topic = session.session.topic,
|
||||
requestId = id,
|
||||
derivationPath = data.walletManager.wallet.publicKey.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun completeTransaction(topic: Topic) {
|
||||
val activeData = sessions[topic]
|
||||
val data = activeData?.transactionData ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().completeTransaction(data, cardId).guard {
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(topic, data.id, hash)
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
fun signBnb(id: Long, data: ByteArray, topic: Topic) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signBnbTransaction(
|
||||
data = data,
|
||||
networkId = activeData.wallet.blockchain?.toNetworkId() ?: "",
|
||||
derivationPath = activeData.wallet.derivationPath?.rawPath,
|
||||
cardId = cardId,
|
||||
).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(topic, id, hash)
|
||||
}
|
||||
}
|
||||
|
||||
fun handlePersonalSignRequest(message: WCEthereumSignMessage, session: WalletConnectSession, id: Long) {
|
||||
val activeData = sessions[session.session.topic] ?: return
|
||||
scope.launch {
|
||||
val data = WalletConnectSdkHelper().prepareDataForPersonalSign(
|
||||
message = message.toWcEthereumSignMessage(),
|
||||
topic = session.session.topic,
|
||||
id = id,
|
||||
metaName = session.peerMeta.name,
|
||||
)
|
||||
sessions[session.session.topic] = activeData.copy(personalSignData = data)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.PersonalSign(
|
||||
WcPreparedRequest.EthSign(
|
||||
preparedRequestData = data,
|
||||
topic = session.session.topic,
|
||||
requestId = id,
|
||||
derivationPath = session.wallet.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendSignedMessage(topic: Topic) {
|
||||
val activeData = sessions[topic]
|
||||
val data = activeData?.personalSignData ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signPersonalMessage(
|
||||
hashToSign = data.hash,
|
||||
networkId = activeData.wallet.blockchain?.toNetworkId() ?: "",
|
||||
type = data.type,
|
||||
derivationPath = activeData.wallet.derivationPath?.rawPath,
|
||||
cardId = cardId,
|
||||
)
|
||||
.guard {
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[topic] = activeData.copy(personalSignData = data)
|
||||
acceptRequest(topic, data.id, hash)
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
private fun setListeners(client: WCClient) {
|
||||
client.onSessionRequest = { id: Long, peer: WCPeerMeta ->
|
||||
Timber.d("OnSessionRequest: $peer")
|
||||
val session = client.session
|
||||
val data = sessions[session?.topic]?.copy(peerMeta = peer, remotePeerId = client.remotePeerId)
|
||||
if (data != null && session != null) {
|
||||
if (!peer.isDappSupported()) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = session,
|
||||
error = TapError.WalletConnect.UnsupportedDapp,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
sessions[session.topic] = data
|
||||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ScanCard(
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client.onSessionUpdate = { id: Long, update: WCSessionUpdate ->
|
||||
Timber.d("onSessionUpdate: $update")
|
||||
val session = client.session
|
||||
if (session != null && !update.approved) onSessionClosed(session)
|
||||
}
|
||||
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSendTransaction: $transaction")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.SendTransaction
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcEthTransactionType.EthSendTransaction,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSignTransaction: $transaction")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.SignTransaction
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcEthTransactionType.EthSignTransaction,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
|
||||
Timber.d("onEthSign: $message")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.PersonalSign
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandlePersonalSignRequest(
|
||||
message,
|
||||
sessionData,
|
||||
id,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
client.onBnbCancel = { id: Long, order: WCBinanceCancelOrder ->
|
||||
}
|
||||
client.onBnbTrade = { id: Long, order: WCBinanceTradeOrder ->
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Trade(
|
||||
id = id,
|
||||
order = order,
|
||||
sessionData = sessionData,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTransfer = { id: Long, order: WCBinanceTransferOrder ->
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Transfer(
|
||||
id = id,
|
||||
order = order,
|
||||
sessionData = sessionData,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTxConfirm = { id: Long, order: WCBinanceTxConfirmParam ->
|
||||
// send empty approve request if status is OK
|
||||
if (order.ok) client.approveRequest(id, "")
|
||||
}
|
||||
client.onDisconnect = { code: Int, reason: String ->
|
||||
val session = client.session
|
||||
if (session != null) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
client.onWalletChangeNetwork = { id: Long, chainId: Int ->
|
||||
switchChain(chainId, client)
|
||||
}
|
||||
client.onWalletAddNetwork = { id: Long, network: WCAddNetwork ->
|
||||
// TODO
|
||||
// In fact this method is used to add a EVM network. It provides RPC url and chain ID.
|
||||
// Here now it just tries to switch to a EVM network with a provided chain ID.
|
||||
try {
|
||||
val chainId = Integer.decode(network.chainIdHex)
|
||||
switchChain(chainId, client)
|
||||
} catch (exception: Exception) {
|
||||
Timber.d("WC add network error: chain could not be parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchChain(chainId: Int, client: WCClient) {
|
||||
val blockchain = Blockchain.fromChainId(chainId)
|
||||
Timber.d("WC switch chainID\nNew Blockchain: $blockchain")
|
||||
val session = sessions[client.session?.topic]?.toWalletConnectSession()
|
||||
if (session != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.SwitchBlockchain(blockchain, session))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val WC_SCHEME = "wc"
|
||||
|
||||
private val tangemPeerMeta = WCPeerMeta(name = "Tangem Wallet", url = "https://tangem.com")
|
||||
|
||||
fun isCorrectWcUri(string: String): Boolean = WCSession.from(string) != null
|
||||
}
|
||||
}
|
||||
|
||||
internal typealias Topic = String
|
||||
|
||||
internal data class WalletConnectActiveData(
|
||||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val client: WCClient,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta? = null,
|
||||
val wallet: WalletForSession,
|
||||
val transactionData: WcTransactionData? = null,
|
||||
val personalSignData: WcPersonalSignData? = null,
|
||||
) {
|
||||
fun toWalletConnectSession(): WalletConnectSession? {
|
||||
if (peerMeta == null) return null
|
||||
return WalletConnectSession(
|
||||
peerId = peerId,
|
||||
remotePeerId = remotePeerId,
|
||||
wallet = wallet,
|
||||
session = session,
|
||||
peerMeta = peerMeta,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal class RetryInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request: Request = chain.request()
|
||||
val response = chain.proceed(request)
|
||||
when (response.code) {
|
||||
502 -> {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
||||
object WalletConnectNetworkUtils {
|
||||
fun parseBlockchain(chainId: Int?, peer: WCPeerMeta): Blockchain? {
|
||||
return when {
|
||||
peer.url.contains("pancakeswap.finance") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
peer.url.contains("optimism") -> {
|
||||
Blockchain.Optimism
|
||||
}
|
||||
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
|
||||
}
|
||||
peer.url.contains("honeyswap.1hive.eth.limo") -> {
|
||||
// Check if something's changed after this bug report:
|
||||
// https://github.com/1Hive/honeyswap-interface/issues/83
|
||||
Blockchain.Gnosis
|
||||
}
|
||||
else -> {
|
||||
Blockchain.Ethereum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import timber.log.Timber
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class WalletConnectRepository(val context: Application) {
|
||||
private val walletConnectAdapter: JsonAdapter<List<SessionDao>> = MoshiConverter.sdkMoshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, SessionDao::class.java),
|
||||
)
|
||||
|
||||
fun saveSession(session: WalletConnectSession) {
|
||||
val sessions = loadSavedSessions() + session
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun removeSession(session: WCSession) {
|
||||
val sessions = loadSavedSessions().filterNot { it.session == session }
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun loadSavedSessions(): List<WalletConnectSession> {
|
||||
return try {
|
||||
val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS)
|
||||
.hexToUtf8()
|
||||
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
|
||||
} catch (e: FileNotFoundException) {
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Timber.w(exception)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveSessions(sessions: List<WalletConnectSession>) {
|
||||
val json = walletConnectAdapter.toJson(sessions.map { SessionDao.fromSession(it) })
|
||||
.utf8ToHex() // convert to hex to solve problems with saving text with emojis
|
||||
Timber.e("WC sessions, saving following json: $json")
|
||||
context.rewriteFile(json, FILE_NAME_PREFIX_SESSIONS)
|
||||
}
|
||||
|
||||
private fun String.utf8ToHex(): String {
|
||||
return this.toByteArray().toHexString()
|
||||
}
|
||||
|
||||
private fun String.hexToUtf8(): String {
|
||||
return this.hexToBytes().toString(Charset.defaultCharset())
|
||||
}
|
||||
|
||||
private fun Context.readFileText(fileName: String): String =
|
||||
this.openFileInput(fileName).bufferedReader().readText()
|
||||
|
||||
private fun Context.rewriteFile(content: String, fileName: String) {
|
||||
this.openFileOutput(fileName, Context.MODE_PRIVATE).use {
|
||||
it.write(content.toByteArray(), 0, content.length)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FILE_NAME_PREFIX_SESSIONS = "wc_sessions"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SessionDao(
|
||||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val wallet: WalletForSession,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta,
|
||||
) {
|
||||
fun toSession(): WalletConnectSession {
|
||||
return WalletConnectSession(
|
||||
peerId = peerId,
|
||||
remotePeerId = remotePeerId,
|
||||
wallet = wallet,
|
||||
session = session,
|
||||
peerMeta = peerMeta,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromSession(session: WalletConnectSession): SessionDao {
|
||||
return SessionDao(
|
||||
peerId = session.peerId,
|
||||
remotePeerId = session.remotePeerId,
|
||||
wallet = session.wallet,
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,8 +20,6 @@ import com.tangem.operations.sign.SignHashCommand
|
|||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTradeOrder
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTransferOrder
|
||||
import com.tangem.tap.domain.walletconnect2.domain.TransactionType
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
|
|
@ -227,11 +225,11 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
|
||||
fun prepareBnbTradeOrder(data: WcBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
return BnbHelper.createMessageData(data.toWCBinanceTradeOrder())
|
||||
return BnbHelper.createMessageData(data)
|
||||
}
|
||||
|
||||
fun prepareBnbTransferOrder(data: WcBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
return BnbHelper.createMessageData(data.toWCBinanceTransferOrder())
|
||||
return BnbHelper.createMessageData(data)
|
||||
}
|
||||
|
||||
suspend fun signBnbTransaction(
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect.extensions
|
||||
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
|
||||
internal fun WCPeerMeta.isDappSupported(): Boolean {
|
||||
return !unsupportedDappsList.any { this.url.contains(it) }
|
||||
}
|
||||
|
||||
private val unsupportedDappsList: List<String> = listOf("dydx.exchange")
|
||||
|
||||
internal fun WCEthereumTransaction.toWcEthTransaction(): WcEthereumTransaction {
|
||||
return WcEthereumTransaction(
|
||||
from = from,
|
||||
to = to,
|
||||
nonce = nonce,
|
||||
gasPrice = gasPrice,
|
||||
maxFeePerGas = maxFeePerGas,
|
||||
maxPriorityFeePerGas = maxPriorityFeePerGas,
|
||||
gas = gas,
|
||||
gasLimit = gasLimit,
|
||||
value = value,
|
||||
data = data,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun WCEthereumSignMessage.toWcEthereumSignMessage(): WcSignMessage {
|
||||
return WcSignMessage(
|
||||
raw = raw,
|
||||
type = when (type) {
|
||||
WCEthereumSignMessage.WCSignType.MESSAGE -> WcSignMessage.WCSignType.MESSAGE
|
||||
WCEthereumSignMessage.WCSignType.PERSONAL_MESSAGE -> WcSignMessage.WCSignType.PERSONAL_MESSAGE
|
||||
WCEthereumSignMessage.WCSignType.TYPED_MESSAGE -> WcSignMessage.WCSignType.TYPED_MESSAGE
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,118 +1,26 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.walletconnect.Topic
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class WalletConnectAction : Action {
|
||||
object ResetState : WalletConnectAction()
|
||||
data class HandleDeepLink(val wcUri: String?) : WalletConnectAction()
|
||||
data class RestoreSessions(val scanResponse: ScanResponse) : WalletConnectAction()
|
||||
|
||||
data class StartWalletConnect(
|
||||
val copiedUri: String?,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction()
|
||||
object UnsupportedCard : WalletConnectAction()
|
||||
data class OpenSession(
|
||||
val wcUri: String,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class SetNewSessionData(
|
||||
val newSession: NewWcSessionData,
|
||||
) : WalletConnectAction()
|
||||
data class DisconnectSession(val topic: String) : WalletConnectAction()
|
||||
|
||||
object RefuseOpeningSession : WalletConnectAction()
|
||||
data class OpeningSessionTimeout(val session: WCSession) : WalletConnectAction()
|
||||
data class ScanCard(
|
||||
val session: WalletConnectSession,
|
||||
val chainId: Int?,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class ApproveSession(
|
||||
val session: WCSession,
|
||||
) : WalletConnectAction() {
|
||||
data class Success(val session: WalletConnectSession) : WalletConnectAction()
|
||||
}
|
||||
|
||||
data class SwitchBlockchain(
|
||||
val blockchain: Blockchain?,
|
||||
val session: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class SelectNetwork(val session: WalletConnectSession, val networks: List<Blockchain>) : WalletConnectAction()
|
||||
data class ChooseNetwork(val blockchain: Blockchain) : WalletConnectAction()
|
||||
data class UpdateBlockchain(
|
||||
val updatedSession: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class FailureEstablishingSession(val session: WCSession?, val error: TapError? = null) : WalletConnectAction()
|
||||
data class SetSessionsRestored(val sessions: List<WalletConnectSession>) : WalletConnectAction()
|
||||
|
||||
data class DisconnectSession(val topic: String, val session: WCSession?) : WalletConnectAction()
|
||||
|
||||
data class RemoveSession(val session: WCSession) : WalletConnectAction()
|
||||
|
||||
data class HandleTransactionRequest(
|
||||
val transaction: WCEthereumTransaction,
|
||||
val session: WalletConnectSession,
|
||||
val id: Long,
|
||||
val type: WcEthTransactionType,
|
||||
) :
|
||||
WalletConnectAction()
|
||||
|
||||
data class HandlePersonalSignRequest(
|
||||
val message: WCEthereumSignMessage,
|
||||
val session: WalletConnectSession,
|
||||
val id: Long,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class SendTransaction(val topic: Topic) : WalletConnectAction()
|
||||
|
||||
data class SignMessage(val topic: Topic) : WalletConnectAction()
|
||||
|
||||
data class RejectRequest(val topic: Topic, val id: Long) : WalletConnectAction()
|
||||
|
||||
object NotEnoughFunds : WalletConnectAction(), NotificationAction {
|
||||
override val messageResource = R.string.wallet_connect_create_tx_not_enough_funds
|
||||
}
|
||||
|
||||
object NotifyCameraPermissionIsRequired : WalletConnectAction(), NotificationAction {
|
||||
override val messageResource = R.string.common_camera_denied_alert_message
|
||||
}
|
||||
|
||||
object BinanceTransaction : WalletConnectAction() {
|
||||
data class Trade(
|
||||
val id: Long,
|
||||
val order: WCBinanceTradeOrder,
|
||||
val sessionData: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class Transfer(
|
||||
val id: Long,
|
||||
val order: WCBinanceTransferOrder,
|
||||
val sessionData: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class Sign(
|
||||
val id: Long,
|
||||
val data: ByteArray,
|
||||
val topic: Topic,
|
||||
) : WalletConnectAction()
|
||||
}
|
||||
data class RejectRequest(val topic: String, val id: Long) : WalletConnectAction()
|
||||
|
||||
data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction()
|
||||
//region WalletConnect 2.0
|
||||
object ApproveProposal : WalletConnectAction()
|
||||
object RejectProposal : WalletConnectAction()
|
||||
|
|
|
|||
|
|
@ -1,44 +1,28 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import androidx.core.os.bundleOf
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.walletconnect.WalletConnectActions
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils
|
||||
import com.tangem.tap.domain.walletconnect.extensions.toWcEthTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.Account
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.BnbData
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -46,7 +30,6 @@ import timber.log.Timber
|
|||
|
||||
@Suppress("LargeClass")
|
||||
class WalletConnectMiddleware {
|
||||
private var walletConnectManager = WalletConnectManager()
|
||||
private val walletConnectInteractor: WalletConnectInteractor
|
||||
get() = store.inject(DaggerGraphState::walletConnectInteractor)
|
||||
private val walletConnectRepository: WalletConnectRepository
|
||||
|
|
@ -88,18 +71,18 @@ class WalletConnectMiddleware {
|
|||
wcInteractor.setUserChains(getAccountsForWc(wcInteractor, userWallet))
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.ResetState -> walletConnectManager = WalletConnectManager()
|
||||
is WalletConnectAction.RestoreSessions -> {
|
||||
walletConnectManager.restoreSessions(action.scanResponse)
|
||||
}
|
||||
is WalletConnectAction.HandleDeepLink -> {
|
||||
if (!action.wcUri.isNullOrBlank()) {
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(action.wcUri))
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.DisconnectSession -> {
|
||||
walletConnectInteractor.disconnectSession(action.topic)
|
||||
}
|
||||
is WalletConnectAction.StartWalletConnect -> {
|
||||
val uri = action.copiedUri
|
||||
if (uri != null && isWalletConnectUri(uri)) {
|
||||
// TODO check
|
||||
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
||||
} else {
|
||||
store.dispatchOnMain(
|
||||
|
|
@ -112,27 +95,6 @@ class WalletConnectMiddleware {
|
|||
)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.SelectNetwork -> {
|
||||
store.dispatch(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ChooseNetwork(
|
||||
session = action.session,
|
||||
networks = action.networks,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.ChooseNetwork -> {
|
||||
val data = state()?.walletConnectState?.newSessionData ?: return
|
||||
scope.launch {
|
||||
prepareWalletManager(
|
||||
scanResponse = data.scanResponse,
|
||||
blockchain = action.blockchain,
|
||||
session = data.session,
|
||||
walletConnectManager = walletConnectManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
|
|
@ -142,176 +104,15 @@ class WalletConnectMiddleware {
|
|||
),
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.OpeningSessionTimeout -> {
|
||||
Timber.e("OpeningSessionTimeout for topic ${action.session.topic}")
|
||||
// do not show dialog for now, it shows always to user if cannot establish connection
|
||||
// store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout))
|
||||
}
|
||||
is WalletConnectAction.FailureEstablishingSession -> {
|
||||
Timber.e("FailureEstablishingSession for topic ${action.session?.topic}")
|
||||
// disable alerts in release to avoid annoying users
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.UnsupportedCard -> {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedCard))
|
||||
}
|
||||
is WalletConnectAction.OpenSession -> {
|
||||
val index = action.wcUri.indexOf("@")
|
||||
when (action.wcUri[index + 1]) {
|
||||
'1' -> {
|
||||
walletConnectManager.connect(wcUri = action.wcUri)
|
||||
}
|
||||
'2' -> walletConnectRepository.pair(uri = action.wcUri)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.RefuseOpeningSession -> {
|
||||
Timber.e("RefuseOpeningSession")
|
||||
|
||||
// do not show for now to avoid anoying users with alert
|
||||
// store.dispatch(
|
||||
// GlobalAction.ShowDialog(
|
||||
// WalletConnectDialog.OpeningSessionRejected,
|
||||
// ),
|
||||
// )
|
||||
}
|
||||
is WalletConnectAction.ScanCard -> {
|
||||
val scanResponse = userWalletsListManager.selectedUserWalletSync.guard {
|
||||
Timber.w("Unable to get selected user wallet for WC session")
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch(Dispatchers.Main) {
|
||||
scanCard(scanResponse, action.session, action.chainId)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.ApproveSession -> {
|
||||
walletConnectManager.approve(action.session)
|
||||
}
|
||||
is WalletConnectAction.DisconnectSession -> {
|
||||
if (action.session != null) {
|
||||
walletConnectManager.disconnect(action.session)
|
||||
} else {
|
||||
walletConnectInteractor.disconnectSession(action.topic)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.HandleTransactionRequest -> {
|
||||
walletConnectManager.handleTransactionRequest(
|
||||
transaction = action.transaction.toWcEthTransaction(),
|
||||
session = action.session,
|
||||
id = action.id,
|
||||
type = action.type,
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.HandlePersonalSignRequest -> {
|
||||
walletConnectManager.handlePersonalSignRequest(
|
||||
message = action.message,
|
||||
session = action.session,
|
||||
id = action.id,
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.RejectRequest -> {
|
||||
walletConnectManager.rejectRequest(action.topic, action.id)
|
||||
walletConnectInteractor.cancelRequest(action.topic, action.id)
|
||||
}
|
||||
is WalletConnectAction.SendTransaction -> {
|
||||
walletConnectManager.completeTransaction(action.topic)
|
||||
}
|
||||
is WalletConnectAction.SignMessage -> {
|
||||
walletConnectManager.sendSignedMessage(action.topic)
|
||||
}
|
||||
is WalletConnectAction.BinanceTransaction.Trade -> {
|
||||
val messageData = BnbHelper.createMessageData(action.order)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.BnbTransactionDialog(
|
||||
WcPreparedRequest.BnbTransaction(
|
||||
preparedRequestData = BnbData(
|
||||
data = messageData,
|
||||
topic = action.sessionData.session.topic,
|
||||
requestId = action.id,
|
||||
dAppName = action.sessionData.peerMeta.name,
|
||||
),
|
||||
topic = action.sessionData.session.topic,
|
||||
requestId = action.id,
|
||||
derivationPath = action.sessionData.wallet.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.BinanceTransaction.Transfer -> {
|
||||
val messageData = BnbHelper.createMessageData(action.order)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.BnbTransactionDialog(
|
||||
WcPreparedRequest.BnbTransaction(
|
||||
preparedRequestData = BnbData(
|
||||
data = messageData,
|
||||
topic = action.sessionData.session.topic,
|
||||
requestId = action.id,
|
||||
dAppName = action.sessionData.peerMeta.name,
|
||||
),
|
||||
topic = action.sessionData.session.topic,
|
||||
requestId = action.id,
|
||||
derivationPath = action.sessionData.wallet.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.BinanceTransaction.Sign -> {
|
||||
walletConnectManager.signBnb(
|
||||
id = action.id,
|
||||
data = action.data,
|
||||
topic = action.topic,
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.SwitchBlockchain -> {
|
||||
if (action.session.wallet.derivationStyle == DerivationStyle.LEGACY) {
|
||||
Timber.d("Cannot switch chains on AC01/AC02 wallets")
|
||||
return
|
||||
}
|
||||
val blockchain = action.blockchain.guard {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork()))
|
||||
return
|
||||
}
|
||||
scope.launch {
|
||||
val walletManager = getWalletManager(
|
||||
wallet = action.session.wallet,
|
||||
blockchain = blockchain,
|
||||
).guard {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.AddNetwork(listOf(blockchain.fullName)),
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
val updatedWallet = action.session.wallet.copy(
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
derivedPublicKey = walletManager.wallet.publicKey.derivedKey,
|
||||
derivationPath = walletManager.wallet.publicKey.derivationPath,
|
||||
blockchain = action.blockchain,
|
||||
)
|
||||
val updatedSession = action.session.copy(wallet = updatedWallet)
|
||||
store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession))
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.UpdateBlockchain -> {
|
||||
walletConnectManager.updateBlockchain(action.updatedSession)
|
||||
}
|
||||
is WalletConnectAction.ApproveProposal -> {
|
||||
scope.launch {
|
||||
val accounts = getWalletManagers()
|
||||
|
|
@ -402,119 +203,8 @@ class WalletConnectMiddleware {
|
|||
return walletManagerFacade.getStoredWalletManagers(userWallet.walletId)
|
||||
}
|
||||
|
||||
private suspend fun scanCard(userWallet: UserWallet, session: WalletConnectSession, chainId: Int?) {
|
||||
val blockchain = WalletConnectNetworkUtils.parseBlockchain(
|
||||
chainId = chainId,
|
||||
peer = session.peerMeta,
|
||||
).guard {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork()))
|
||||
return
|
||||
}
|
||||
|
||||
handleScanResponse(userWallet, session, blockchain)
|
||||
}
|
||||
|
||||
private suspend fun getAvailableEvmBlockchains(userWalletId: UserWalletId): List<Blockchain> {
|
||||
val currenciesRepository = store.inject(DaggerGraphState::currenciesRepository)
|
||||
|
||||
return currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId)
|
||||
.asSequence()
|
||||
.filterIsInstance<CryptoCurrency.Coin>()
|
||||
.filterNot { it.isCustom }
|
||||
.mapNotNull { Blockchain.fromNetworkId(it.network.id.value) }
|
||||
.filter { it.isEvm() }
|
||||
.toList()
|
||||
}
|
||||
|
||||
private suspend fun prepareWalletManager(
|
||||
scanResponse: ScanResponse,
|
||||
blockchain: Blockchain,
|
||||
session: WalletConnectSession,
|
||||
walletConnectManager: WalletConnectManager,
|
||||
) {
|
||||
val walletManager = getWalletManager(session.wallet, blockchain).guard {
|
||||
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.AddNetwork(listOf(blockchain.fullName)),
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey =
|
||||
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
val walletForSession = WalletForSession(
|
||||
walletPublicKey = wallet.publicKey.seedKey,
|
||||
derivedPublicKey = derivedKey,
|
||||
derivationPath = wallet.publicKey.derivationPath,
|
||||
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
blockchain = wallet.blockchain,
|
||||
)
|
||||
|
||||
withMainContext {
|
||||
val updatedSession = session.copy(wallet = walletForSession)
|
||||
walletConnectManager.updateSession(updatedSession)
|
||||
|
||||
store.dispatch(WalletConnectAction.ApproveSession(session.session))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleScanResponse(
|
||||
userWallet: UserWallet,
|
||||
session: WalletConnectSession,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
val scanResponse = userWallet.scanResponse
|
||||
|
||||
if (!scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return
|
||||
}
|
||||
val updatedSession = session.copy(wallet = session.wallet.copy(blockchain = blockchain))
|
||||
store.dispatch(
|
||||
WalletConnectAction.SetNewSessionData(
|
||||
NewWcSessionData(updatedSession, scanResponse, blockchain),
|
||||
),
|
||||
)
|
||||
val blockchains = if (blockchain.isEvm()) {
|
||||
getAvailableEvmBlockchains(userWallet.walletId)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
store.dispatch(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getWalletManager(wallet: WalletForSession, blockchain: Blockchain): WalletManager? {
|
||||
val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) {
|
||||
Blockchain.EthereumTestnet
|
||||
} else {
|
||||
blockchain
|
||||
}
|
||||
val userWallet = userWalletsListManager.selectedUserWalletSync ?: return null
|
||||
val derivation = blockchainToMake.derivationPath(
|
||||
style = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
)?.rawPath
|
||||
|
||||
val walletManagerFacade = store.inject(DaggerGraphState::walletManagersFacade)
|
||||
|
||||
return walletManagerFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWallet.walletId,
|
||||
blockchain = blockchainToMake,
|
||||
derivationPath = derivation,
|
||||
)
|
||||
}
|
||||
|
||||
private fun isWalletConnectUri(uri: String): Boolean {
|
||||
return WalletConnectManager.isCorrectWcUri(uri) || walletConnectInteractor.isWalletConnectUri(uri)
|
||||
return walletConnectInteractor.isWalletConnectUri(uri)
|
||||
}
|
||||
|
||||
private suspend fun getAccountsForWc(wcInteractor: WalletConnectInteractor, userWallet: UserWallet): List<Account> {
|
||||
|
|
|
|||
|
|
@ -7,36 +7,9 @@ object WalletConnectReducer {
|
|||
if (action !is WalletConnectAction) return state
|
||||
|
||||
return when (action) {
|
||||
is WalletConnectAction.ResetState -> return WalletConnectState()
|
||||
is WalletConnectAction.ApproveSession.Success -> {
|
||||
state.copy(
|
||||
loading = false,
|
||||
sessions = state.sessions + action.session,
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.OpenSession -> {
|
||||
state.copy(loading = true)
|
||||
}
|
||||
is WalletConnectAction.SetNewSessionData -> {
|
||||
state.copy(newSessionData = action.newSession)
|
||||
}
|
||||
is WalletConnectAction.SetSessionsRestored -> state.copy(
|
||||
sessions = action.sessions,
|
||||
)
|
||||
is WalletConnectAction.RemoveSession -> {
|
||||
val sessions =
|
||||
state.sessions.filterNot { it.session.toUri() == action.session.toUri() }
|
||||
state.copy(sessions = sessions)
|
||||
}
|
||||
is WalletConnectAction.UnsupportedCard,
|
||||
is WalletConnectAction.RefuseOpeningSession,
|
||||
is WalletConnectAction.OpeningSessionTimeout,
|
||||
is WalletConnectAction.FailureEstablishingSession,
|
||||
-> state.copy(loading = false)
|
||||
is WalletConnectAction.UpdateBlockchain -> state.copy(
|
||||
sessions = state.sessions
|
||||
.filterNot { it.peerId == action.updatedSession.peerId } + action.updatedSession,
|
||||
)
|
||||
is WalletConnectAction.ApproveProposal -> state.copy(loading = true)
|
||||
is WalletConnectAction.RejectProposal,
|
||||
is WalletConnectAction.SessionEstablished,
|
||||
|
|
|
|||
|
|
@ -14,12 +14,9 @@ import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents
|
|||
import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen
|
||||
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
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
|
||||
data class WalletConnectState(
|
||||
val loading: Boolean = false,
|
||||
val sessions: List<WalletConnectSession> = listOf(),
|
||||
val wc2Sessions: List<WcSessionForScreen> = listOf(),
|
||||
val newSessionData: NewWcSessionData? = null,
|
||||
)
|
||||
|
|
@ -34,8 +31,6 @@ data class WalletConnectSession(
|
|||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val wallet: WalletForSession,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta,
|
||||
) {
|
||||
fun getAddress(): String? {
|
||||
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey ?: return null
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.tangem.core.navigation.NavigationAction
|
|||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import com.tangem.tap.common.analytics.events.WalletConnect
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import com.tangem.tap.store
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -42,13 +41,6 @@ internal class WalletConnectFragment : ComposeFragment(), StoreSubscriber<Wallet
|
|||
modifier = modifier,
|
||||
state = state,
|
||||
onBackClick = {
|
||||
if (state.isLoading) {
|
||||
store.dispatch(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
store.state.walletConnectState.newSessionData?.session?.session,
|
||||
),
|
||||
)
|
||||
}
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class WalletConnectScreenState(
|
||||
|
|
@ -13,13 +12,4 @@ internal data class WalletConnectScreenState(
|
|||
data class WcSessionForScreen(
|
||||
val description: String,
|
||||
val sessionId: String,
|
||||
) {
|
||||
companion object {
|
||||
fun fromSession(session: WalletConnectSession): WcSessionForScreen {
|
||||
return WcSessionForScreen(
|
||||
description = session.peerMeta.name,
|
||||
sessionId = session.session.toUri(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -5,7 +5,6 @@ import arrow.core.getOrElse
|
|||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import com.tangem.tap.store
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -31,28 +30,18 @@ internal class WalletConnectViewModel @Inject constructor(
|
|||
|
||||
fun updateState(state: WalletConnectState): WalletConnectScreenState {
|
||||
Timber.d("WC2 Sessions: ${state.wc2Sessions}")
|
||||
val sessions = state.sessions.map { wcSession -> WcSessionForScreen.fromSession(wcSession) } + state.wc2Sessions
|
||||
val sessions = state.wc2Sessions
|
||||
return WalletConnectScreenState(
|
||||
sessions.toImmutableList(),
|
||||
isLoading = state.loading,
|
||||
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, state.sessions, state.wc2Sessions) },
|
||||
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) },
|
||||
onAddSession = { copiedUri -> store.dispatch(WalletConnectAction.StartWalletConnect(copiedUri)) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun onRemoveSession(
|
||||
sessionUri: String,
|
||||
sessions: List<WalletConnectSession>,
|
||||
wc2sessions: List<WcSessionForScreen>,
|
||||
) {
|
||||
sessions
|
||||
.firstOrNull { it.session.toUri() == sessionUri }
|
||||
?.let { baseSession ->
|
||||
store.dispatch(WalletConnectAction.DisconnectSession(baseSession.session.topic, baseSession.session))
|
||||
return
|
||||
}
|
||||
private fun onRemoveSession(sessionUri: String, wc2sessions: List<WcSessionForScreen>) {
|
||||
wc2sessions.firstOrNull { it.sessionId == sessionUri }?.let {
|
||||
store.dispatch(WalletConnectAction.DisconnectSession(sessionUri, null))
|
||||
store.dispatch(WalletConnectAction.DisconnectSession(sessionUri))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
object ApproveWcSessionDialog {
|
||||
fun create(session: WalletConnectSession, networks: List<Blockchain>, context: Context): AlertDialog {
|
||||
val sessionBlockchain = requireNotNull(session.wallet.blockchain) { "session network is null" }
|
||||
val message = context.getString(
|
||||
R.string.wallet_connect_request_session_start,
|
||||
session.peerMeta.name,
|
||||
sessionBlockchain.fullName,
|
||||
session.peerMeta.url,
|
||||
)
|
||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
||||
setTitle(context.getString(R.string.wallet_connect_title))
|
||||
setMessage(message)
|
||||
setPositiveButton(context.getText(R.string.common_start)) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.ChooseNetwork(sessionBlockchain))
|
||||
}
|
||||
if (networks.size > 1) {
|
||||
setNeutralButton(context.getText(R.string.wallet_connect_select_network)) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.SelectNetwork(session = session, networks = networks))
|
||||
}
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
setOnCancelListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
}
|
||||
|
|
@ -42,13 +42,6 @@ object BnbTransactionDialog {
|
|||
setTitle(context.getString(R.string.wallet_connect_title))
|
||||
setMessage(fullMessage)
|
||||
setPositiveButton(positiveButtonTitle) { _, _ ->
|
||||
store.dispatch(
|
||||
WalletConnectAction.BinanceTransaction.Sign(
|
||||
id = preparedData.requestId,
|
||||
data = data.data,
|
||||
topic = preparedData.topic,
|
||||
),
|
||||
)
|
||||
store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData))
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
object ChooseNetworkDialog {
|
||||
fun create(session: WalletConnectSession, networks: List<Blockchain>, context: Context): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog)
|
||||
.setTitle(context.getString(R.string.wallet_connect_select_network))
|
||||
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
.setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
.setSingleChoiceItems(networks.map { it.fullName }.toTypedArray(), 0) { _, which ->
|
||||
networks.getOrNull(which)?.let { selectedBlockchain ->
|
||||
store.dispatch(
|
||||
WalletConnectAction.ChooseNetwork(
|
||||
blockchain = selectedBlockchain,
|
||||
),
|
||||
)
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ object PersonalSignDialog {
|
|||
setTitle(context.getString(R.string.wallet_connect_title))
|
||||
setMessage(message)
|
||||
setPositiveButton(context.getText(R.string.common_sign)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.SignMessage(data.topic))
|
||||
store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData))
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
|
|
|
|||
|
|
@ -32,11 +32,9 @@ object TransactionDialog {
|
|||
setMessage(message)
|
||||
setPositiveButton(positiveButtonTitle) { _, _ ->
|
||||
if (data.isEnoughFundsToSend) {
|
||||
store.dispatch(WalletConnectAction.SendTransaction(data.topic))
|
||||
store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData))
|
||||
} else {
|
||||
store.dispatch(WalletConnectAction.RejectRequest(data.topic, data.id))
|
||||
store.dispatch(WalletConnectAction.NotEnoughFunds)
|
||||
}
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.tap.features.intentHandler.handlers
|
|||
import android.content.Intent
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.removePrefixOrNull
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -20,7 +19,7 @@ class WalletConnectLinkIntentHandler : IntentHandler {
|
|||
val scheme = intent.scheme ?: return false
|
||||
|
||||
val wcUri = when (scheme) {
|
||||
WalletConnectManager.WC_SCHEME -> intentData.toString()
|
||||
WC_SCHEME -> intentData.toString()
|
||||
TANGEM_SCHEME -> intentData.toString().removePrefixOrNull(TANGEM_WC_PREFIX)
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -40,8 +39,9 @@ class WalletConnectLinkIntentHandler : IntentHandler {
|
|||
}
|
||||
|
||||
private companion object {
|
||||
private const val TANGEM_SCHEME = "tangem"
|
||||
private const val TANGEM_WC_PREFIX = "tangem://wc?uri="
|
||||
private const val DEFAULT_CHARSET_NAME = "UTF-8"
|
||||
const val TANGEM_SCHEME = "tangem"
|
||||
const val TANGEM_WC_PREFIX = "tangem://wc?uri="
|
||||
const val DEFAULT_CHARSET_NAME = "UTF-8"
|
||||
const val WC_SCHEME = "wc"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue