Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-30 20:49:02 +05:00
parent 72cf995bd6
commit 9925da2864
15 changed files with 410 additions and 18 deletions

View file

@ -81,6 +81,12 @@ sealed class AnalyticsParam {
override val feeType: FeeType,
) : TxSentFrom("Swap"), TxData
data class Staking(
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
) : TxSentFrom("Staking"), TxData
data class Approve(
override val blockchain: String,
override val token: String,

View file

@ -25,6 +25,13 @@ sealed class YieldBalance {
.filter { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
}
fun getValidatorsCount(): Int {
return balance.items
.filterNot { it.validatorAddress.isNullOrBlank() }
.distinctBy { it.validatorAddress }
.size
}
}
data object Empty : YieldBalance()

View file

@ -77,4 +77,9 @@ sealed class TokenScreenAnalyticsEvent(
event = "Button - Token Trustline",
params = mapOf("Token" to tokenSymbol, "Blockchain" to blockchain),
)
data class StakingClicked(val token: String) : TokenScreenAnalyticsEvent(
event = "Staking Clicked",
params = mapOf("Token" to token),
)
}

View file

@ -0,0 +1,157 @@
package com.tangem.features.staking.impl.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
internal sealed class StakingAnalyticsEvents(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent(
category = "Staking",
event = event,
params = params,
) {
data class StakingInfoScreenOpened(
val validatorsCount: Int,
val token: String,
) : StakingAnalyticsEvents(
event = "Staking Info Screen Opened",
params = mapOf(
"Validators Count" to validatorsCount.toString(),
AnalyticsParam.TOKEN_PARAM to token,
),
)
data class WhatIsStaking(val token: String) : StakingAnalyticsEvents(
event = "Link - What Is Staking",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
),
)
data class AmountScreenOpened(val token: String) : StakingAnalyticsEvents(
event = "Amount Screen Opened",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
),
)
data class ConfirmationScreenOpened(
val token: String,
val validator: String,
val action: StakingActionType,
) : StakingAnalyticsEvents(
event = "Confirmation Screen Opened",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
"Validator" to validator,
"Action" to action.name,
),
)
data class StakeInProgressScreenOpened(
val validator: String,
val token: String,
) : StakingAnalyticsEvents(
event = "Stake In Progress Screen Opened",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
"Validator" to validator,
),
)
data class RewardScreenOpened(val token: String) : StakingAnalyticsEvents(
event = "Reward Screen Opened",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
),
)
data class AmountSelectCurrency(
val token: String,
val isAppCurrency: Boolean,
) : StakingAnalyticsEvents(
event = "Selected Currency",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
AnalyticsParam.TYPE to if (isAppCurrency) "App Currency" else "Token",
),
)
data class ButtonMax(val token: String) : StakingAnalyticsEvents(
event = "Button - Max",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
),
)
data class ButtonCancel(
val source: StakeScreenSource,
val token: String,
) : StakingAnalyticsEvents(
event = "Button - Cancel",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
AnalyticsParam.SOURCE to source.name,
),
)
data class ButtonValidator(
val source: StakeScreenSource,
val token: String,
) : StakingAnalyticsEvents(
event = "Button - Validator",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
AnalyticsParam.SOURCE to source.name,
),
)
data class ButtonRewards(
val token: String,
) : StakingAnalyticsEvents(
event = "Button - Rewards",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
),
)
data class ButtonAction(
val action: String,
val token: String,
val validator: String,
) : StakingAnalyticsEvents(
event = "Button - $action",
params = mapOf(
AnalyticsParam.TOKEN_PARAM to token,
"Validator" to validator,
),
)
data object ButtonShare : StakingAnalyticsEvents(event = "Button - Share")
data object ButtonExplore : StakingAnalyticsEvents(event = "Button - Explore")
data class StakingError(
val token: String,
) : StakingAnalyticsEvents(
event = "Errors",
params = mapOf(AnalyticsParam.TOKEN_PARAM to token),
)
data class TransactionError(
val token: String,
) : StakingAnalyticsEvents(
event = "Error - Transaction Rejected",
params = mapOf(AnalyticsParam.TOKEN_PARAM to token),
)
}
enum class StakeScreenSource {
Info,
Amount,
Confirmation,
Validators,
}

View file

@ -0,0 +1,104 @@
package com.tangem.features.staking.impl.analytics.utils
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.features.staking.impl.analytics.StakeScreenSource
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
import com.tangem.features.staking.impl.presentation.state.*
internal class StakingAnalyticSender(
private val analyticsEventHandler: AnalyticsEventHandler,
) {
fun initialInfoScreen(value: StakingUiState) {
val initialInfoState = value.initialInfoState as? StakingStates.InitialInfoState.Data
val validatorState = initialInfoState?.yieldBalance as? InnerYieldBalanceState.Data
val validatorCount = validatorState?.balance
?.filterNot { it.validator?.address.isNullOrBlank() }
?.distinctBy { it.validator?.address }
?.size ?: 0
analyticsEventHandler.send(
StakingAnalyticsEvents.StakingInfoScreenOpened(
validatorsCount = validatorCount,
token = value.cryptoCurrencySymbol,
),
)
}
fun confirmationScreen(value: StakingUiState) {
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
val validatorName = validatorState?.chosenValidator?.name ?: return
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) return
analyticsEventHandler.send(
StakingAnalyticsEvents.ConfirmationScreenOpened(
token = value.cryptoCurrencySymbol,
validator = validatorName,
action = when (value.actionType) {
StakingActionCommonType.ENTER -> StakingActionType.STAKE
StakingActionCommonType.EXIT -> StakingActionType.UNSTAKE
StakingActionCommonType.PENDING_REWARDS,
StakingActionCommonType.PENDING_OTHER,
-> confirmationState.pendingAction?.type ?: StakingActionType.UNKNOWN
},
),
)
}
fun screenCancel(value: StakingUiState) {
analyticsEventHandler.send(
StakingAnalyticsEvents.ButtonCancel(
source = when (value.currentStep) {
StakingStep.InitialInfo -> StakeScreenSource.Info
StakingStep.Amount -> StakeScreenSource.Amount
StakingStep.Confirmation -> StakeScreenSource.Confirmation
StakingStep.Validators,
StakingStep.RewardsValidators,
-> StakeScreenSource.Validators
},
token = value.cryptoCurrencyName,
),
)
}
fun sendTransactionApprovalAnalytics(tokenCryptoCurrency: CryptoCurrency) {
analyticsEventHandler.send(
Basic.TransactionSent(
sentFrom = AnalyticsParam.TxSentFrom.Approve(
blockchain = tokenCryptoCurrency.network.name,
token = tokenCryptoCurrency.symbol,
feeType = AnalyticsParam.FeeType.Normal,
permissionType = ApproveType.LIMITED.name,
),
memoType = Basic.TransactionSent.MemoType.Null,
),
)
}
fun sendTransactionStakingAnalytics(cryptoCurrency: CryptoCurrency, validator: String) {
analyticsEventHandler.send(
Basic.TransactionSent(
sentFrom = AnalyticsParam.TxSentFrom.Staking(
blockchain = cryptoCurrency.network.name,
token = cryptoCurrency.symbol,
feeType = AnalyticsParam.FeeType.Normal,
),
memoType = Basic.TransactionSent.MemoType.Null,
),
)
analyticsEventHandler.send(
StakingAnalyticsEvents.StakeInProgressScreenOpened(
validator = validator,
token = cryptoCurrency.symbol,
),
)
}
}

View file

@ -54,6 +54,7 @@ internal class StakingFragment : ComposeFragment() {
StakingStateRouter(
appRouter = appRouter,
stateController = stateController,
analyticsEventsHandler = analyticsEventsHandler,
),
)
}

View file

@ -68,6 +68,7 @@ internal class StakingStateController @Inject constructor(
clickIntents = StakingClickIntentsStub,
walletName = "",
cryptoCurrencyName = "",
cryptoCurrencySymbol = "",
currentStep = StakingStep.InitialInfo,
initialInfoState = StakingStates.InitialInfoState.Empty(),
amountState = AmountState.Empty(),

View file

@ -1,14 +1,21 @@
package com.tangem.features.staking.impl.presentation.state
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
internal class StakingStateRouter(
private val appRouter: AppRouter,
private val stateController: StakingStateController,
private val analyticsEventsHandler: AnalyticsEventHandler,
) {
private val analyticSender = StakingAnalyticSender(analyticsEventsHandler)
fun onBackClick() {
analyticSender.screenCancel(stateController.value)
appRouter.pop()
stateController.clear()
}
@ -48,14 +55,21 @@ internal class StakingStateRouter(
}
private fun showInitial() {
analyticSender.initialInfoScreen(stateController.value)
stateController.update { it.copy(currentStep = StakingStep.InitialInfo) }
}
fun showRewardsValidators() {
private fun showRewardsValidators() {
analyticsEventsHandler.send(
StakingAnalyticsEvents.RewardScreenOpened(stateController.value.cryptoCurrencySymbol),
)
stateController.update { it.copy(currentStep = StakingStep.RewardsValidators) }
}
fun showAmount() {
private fun showAmount() {
analyticsEventsHandler.send(
StakingAnalyticsEvents.AmountScreenOpened(stateController.value.cryptoCurrencySymbol),
)
stateController.update { it.copy(currentStep = StakingStep.Amount) }
}
@ -63,7 +77,8 @@ internal class StakingStateRouter(
stateController.update { it.copy(currentStep = StakingStep.Validators) }
}
fun showConfirmation() {
private fun showConfirmation() {
analyticSender.confirmationScreen(stateController.value)
stateController.update { it.copy(currentStep = StakingStep.Confirmation) }
}
}

View file

@ -27,6 +27,7 @@ internal data class StakingUiState(
val clickIntents: StakingClickIntents,
val walletName: String,
val cryptoCurrencyName: String,
val cryptoCurrencySymbol: String,
val currentStep: StakingStep,
val initialInfoState: StakingStates.InitialInfoState,
val amountState: AmountState,

View file

@ -69,4 +69,6 @@ internal object StakingClickIntentsStub : StakingClickIntents {
override fun onNotificationCancel(notification: Class<out NotificationUM>) {}
override fun openTokenDetails(cryptoCurrency: CryptoCurrency) {}
override fun onActiveStakeAnalytic() {}
}

View file

@ -56,16 +56,19 @@ internal class SetInitialDataStateTransformer(
}
private val yieldBalancesConverter by lazy(LazyThreadSafetyMode.NONE) {
YieldBalancesConverter(cryptoCurrencyStatusProvider, appCurrencyProvider, yield)
YieldBalancesConverter(
cryptoCurrencyStatusProvider,
appCurrencyProvider,
yield,
)
}
override fun transform(prevState: StakingUiState): StakingUiState {
val cryptoCurrencyName = cryptoCurrencyStatusProvider().currency.name
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
return prevState.copy(
title = TextReference.EMPTY,
walletName = userWalletProvider.invoke().name,
cryptoCurrencyName = cryptoCurrencyName,
cryptoCurrencyName = cryptoCurrency.name,
cryptoCurrencySymbol = cryptoCurrency.symbol,
clickIntents = clickIntents,
currentStep = StakingStep.InitialInfo,
initialInfoState = createInitialInfoState(),

View file

@ -173,8 +173,9 @@ private fun LazyListScope.activeStakingBlock(
) { balance ->
ActiveStakingBlock(
balance = balance,
onClick = clickIntents::onActiveStake,
isBalanceHidden = isBalanceHidden,
onClick = clickIntents::onActiveStake,
onAnalytic = clickIntents::onActiveStakeAnalytic,
modifier = Modifier
.animateItemPlacement()
.then(
@ -268,8 +269,9 @@ private fun StakingRewardBlock(
@Composable
private fun ActiveStakingBlock(
balance: BalanceState,
onClick: (BalanceState) -> Unit,
isBalanceHidden: Boolean,
onClick: (BalanceState) -> Unit,
onAnalytic: () -> Unit,
modifier: Modifier = Modifier,
) {
val (icon, iconTint) = balance.type.getIcon()
@ -287,7 +289,10 @@ private fun ActiveStakingBlock(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
enabled = balance.isClickable,
onClick = { onClick(balance) },
onClick = {
onAnalytic()
onClick(balance)
},
),
)
}

View file

@ -39,6 +39,8 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
fun onActiveStake(activeStake: BalanceState)
fun onActiveStakeAnalytic()
fun showApprovalBottomSheet()
fun onApprovalClick()

View file

@ -15,6 +15,7 @@ import com.tangem.common.routing.bundle.unbundle
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -29,7 +30,9 @@ import com.tangem.domain.staking.*
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.stakekit.PendingAction
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
@ -45,6 +48,9 @@ import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.staking.impl.analytics.StakeScreenSource
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
import com.tangem.features.staking.impl.navigation.InnerStakingRouter
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
@ -106,6 +112,7 @@ internal class StakingViewModel @Inject constructor(
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
private val isAnyTokenStakedUseCase: IsAnyTokenStakedUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
@DelayedWork private val coroutineScope: CoroutineScope,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
@ -135,6 +142,8 @@ internal class StakingViewModel @Inject constructor(
private var userWallet: UserWallet by Delegates.notNull()
private var appCurrency: AppCurrency by Delegates.notNull()
private var isInitialInfoAnalyticSent: Boolean = false
private val stakingEventFactory: StakingEventFactory
get() = StakingEventFactory(
stateController = stateController,
@ -142,6 +151,10 @@ internal class StakingViewModel @Inject constructor(
onFailedTxEmailClick = ::onFailedTxEmailClick,
)
private val stakingAnalyticSender = StakingAnalyticSender(
analyticsEventHandler = analyticsEventHandler,
)
private var stakingApproval: StakingApproval = StakingApproval.Empty
private var isAmountSubtractAvailable: Boolean = false
private val allowanceTaskScheduler = SingleTaskScheduler<BigDecimal>()
@ -270,6 +283,7 @@ internal class StakingViewModel @Inject constructor(
),
).getOrElse {
Timber.e(it.toString())
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
stakingEventFactory.createStakingErrorAlert(it)
stateController.update(SetConfirmationStateResetAssentTransformer)
return@launch
@ -284,6 +298,7 @@ internal class StakingViewModel @Inject constructor(
transactionId = transaction.id,
).getOrElse {
Timber.e(it.toString())
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
stakingEventFactory.createStakingErrorAlert(it)
stateController.update(SetConfirmationStateResetAssentTransformer)
return@launch
@ -301,6 +316,7 @@ internal class StakingViewModel @Inject constructor(
fullTransactionsData = fullTransactionsData,
fee = fee,
pendingAction = confirmationState.pendingAction,
validator = validatorState.chosenValidator.name,
)
}
}
@ -326,6 +342,7 @@ internal class StakingViewModel @Inject constructor(
type = pendingAction?.type,
),
).getOrElse {
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
stateController.update(AddStakingErrorTransformer())
updateNotifications(GetFeeError.UnknownError)
return
@ -365,6 +382,7 @@ internal class StakingViewModel @Inject constructor(
updateNotifications()
},
ifLeft = {
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencyName))
stateController.update(AddStakingErrorTransformer())
updateNotifications(it)
},
@ -389,6 +407,7 @@ internal class StakingViewModel @Inject constructor(
}
override fun onInitialInfoBannerClick() {
analyticsEventHandler.send(StakingAnalyticsEvents.WhatIsStaking(cryptoCurrencyStatus.currency.symbol))
innerRouter.openUrl(WHAT_IS_STAKING_ARTICLE_URL)
}
@ -409,26 +428,47 @@ internal class StakingViewModel @Inject constructor(
}
override fun onMaxValueClick() {
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonMax(cryptoCurrencyStatus.currency.symbol))
stateController.update(AmountMaxValueStateTransformer(cryptoCurrencyStatus, yield))
}
override fun onCurrencyChangeClick(isFiat: Boolean) {
analyticsEventHandler.send(
StakingAnalyticsEvents.AmountSelectCurrency(cryptoCurrencyStatus.currency.symbol, isFiat),
)
stateController.update(AmountCurrencyChangeStateTransformer(cryptoCurrencyStatus, isFiat))
}
override fun openValidators() = stakingStateRouter.showValidators()
override fun openValidators() {
analyticsEventHandler.send(
StakingAnalyticsEvents.ButtonValidator(
source = StakeScreenSource.Confirmation,
token = cryptoCurrencyStatus.currency.symbol,
),
)
stakingStateRouter.showValidators()
}
override fun onValidatorSelect(validator: Yield.Validator) {
stateController.update(ValidatorSelectChangeTransformer(validator))
}
override fun openRewardsValidators() {
analyticsEventHandler.send(
StakingAnalyticsEvents.ButtonRewards(value.cryptoCurrencyName),
)
val rewardsValidators =
stateController.value.rewardsValidatorsState as? StakingStates.RewardsValidatorsState.Data
val rewards = rewardsValidators?.rewards
if (rewards != null && rewards.isSingleItem()) {
onActiveStake(rewards.first())
} else {
analyticsEventHandler.send(
StakingAnalyticsEvents.ButtonValidator(
source = StakeScreenSource.Info,
token = cryptoCurrencyStatus.currency.symbol,
),
)
onNextClick(actionTypeToOverwrite = StakingActionCommonType.PENDING_REWARDS)
}
}
@ -463,6 +503,15 @@ internal class StakingViewModel @Inject constructor(
}
}
override fun onActiveStakeAnalytic() {
analyticsEventHandler.send(
StakingAnalyticsEvents.ButtonValidator(
source = StakeScreenSource.Info,
token = cryptoCurrencyStatus.currency.symbol,
),
)
}
override fun showApprovalBottomSheet() {
stateController.update(
ShowApprovalBottomSheetTransformer(
@ -503,6 +552,7 @@ internal class StakingViewModel @Inject constructor(
).fold(
ifLeft = { error ->
Timber.e(error.toString())
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencyName))
stateController.update(
SetConfirmationStateAssentApprovalTransformer(
appCurrencyProvider = Provider { appCurrency },
@ -524,6 +574,7 @@ internal class StakingViewModel @Inject constructor(
).fold(
ifLeft = { error ->
Timber.e(error.toString())
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencyName))
stateController.update(
SetConfirmationStateAssentApprovalTransformer(
appCurrencyProvider = Provider { appCurrency },
@ -535,6 +586,7 @@ internal class StakingViewModel @Inject constructor(
stateController.update(SetConfirmationStateResetAssentTransformer)
},
ifRight = {
stakingAnalyticSender.sendTransactionApprovalAnalytics(tokenCryptoCurrency)
stateController.update(SetApprovalInProgressTransformer)
stateController.update(DismissBottomSheetStateTransformer())
awaitForAllowance(confirmationState.pendingAction)
@ -652,10 +704,10 @@ internal class StakingViewModel @Inject constructor(
}
override fun onExploreClick() {
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonExplore)
val confirmationDataState = uiState.value.confirmationState as? StakingStates.ConfirmationState.Data
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
val txUrl = transactionDoneState?.txUrl
if (txUrl != null) {
innerRouter.openUrl(txUrl)
}
@ -666,12 +718,11 @@ internal class StakingViewModel @Inject constructor(
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
val txUrl = transactionDoneState?.txUrl
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonShare)
if (txUrl != null) {
vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click)
stakingEventFactory.createShareDialog(txUrl = txUrl)
}
// TODO staking [REDACTED_TASK_KEY]
}
override fun onFailedTxEmailClick(errorMessage: String) {
@ -742,6 +793,17 @@ internal class StakingViewModel @Inject constructor(
.onEach { maybeStatus ->
maybeStatus.fold(
ifRight = { status ->
if (!isInitialInfoAnalyticSent) {
isInitialInfoAnalyticSent = true
val balances = status.value.yieldBalance as? YieldBalance.Data
analyticsEventHandler.send(
StakingAnalyticsEvents.StakingInfoScreenOpened(
validatorsCount = balances?.getValidatorsCount() ?: 0,
token = status.currency.symbol,
),
)
}
feeCryptoCurrencyStatus =
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull()
cryptoCurrencyStatus = status
@ -800,14 +862,33 @@ internal class StakingViewModel @Inject constructor(
fullTransactionsData: List<FullTransactionData>,
fee: Fee,
pendingAction: PendingAction?,
validator: String,
) {
val cryptoCurrency = cryptoCurrencyStatus.currency
val action = when (value.actionType) {
StakingActionCommonType.ENTER -> StakingActionType.STAKE
StakingActionCommonType.EXIT -> StakingActionType.UNSTAKE
StakingActionCommonType.PENDING_REWARDS,
StakingActionCommonType.PENDING_OTHER,
-> pendingAction?.type
}
action?.let {
analyticsEventHandler.send(
StakingAnalyticsEvents.ButtonAction(
action = it.name,
token = value.cryptoCurrencyName,
validator = validator,
),
)
}
sendMultipleTransactionUseCase(
txsData = fullTransactionsData.map { it.tangemTransaction },
userWallet = userWallet,
network = cryptoCurrencyStatus.currency.network,
network = cryptoCurrency.network,
).fold(
ifLeft = { error ->
Timber.e(error.toString())
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
stateController.update(
SetConfirmationStateAssentTransformer(
appCurrencyProvider = Provider { appCurrency },
@ -820,6 +901,7 @@ internal class StakingViewModel @Inject constructor(
stateController.update(SetConfirmationStateResetAssentTransformer)
},
ifRight = { transactionHashes ->
stakingAnalyticSender.sendTransactionStakingAnalytics(cryptoCurrency, validator)
transactionsInProgress.clear()
submitHash(
transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id },
@ -828,9 +910,8 @@ internal class StakingViewModel @Inject constructor(
scheduleUpdates()
val txUrl = getExplorerTransactionUrlUseCase(
txHash = transactionHashes.last(),
networkId = cryptoCurrencyStatus.currency.network.id,
networkId = cryptoCurrency.network.id,
).getOrElse { "" }
stateController.update(
SetConfirmationStateCompletedTransformer(
appCurrencyProvider = Provider { appCurrency },
@ -852,6 +933,7 @@ internal class StakingViewModel @Inject constructor(
transactionHash = transactionHash,
)
.onLeft {
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
saveUnsubmittedHashUseCase.invoke(
transactionId = transactionId,
transactionHash = transactionHash,

View file

@ -466,6 +466,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onStakeBannerClick() {
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.StakingClicked(cryptoCurrency.symbol))
openStaking()
}