Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-04 12:09:36 +03:00
parent 184c94d9d3
commit 2938fda36e
12 changed files with 83 additions and 67 deletions

View file

@ -285,5 +285,6 @@ sealed class AnalyticsParam {
const val ENS_ADDRESS = "ENS Address" const val ENS_ADDRESS = "ENS Address"
const val ACCOUNT_DERIVATION_FROM = "Account Derivation (from)" const val ACCOUNT_DERIVATION_FROM = "Account Derivation (from)"
const val FEE_TOKEN = "Fee Token" const val FEE_TOKEN = "Fee Token"
const val ACCOUNT_DERIVATION = "Account Derivation"
} }
} }

View file

@ -5,6 +5,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.walletconnect.sign.SignStateConverter.toPreSign import com.tangem.data.walletconnect.sign.SignStateConverter.toPreSign
import com.tangem.data.walletconnect.sign.SignStateConverter.toResult import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
import com.tangem.data.walletconnect.sign.SignStateConverter.toSigning import com.tangem.data.walletconnect.sign.SignStateConverter.toSigning
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.WcRequestError.Companion.code import com.tangem.domain.walletconnect.model.WcRequestError.Companion.code
@ -71,6 +72,7 @@ internal class WcSignUseCaseDelegate<MiddleAction, SignModel>(
network = context.network, network = context.network,
errorCode = error.code(), errorCode = error.code(),
errorMessage = errorMessage, errorMessage = errorMessage,
accountDerivation = context.session.account?.derivationIndex?.value,
) )
analytics.send(event) analytics.send(event)
} }

View file

@ -185,4 +185,10 @@ sealed interface Account {
error("Not yet implemented") error("Not yet implemented")
} }
} }
} }
val Account.derivationIndex: DerivationIndex?
get() = when (this) {
is Account.CryptoPortfolio -> derivationIndex
is Account.Payment -> TODO("[REDACTED_JIRA]")
}

View file

@ -34,8 +34,17 @@ sealed class WcAnalyticEvents(
), ),
) )
class PairButtonConnect : WcAnalyticEvents( class PairButtonConnect(
dAppName: String,
accountDerivation: Int?,
) : WcAnalyticEvents(
event = "Button - Connect", event = "Button - Connect",
params = buildMap {
put(AnalyticsParam.DAPP_NAME, dAppName)
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
) )
class PairRequested( class PairRequested(
@ -114,6 +123,7 @@ sealed class WcAnalyticEvents(
rawRequest: WcSdkSessionRequest, rawRequest: WcSdkSessionRequest,
network: Network, network: Network,
emulationStatus: EmulationStatus?, emulationStatus: EmulationStatus?,
accountDerivation: Int?,
securityStatus: CheckDAppResult, securityStatus: CheckDAppResult,
) : WcAnalyticEvents( ) : WcAnalyticEvents(
event = "Signature Request Received", event = "Signature Request Received",
@ -124,6 +134,7 @@ sealed class WcAnalyticEvents(
AnalyticsParam.BLOCKCHAIN to network.name, AnalyticsParam.BLOCKCHAIN to network.name,
AnalyticsParam.EMULATION_STATUS to emulationStatus?.status, AnalyticsParam.EMULATION_STATUS to emulationStatus?.status,
AnalyticsParam.TYPE to securityStatus.toAnalyticVerificationStatus(), AnalyticsParam.TYPE to securityStatus.toAnalyticVerificationStatus(),
AnalyticsParam.ACCOUNT_DERIVATION to accountDerivation?.toString(),
).mapNotNullValues { it.value }, ).mapNotNullValues { it.value },
) { ) {
enum class EmulationStatus(val status: String) { enum class EmulationStatus(val status: String) {
@ -137,15 +148,19 @@ sealed class WcAnalyticEvents(
rawRequest: WcSdkSessionRequest, rawRequest: WcSdkSessionRequest,
network: Network, network: Network,
securityStatus: CheckDAppResult, securityStatus: CheckDAppResult,
accountDerivation: Int?,
) : WcAnalyticEvents( ) : WcAnalyticEvents(
event = "Signature Request Handled", event = "Signature Request Handled",
params = mapOf( params = buildMap {
AnalyticsParam.DAPP_NAME to rawRequest.dAppMetaData.name, put(AnalyticsParam.DAPP_NAME, rawRequest.dAppMetaData.name)
AnalyticsParam.DAPP_URL to rawRequest.dAppMetaData.url, put(AnalyticsParam.DAPP_URL, rawRequest.dAppMetaData.url)
AnalyticsParam.METHOD_NAME to rawRequest.request.method, put(AnalyticsParam.METHOD_NAME, rawRequest.request.method)
AnalyticsParam.BLOCKCHAIN to network.name, put(AnalyticsParam.BLOCKCHAIN, network.name)
AnalyticsParam.TYPE to securityStatus.toAnalyticVerificationStatus(), put(AnalyticsParam.TYPE, securityStatus.toAnalyticVerificationStatus())
), accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
), AppsFlyerIncludedEvent ), AppsFlyerIncludedEvent
class SignatureRequestFailed( class SignatureRequestFailed(
@ -153,16 +168,18 @@ sealed class WcAnalyticEvents(
network: Network, network: Network,
errorCode: String, errorCode: String,
errorMessage: String, errorMessage: String,
accountDerivation: Int?,
) : WcAnalyticEvents( ) : WcAnalyticEvents(
event = "Signature Request Failed", event = "Signature Request Failed",
params = mapOf( params = buildMap {
AnalyticsParam.DAPP_NAME to rawRequest.dAppMetaData.name, put(AnalyticsParam.DAPP_NAME, rawRequest.dAppMetaData.name)
AnalyticsParam.DAPP_URL to rawRequest.dAppMetaData.url, put(AnalyticsParam.DAPP_URL, rawRequest.dAppMetaData.url)
AnalyticsParam.METHOD_NAME to rawRequest.request.method, put(AnalyticsParam.METHOD_NAME, rawRequest.request.method)
AnalyticsParam.BLOCKCHAIN to network.name, put(AnalyticsParam.BLOCKCHAIN, network.name)
AnalyticsParam.ERROR_CODE to errorCode, put(AnalyticsParam.ERROR_CODE, errorCode)
AnalyticsParam.ERROR_DESCRIPTION to errorMessage, put(AnalyticsParam.ERROR_DESCRIPTION, errorMessage)
), accountDerivation?.let { put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString()) }
},
) )
class SignatureRequestReceivedFailed( class SignatureRequestReceivedFailed(
@ -279,23 +296,4 @@ fun CheckDAppResult.toAnalyticVerificationStatus(): String = when (this) {
SAFE -> DAppVerificationStatus.Verified SAFE -> DAppVerificationStatus.Verified
UNSAFE -> DAppVerificationStatus.Risky UNSAFE -> DAppVerificationStatus.Risky
FAILED_TO_VERIFY -> DAppVerificationStatus.Unknown FAILED_TO_VERIFY -> DAppVerificationStatus.Unknown
}.status }.status
sealed class WcAnalyticAccountEvents(
event: String,
params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category = WC_CATEGORY_ACCOUNT_NAME, event = event, params = params) {
data class PairButtonConnect(
private val accountDerivation: Int,
) : WcAnalyticAccountEvents(
event = "Button - Connect",
params = mapOf(
"Account Derivation" to accountDerivation.toString(),
),
)
companion object {
const val WC_CATEGORY_ACCOUNT_NAME = "WalletConnect / Account"
}
}

View file

@ -8,7 +8,7 @@ internal sealed class CustomTokenAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = mapOf(),
) : AnalyticsEvent( ) : AnalyticsEvent(
category = "Manage Tokens / Custom", category = "Manage Tokens / Custom Token",
event = event, event = event,
params = params, params = params,
) { ) {

View file

@ -22,16 +22,18 @@ internal sealed class SendWithSwapAnalyticEvents(
val feeType: AnalyticsParam.FeeType, val feeType: AnalyticsParam.FeeType,
val fromToken: CryptoCurrency, val fromToken: CryptoCurrency,
val toToken: CryptoCurrency, val toToken: CryptoCurrency,
val fromDerivationIndex: Int?,
) : SendWithSwapAnalyticEvents( ) : SendWithSwapAnalyticEvents(
event = "Send With Swap In Progress Screen Opened", event = "Send With Swap In Progress Screen Opened",
params = mapOf( params = buildMap {
PROVIDER to providerName, put(PROVIDER, providerName)
FEE_TYPE to if (feeType is AnalyticsParam.FeeType.Normal) "Market" else "Fast", put(FEE_TYPE, if (feeType is AnalyticsParam.FeeType.Normal) "Market" else "Fast")
SEND_TOKEN to fromToken.symbol, put(SEND_TOKEN, fromToken.symbol)
RECEIVE_TOKEN to toToken.symbol, put(RECEIVE_TOKEN, toToken.symbol)
SEND_BLOCKCHAIN to fromToken.network.name, put(SEND_BLOCKCHAIN, fromToken.network.name)
RECEIVE_BLOCKCHAIN to toToken.network.name, put(RECEIVE_BLOCKCHAIN, toToken.network.name)
), if (fromDerivationIndex != null) put("Account Derivation (from)", fromDerivationIndex.toString())
},
), AppsFlyerIncludedEvent ), AppsFlyerIncludedEvent
data class NoticeCanNotSwapToken( data class NoticeCanNotSwapToken(

View file

@ -448,6 +448,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
val toCurrency = confirmData.toCryptoCurrencyStatus?.currency ?: return val toCurrency = confirmData.toCryptoCurrencyStatus?.currency ?: return
val feeSelectorUM = uiState.value.feeSelectorUM as? FeeSelectorUM.Content ?: return val feeSelectorUM = uiState.value.feeSelectorUM as? FeeSelectorUM.Content ?: return
val feeType = feeSelectorUM.toAnalyticType() val feeType = feeSelectorUM.toAnalyticType()
val fromDerivationIndex = confirmData.fromAccount?.derivationIndex?.value
analyticsEventHandler.send( analyticsEventHandler.send(
SendWithSwapAnalyticEvents.TransactionScreenOpened( SendWithSwapAnalyticEvents.TransactionScreenOpened(
@ -455,6 +456,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
feeType = feeType, feeType = feeType,
fromToken = fromCurrency, fromToken = fromCurrency,
toToken = toCurrency, toToken = toCurrency,
fromDerivationIndex = fromDerivationIndex,
), ),
) )
analyticsEventHandler.send( analyticsEventHandler.send(

View file

@ -98,16 +98,17 @@ sealed class SwapEvents(
val toDerivationIndex: Int?, val toDerivationIndex: Int?,
) : SwapEvents( ) : SwapEvents(
event = "Swap in Progress Screen Opened", event = "Swap in Progress Screen Opened",
params = mapOf( params = buildMap {
"Provider" to provider.name, put("Provider", provider.name)
"Commission" to if (commission == FeeType.NORMAL) "Market" else "Fast", put("Commission", if (commission == FeeType.NORMAL) "Market" else "Fast")
"Send Token" to sendToken, put("Send Token", sendToken)
"Receive Token" to receiveToken, put("Receive Token", receiveToken)
"Send Blockchain" to sendBlockchain, put("Send Blockchain", sendBlockchain)
"Receive Blockchain" to receiveBlockchain, put("Receive Blockchain", receiveBlockchain)
"Account Derivation From or To (optional)" to "$fromDerivationIndex, $toDerivationIndex", if (fromDerivationIndex != null) put("Account Derivation (from)", fromDerivationIndex.toString())
FEE_TOKEN to feeToken, if (toDerivationIndex != null) put("Account Derivation (to)", toDerivationIndex.toString())
), put(FEE_TOKEN, feeToken)
},
), AppsFlyerIncludedEvent ), AppsFlyerIncludedEvent
class ProviderClicked : SwapEvents("Provider Clicked") class ProviderClicked : SwapEvents("Provider Clicked")

View file

@ -27,12 +27,12 @@ import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.walletconnect.WcAnalyticAccountEvents
import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.model.WcPairError import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcPairError.Unknown import com.tangem.domain.walletconnect.model.WcPairError.Unknown
@ -324,15 +324,12 @@ internal class WcPairModel @Inject constructor(
val account = selectedPortfolio?.second?.account val account = selectedPortfolio?.second?.account
modelScope.launch { modelScope.launch {
if (selectorController.isAccountModeSync() && account != null) { analytics.send(
val derivationIndex = when (account) { WcAnalyticEvents.PairButtonConnect(
is Account.CryptoPortfolio -> account.derivationIndex.value dAppName = sessionProposal.dAppMetaData.name,
is Account.Payment -> TODO("[REDACTED_JIRA]") accountDerivation = account?.derivationIndex?.value,
} ),
analytics.send(WcAnalyticAccountEvents.PairButtonConnect(derivationIndex)) )
} else {
analytics.send(WcAnalyticEvents.PairButtonConnect())
}
} }
wcPairUseCase.approve( wcPairUseCase.approve(
WcSessionApprove( WcSessionApprove(

View file

@ -11,6 +11,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.usecase.method.WcAddNetworkUseCase import com.tangem.domain.walletconnect.usecase.method.WcAddNetworkUseCase
@ -131,13 +132,13 @@ internal class WcAddNetworkModel @Inject constructor(
private fun sendSignatureReceivedAnalytics(useCase: WcAddNetworkUseCase) { private fun sendSignatureReceivedAnalytics(useCase: WcAddNetworkUseCase) {
if (signatureReceivedAnalyticsSendState.value) return if (signatureReceivedAnalyticsSendState.value) return
analytics.send( analytics.send(
WcAnalyticEvents.SignatureRequestReceived( WcAnalyticEvents.SignatureRequestReceived(
rawRequest = useCase.rawSdkRequest, rawRequest = useCase.rawSdkRequest,
network = useCase.network, network = useCase.network,
emulationStatus = null, emulationStatus = null,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY, securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
accountDerivation = useCase.session.account?.derivationIndex?.value,
), ),
) )

View file

@ -23,6 +23,7 @@ import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.core.lce.Lce import com.tangem.domain.core.lce.Lce
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
@ -426,6 +427,7 @@ internal class WcSendTransactionModel @Inject constructor(
rawRequest = useCase.rawSdkRequest, rawRequest = useCase.rawSdkRequest,
network = useCase.network, network = useCase.network,
securityStatus = securityStatusState.value.toCheckDAppResult(), securityStatus = securityStatusState.value.toCheckDAppResult(),
accountDerivation = useCase.session.account?.derivationIndex?.value,
) )
analytics.send(event) analytics.send(event)
showSuccessSignMessage() showSuccessSignMessage()
@ -465,6 +467,7 @@ internal class WcSendTransactionModel @Inject constructor(
network = useCase.network, network = useCase.network,
emulationStatus = emulationStatus, emulationStatus = emulationStatus,
securityStatus = securityCheck.toCheckDAppResult(), securityStatus = securityCheck.toCheckDAppResult(),
accountDerivation = useCase.session.account?.derivationIndex?.value,
), ),
) )

View file

@ -12,6 +12,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.model.WcEthMethod
@ -143,6 +144,7 @@ internal class WcSignTransactionModel @Inject constructor(
rawRequest = useCase.rawSdkRequest, rawRequest = useCase.rawSdkRequest,
network = useCase.network, network = useCase.network,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY, securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
accountDerivation = useCase.session.account?.derivationIndex?.value,
) )
analytics.send(event) analytics.send(event)
showSuccessSignMessage() showSuccessSignMessage()
@ -171,6 +173,7 @@ internal class WcSignTransactionModel @Inject constructor(
network = useCase.network, network = useCase.network,
emulationStatus = null, emulationStatus = null,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY, securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
accountDerivation = useCase.session.account?.derivationIndex?.value,
), ),
) )