Updated on 2026-08-14
This commit is contained in:
commit
bf2f639cb2
10 changed files with 120 additions and 29 deletions
|
|
@ -113,6 +113,7 @@ sealed class AnalyticsParam {
|
|||
const val SEED_PHRASE_LENGTH = "Seed Phrase Length"
|
||||
const val DAPP_NAME = "DApp Name"
|
||||
const val DAPP_URL = "DApp Url"
|
||||
const val NETWORKS = "Networks"
|
||||
const val METHOD_NAME = "Method Name"
|
||||
const val VALIDATION = "Validation"
|
||||
const val BLOCKCHAIN_EXCEPTION_HOST = "exception_host"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -12,8 +13,41 @@ internal sealed class WalletConnect(
|
|||
) : AnalyticsEvent("Wallet Connect", event, params, error) {
|
||||
|
||||
class ScreenOpened : WalletConnect(event = "WC Screen Opened")
|
||||
class NewSessionEstablished(dAppName: String, dAppUrl: String, blockchainNames: List<String>) : WalletConnect(
|
||||
event = "New Session Established",
|
||||
class NewSessionInitiated(source: SourceType) : WalletConnect(
|
||||
event = "Session Initiated",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to when (source) {
|
||||
SourceType.QR -> "QR"
|
||||
SourceType.DEEPLINK -> "DeepLink"
|
||||
SourceType.ETC -> "etc"
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
data object SessionFailed : WalletConnect(
|
||||
event = "Session Failed",
|
||||
)
|
||||
|
||||
class DAppConnectionRequested(
|
||||
blockchainNames: List<String>,
|
||||
) : WalletConnect(
|
||||
event = "dApp Connection Requested",
|
||||
params = mapOf(
|
||||
AnalyticsParam.NETWORKS to blockchainNames.joinToString(","),
|
||||
),
|
||||
)
|
||||
|
||||
class DAppConnected(dAppName: String, dAppUrl: String, blockchainNames: List<String>) : WalletConnect(
|
||||
event = "dApp Connected",
|
||||
params = mapOf(
|
||||
AnalyticsParam.DAPP_NAME to dAppName,
|
||||
AnalyticsParam.DAPP_URL to dAppUrl,
|
||||
AnalyticsParam.BLOCKCHAIN to blockchainNames.joinToString(","),
|
||||
),
|
||||
)
|
||||
|
||||
class DAppConnectionFailed(dAppName: String, dAppUrl: String, blockchainNames: List<String>) : WalletConnect(
|
||||
event = "dApp Connection Failed",
|
||||
params = mapOf(
|
||||
AnalyticsParam.DAPP_NAME to dAppName,
|
||||
AnalyticsParam.DAPP_URL to dAppUrl,
|
||||
|
|
@ -22,17 +56,31 @@ internal sealed class WalletConnect(
|
|||
)
|
||||
|
||||
class SessionDisconnected(dAppName: String, dAppUrl: String) : WalletConnect(
|
||||
event = "Session Disconnected",
|
||||
event = "dApp Disconnected",
|
||||
params = mapOf(
|
||||
AnalyticsParam.DAPP_NAME to dAppName,
|
||||
AnalyticsParam.DAPP_URL to dAppUrl,
|
||||
),
|
||||
)
|
||||
|
||||
class RequestHandled(
|
||||
class SignatureRequestHandled(
|
||||
params: RequestHandledParams,
|
||||
) : WalletConnect(
|
||||
event = "Request Handled",
|
||||
event = "Signature Request Handled",
|
||||
params = params.toParamsMap(),
|
||||
)
|
||||
|
||||
class SignatureRequestReceived(
|
||||
params: RequestHandledParams,
|
||||
) : WalletConnect(
|
||||
event = "Signature Request Received",
|
||||
params = params.toParamsMap(),
|
||||
)
|
||||
|
||||
class SignatureRequestFailed(
|
||||
params: RequestHandledParams,
|
||||
) : WalletConnect(
|
||||
event = "Signature Request Failed",
|
||||
params = params.toParamsMap(),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ import com.reown.walletkit.client.Wallet
|
|||
import com.reown.walletkit.client.WalletKit
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.events.WalletConnect
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcMethods
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.*
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
|
@ -24,6 +22,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
private val application: Application,
|
||||
private val wcRequestDeserializer: WcJrpcRequestsDeserializer,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
private val blockchainHelper: WcBlockchainHelper,
|
||||
) : LegacyWalletConnectRepository {
|
||||
|
||||
private var sessionProposal: Wallet.Model.SessionProposal? = null
|
||||
|
|
@ -136,6 +135,13 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
userNamespaces = this@DefaultLegacyWalletConnectRepository.userNamespaces ?: emptyMap(),
|
||||
)
|
||||
|
||||
val requiredChainIds = sessionProposal.requiredNamespaces.values.flatMap { it.chains ?: emptyList() }
|
||||
val optionalChainIds = optionalWithoutMissingNetworks.toList()
|
||||
val networks = (requiredChainIds + optionalChainIds)
|
||||
.mapNotNull { blockchainHelper.chainIdToFullNameOrNull(it) }
|
||||
.distinct()
|
||||
analyticsHandler.send(WalletConnect.DAppConnectionRequested(networks))
|
||||
|
||||
scope.launch {
|
||||
_events.emit(
|
||||
WalletConnectEvents.SessionProposal(
|
||||
|
|
@ -143,8 +149,8 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
sessionProposal.description,
|
||||
sessionProposal.url,
|
||||
sessionProposal.icons,
|
||||
sessionProposal.requiredNamespaces.values.flatMap { it.chains ?: emptyList() },
|
||||
optionalWithoutMissingNetworks.toList(),
|
||||
requiredChainIds,
|
||||
optionalChainIds,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -176,7 +182,17 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
result = "",
|
||||
)
|
||||
}
|
||||
else ->
|
||||
else -> {
|
||||
val event = WalletConnect.SignatureRequestReceived(
|
||||
WalletConnect.RequestHandledParams(
|
||||
dAppName = sessionRequest.peerMetaData?.name ?: "",
|
||||
dAppUrl = sessionRequest.peerMetaData?.url ?: "",
|
||||
methodName = sessionRequest.request.method,
|
||||
blockchain = sessionRequest.chainId
|
||||
?.let { blockchainHelper.chainIdToNetworkIdOrNull(it) } ?: "",
|
||||
),
|
||||
)
|
||||
analyticsHandler.send(event)
|
||||
scope.launch {
|
||||
_events.emit(
|
||||
WalletConnectEvents.SessionRequest(
|
||||
|
|
@ -190,6 +206,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +264,8 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
this.userNamespaces = userNamespaces
|
||||
}
|
||||
|
||||
override fun pair(uri: String) {
|
||||
override fun pair(uri: String, source: SourceType) {
|
||||
analyticsHandler.send(WalletConnect.NewSessionInitiated(source = source))
|
||||
WalletKit.pair(
|
||||
params = Wallet.Params.Pair(uri),
|
||||
onSuccess = {
|
||||
|
|
@ -255,6 +273,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
},
|
||||
onError = {
|
||||
Timber.e("Error while pairing: $it")
|
||||
analyticsHandler.send(WalletConnect.SessionFailed)
|
||||
scope.launch {
|
||||
_events.emit(
|
||||
WalletConnectEvents.PairConnectError(it.throwable),
|
||||
|
|
@ -305,7 +324,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
onSuccess = {
|
||||
Timber.i("Approved successfully: $it")
|
||||
analyticsHandler.send(
|
||||
WalletConnect.NewSessionEstablished(
|
||||
WalletConnect.DAppConnected(
|
||||
dAppName = sessionProposal.name,
|
||||
dAppUrl = sessionProposal.url,
|
||||
blockchainNames = blockchainNames,
|
||||
|
|
@ -314,6 +333,13 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
},
|
||||
onError = {
|
||||
Timber.e("Error while approving: $it")
|
||||
analyticsHandler.send(
|
||||
WalletConnect.DAppConnectionFailed(
|
||||
dAppName = sessionProposal.name,
|
||||
dAppUrl = sessionProposal.url,
|
||||
blockchainNames = blockchainNames,
|
||||
),
|
||||
)
|
||||
scope.launch {
|
||||
_events.emit(
|
||||
WalletConnectEvents.SessionApprovalError(
|
||||
|
|
@ -352,7 +378,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
// Add Ethereum Chain method is processed without user input, skip logging it
|
||||
if (requestData.method != WcJrpcMethods.WALLET_ADD_ETHEREUM_CHAIN.code) {
|
||||
analyticsHandler.send(
|
||||
WalletConnect.RequestHandled(
|
||||
WalletConnect.SignatureRequestHandled(
|
||||
WalletConnect.RequestHandledParams(
|
||||
dAppName = session?.name ?: "",
|
||||
dAppUrl = session?.url ?: "",
|
||||
|
|
@ -377,7 +403,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
onError = { error ->
|
||||
Timber.e(error.throwable, "Error while responging session request")
|
||||
|
||||
WalletConnect.RequestHandledParams(
|
||||
val params = WalletConnect.RequestHandledParams(
|
||||
dAppName = session?.name ?: "",
|
||||
dAppUrl = session?.url ?: "",
|
||||
methodName = requestData.method,
|
||||
|
|
@ -385,6 +411,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
errorCode = WalletConnectError.ValidationError.error,
|
||||
errorDescription = error.throwable.message,
|
||||
)
|
||||
analyticsHandler.send(WalletConnect.SignatureRequestFailed(params))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -393,7 +420,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
val session = currentSessions.find { it.topic == requestData.topic }
|
||||
|
||||
analyticsHandler.send(
|
||||
WalletConnect.RequestHandled(
|
||||
WalletConnect.SignatureRequestHandled(
|
||||
WalletConnect.RequestHandledParams(
|
||||
dAppName = session?.name ?: "",
|
||||
dAppUrl = session?.url ?: "",
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
|
|||
import com.tangem.tap.domain.walletconnect2.app.WalletConnectEventsHandlerImpl
|
||||
import com.tangem.tap.domain.walletconnect2.data.DefaultLegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.data.DefaultWalletConnectSessionsRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.*
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -35,7 +33,7 @@ internal object WalletConnectInteractorModule {
|
|||
fun provideWalletConnectInteractor(
|
||||
wcRepository: LegacyWalletConnectRepository,
|
||||
wcSessionsRepository: WalletConnectSessionsRepository,
|
||||
walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
wcBlockchainHelper: WcBlockchainHelper,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
|
|
@ -46,7 +44,7 @@ internal object WalletConnectInteractorModule {
|
|||
walletConnectRepository = wcRepository,
|
||||
sessionsRepository = wcSessionsRepository,
|
||||
sdkHelper = WalletConnectSdkHelper(),
|
||||
blockchainHelper = TangemWcBlockchainHelper(walletConnectFeatureToggles),
|
||||
blockchainHelper = wcBlockchainHelper,
|
||||
currenciesRepository = currenciesRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
userWalletsListManager = userWalletsListManager,
|
||||
|
|
@ -65,17 +63,25 @@ internal object WalletConnectModule {
|
|||
return WalletConnectFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWcBlockchainHelper(walletConnectFeatureToggles: WalletConnectFeatureToggles): WcBlockchainHelper {
|
||||
return TangemWcBlockchainHelper(walletConnectFeatureToggles)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWalletConnectRepository(
|
||||
application: Application,
|
||||
wcRequestDeserializer: WcJrpcRequestsDeserializer,
|
||||
analyticsHandler: AnalyticsEventHandler,
|
||||
wcBlockchainHelper: WcBlockchainHelper,
|
||||
): LegacyWalletConnectRepository {
|
||||
return DefaultLegacyWalletConnectRepository(
|
||||
application = application,
|
||||
wcRequestDeserializer = wcRequestDeserializer,
|
||||
analyticsHandler = analyticsHandler,
|
||||
blockchainHelper = wcBlockchainHelper,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain
|
||||
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface LegacyWalletConnectRepository {
|
||||
|
|
@ -17,7 +18,7 @@ interface LegacyWalletConnectRepository {
|
|||
|
||||
fun updateSessions()
|
||||
|
||||
fun pair(uri: String)
|
||||
fun pair(uri: String, source: SourceType)
|
||||
|
||||
fun disconnect(topic: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,9 @@ class WalletConnectInteractor(
|
|||
isWalletConnectReadyForDeepLinks = true
|
||||
if (deeplinkStack.empty()) return
|
||||
val lastDeeplink = deeplinkStack.pop()
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(lastDeeplink))
|
||||
val action = WalletConnectAction
|
||||
.OpenSession(lastDeeplink, WalletConnectAction.OpenSession.SourceType.DEEPLINK)
|
||||
store.dispatchOnMain(action)
|
||||
}.onFailure {
|
||||
Timber.e("WC deeplink handling failed. $it")
|
||||
}
|
||||
|
|
@ -392,7 +394,8 @@ class WalletConnectInteractor(
|
|||
}
|
||||
|
||||
if (isWalletConnectReadyForDeepLinks) {
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(deeplink))
|
||||
val action = WalletConnectAction.OpenSession(deeplink, WalletConnectAction.OpenSession.SourceType.DEEPLINK)
|
||||
store.dispatchOnMain(action)
|
||||
} else {
|
||||
deeplinkStack.push(deeplink)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ sealed class WalletConnectAction : Action {
|
|||
|
||||
data class OpenSession(
|
||||
val wcUri: String,
|
||||
) : WalletConnectAction()
|
||||
val source: SourceType,
|
||||
) : WalletConnectAction() {
|
||||
enum class SourceType { QR, DEEPLINK, ETC }
|
||||
}
|
||||
|
||||
data class DisconnectSession(val topic: String) : WalletConnectAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class WalletConnectMiddleware {
|
|||
is WalletConnectAction.OpenSession -> {
|
||||
val index = action.wcUri.indexOf("@")
|
||||
when (action.wcUri[index + 1]) {
|
||||
'2' -> walletConnectRepository.pair(uri = action.wcUri)
|
||||
'2' -> walletConnectRepository.pair(uri = action.wcUri, source = action.source)
|
||||
'1' -> {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedDappRequest)
|
||||
store.dispatchOnMain(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.tap.store
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -29,7 +30,8 @@ internal class WalletConnectModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
listenToQrScanningUseCase(SourceType.WALLET_CONNECT)
|
||||
.getOrElse { emptyFlow() }
|
||||
.collect { store.dispatch(WalletConnectAction.OpenSession(it)) }
|
||||
.map { WalletConnectAction.OpenSession(it, WalletConnectAction.OpenSession.SourceType.QR) }
|
||||
.collect { store.dispatch(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ object ClipboardOrScanQrDialog {
|
|||
setTitle(context.getString(R.string.common_select_action))
|
||||
setMessage(context.getText(R.string.wallet_connect_clipboard_alert))
|
||||
setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.OpenSession(wcUri))
|
||||
store.dispatch(WalletConnectAction.OpenSession(wcUri, WalletConnectAction.OpenSession.SourceType.ETC))
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ ->
|
||||
store.dispatchNavigationAction {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue