Updated on 2026-08-14
This commit is contained in:
parent
a08f570a34
commit
72ea156756
18 changed files with 276 additions and 590 deletions
|
|
@ -179,6 +179,7 @@ sealed class AnalyticsParam {
|
|||
const val ERROR_CODE = "Error Code"
|
||||
const val ERROR_KEY = "Error Key"
|
||||
const val ERROR_TYPE = "Error Type"
|
||||
const val ERROR_MESSAGE = "Error Message"
|
||||
const val CREATION_TYPE = "Creation type"
|
||||
const val DAPP_NAME = "DApp Name"
|
||||
const val DAPP_URL = "DApp Url"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.core.analytics.di
|
|||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.ParamsInterceptorHolder
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.domain.analytics.repository.AnalyticsRepository
|
||||
import dagger.Module
|
||||
|
|
@ -20,6 +21,12 @@ internal object AnalyticsModule {
|
|||
return Analytics // todo replace after refactoring calling Analytics in whole project
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideParamsInterceptorHolder(): ParamsInterceptorHolder {
|
||||
return Analytics // todo replace after refactoring calling Analytics in whole project
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideOneTimeEventFilter(analyticsRepository: AnalyticsRepository): OneTimeEventFilter {
|
||||
return OneTimeEventFilter(analyticsRepository)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO.RewardT
|
|||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO.ValidatorDTO.ValidatorStatusDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.action.StakingActionStatusDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.action.StakingActionTypeDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.error.AccessDeniedErrorTypeDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorMessageDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.StakingTransactionStatusDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.StakingTransactionTypeDTO
|
||||
|
||||
|
|
@ -40,9 +38,6 @@ fun Moshi.Builder.addStakeKitEnumFallbackAdapters(): Moshi.Builder {
|
|||
StakingTransactionStatusDTO::class.java to StakingTransactionStatusDTO.UNKNOWN,
|
||||
StakingTransactionTypeDTO::class.java to StakingTransactionTypeDTO.UNKNOWN,
|
||||
ValidatorStatusDTO::class.java to ValidatorStatusDTO.UNKNOWN,
|
||||
// error enums
|
||||
AccessDeniedErrorTypeDTO::class.java to AccessDeniedErrorTypeDTO.UNKNOWN,
|
||||
StakeKitErrorMessageDTO::class.java to StakeKitErrorMessageDTO.UNKNOWN,
|
||||
)
|
||||
|
||||
return apply {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,21 @@ package com.tangem.datasource.api.stakekit.models.response.model.error
|
|||
import com.squareup.moshi.Json
|
||||
|
||||
class StakeKitErrorResponse(
|
||||
@Json(name = "message")
|
||||
val message: String? = null,
|
||||
@Json(name = "code")
|
||||
val code: Int? = null,
|
||||
@Json(name = "path")
|
||||
val path: String? = null,
|
||||
|
||||
@Json(name = "details")
|
||||
val details: StakeKitErrorDetailsDTO? = null,
|
||||
@Json(name = "message")
|
||||
val message: StakeKitErrorMessageDTO? = null,
|
||||
@Json(name = "level")
|
||||
val level: String? = null, // unused
|
||||
|
||||
// 403
|
||||
@Json(name = "type")
|
||||
val type: AccessDeniedErrorTypeDTO? = null,
|
||||
@Json(name = "code")
|
||||
val code: String? = null,
|
||||
val type: String? = null,
|
||||
@Json(name = "countryCode")
|
||||
val countryCode: String?,
|
||||
@Json(name = "regionCode")
|
||||
|
|
@ -30,135 +33,4 @@ data class StakeKitErrorDetailsDTO(
|
|||
val amount: String? = null,
|
||||
@Json(name = "yieldId")
|
||||
val yieldId: String? = null,
|
||||
)
|
||||
|
||||
enum class AccessDeniedErrorTypeDTO {
|
||||
@Json(name = "GEO_LOCATION")
|
||||
GEO_LOCATION,
|
||||
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
enum class StakeKitErrorMessageDTO {
|
||||
@Json(name = "MissingArgumentsError")
|
||||
MISSING_ARGUMENTS_ERROR,
|
||||
|
||||
@Json(name = "MinimumAmountNotReached")
|
||||
MINIMUM_AMOUNT_NOT_REACHED,
|
||||
|
||||
@Json(name = "YieldUnderMaintenanceError")
|
||||
YIELD_UNDER_MAINTENANCE_ERROR,
|
||||
|
||||
@Json(name = "InsufficientFundsError")
|
||||
INSUFFICIENT_FUNDS_ERROR,
|
||||
|
||||
@Json(name = "StakedPositionNotFoundError")
|
||||
STAKED_POSITION_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "InvalidAmountSubmittedError")
|
||||
INVALID_AMOUNT_SUBMITTED_ERROR,
|
||||
|
||||
@Json(name = "BalanceUnavailableError")
|
||||
BALANCE_UNAVAILABLE_ERROR,
|
||||
|
||||
@Json(name = "GasPriceUnavailableError")
|
||||
GAS_PRICE_UNAVAILABLE_ERROR,
|
||||
|
||||
@Json(name = "NotImplementedError")
|
||||
NOT_IMPLEMENTED_ERROR,
|
||||
|
||||
@Json(name = "TokenNotFoundError")
|
||||
TOKEN_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "BroadcastTransactionError")
|
||||
BROADCAST_TRANSACTION_ERROR,
|
||||
|
||||
@Json(name = "MissingGasPriceStrategyError")
|
||||
MISSING_GAS_PRICE_STRATEGY_ERROR,
|
||||
|
||||
@Json(name = "SubstrateMalformedTransactionHashError")
|
||||
SUBSTRATE_MALFORMED_TRANSACTION_HASH_ERROR,
|
||||
|
||||
@Json(name = "TronMaximumAmountOfValidatorsExceededError")
|
||||
TRON_MAXIMUM_AMOUNT_OF_VALIDATORS_EXCEEDED_ERROR,
|
||||
|
||||
@Json(name = "SubstratePoolNotFoundError")
|
||||
SUBSTRATE_POOL_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "SubstrateBondedAmountTooLowError")
|
||||
SUBSTRATE_BONDED_AMOUNT_TOO_LOW_ERROR,
|
||||
|
||||
@Json(name = "TronMissingResourceTypeArgumentError")
|
||||
TRON_MISSING_RESOURCE_TYPE_ARGUMENT_ERROR,
|
||||
|
||||
@Json(name = "AaveV3PoolFrozenError")
|
||||
AAVE_V3_POOL_FROZEN_ERROR,
|
||||
|
||||
@Json(name = "AaveV3TokenPairNotFoundError")
|
||||
AAVE_V3_TOKEN_PAIR_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "YearnVaultAtMaxCapacityError")
|
||||
YEARN_VAULT_AT_MAX_CAPACITY_ERROR,
|
||||
|
||||
@Json(name = "StETHNoWithdrawalRequestsFoundError")
|
||||
STETH_NO_WITHDRAWAL_REQUESTS_FOUND_ERROR,
|
||||
|
||||
@Json(name = "MorphoLendingPoolPausedError")
|
||||
MORPHO_LENDING_POOL_PAUSED_ERROR,
|
||||
|
||||
@Json(name = "NonceUnavailableError")
|
||||
NONCE_UNAVAILABLE_ERROR,
|
||||
|
||||
@Json(name = "CosmosAcccountNotFoundError")
|
||||
COSMOS_ACCOUNT_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "AvalancheMissingAdditionalAddressesArgumentError")
|
||||
AVALANCHE_MISSING_ADDITIONAL_ADDRESSES_ARGUMENT_ERROR,
|
||||
|
||||
@Json(name = "AvalancheValidatorInfoNotFoundError")
|
||||
AVALANCHE_VALIDATOR_INFO_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "SolanaTransactionSignatureVerificationFailureError")
|
||||
SOLANA_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE_ERROR,
|
||||
|
||||
@Json(name = "SolanaUnableTocreateStakeAccountError")
|
||||
SOLANA_UNABLE_TO_CREATE_STAKE_ACCOUNT_ERROR,
|
||||
|
||||
@Json(name = "SolanaStakeAmountTooLowError")
|
||||
SOLANA_STAKE_AMOUNT_TOO_LOW_ERROR,
|
||||
|
||||
@Json(name = "SolanaUnstakeAmountTooLowError")
|
||||
SOLANA_UNSTAKE_AMOUNT_TOO_LOW_ERROR,
|
||||
|
||||
@Json(name = "SolanaStakeAccountsNotFoundError")
|
||||
SOLANA_STAKE_ACCOUNTS_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "SolanaEligibleStakeAccountsNotFoundError")
|
||||
SOLANA_ELIGIBLE_STAKE_ACCOUNTS_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "TezosNoBalanceDelegatedError")
|
||||
TEZOS_NO_BALANCE_DELEGATED_ERROR,
|
||||
|
||||
@Json(name = "TezosMissingPubkeyArgumentError")
|
||||
TEZOS_MISSING_PUBKEY_ARGUMENT_ERROR,
|
||||
|
||||
@Json(name = "TezosEstimateRevealGasLimitError")
|
||||
TEZOS_ESTIMATE_REVEAL_GAS_LIMIT_ERROR,
|
||||
|
||||
@Json(name = "TezosBalanceAlreadyDelegatedError")
|
||||
TEZOS_BALANCE_ALREADY_DELEGATED_ERROR,
|
||||
|
||||
@Json(name = "BinanceAccountNotFoundError")
|
||||
BINANCE_ACCOUNT_NOT_FOUND_ERROR,
|
||||
|
||||
@Json(name = "BinanceMissingAccountNumberOrSequenceError")
|
||||
BINANCE_MISSING_ACCOUNT_NUMBER_OR_SEQUENCE_ERROR,
|
||||
|
||||
@Json(name = "GRTStakingDisabledError")
|
||||
GRT_STAKING_DISABLED_ERROR,
|
||||
|
||||
@Json(name = "GRTStakingDisabledLedgerLiveError")
|
||||
GRT_STAKING_DISABLED_LEDGER_LIVE_ERROR,
|
||||
|
||||
UNKNOWN,
|
||||
}
|
||||
)
|
||||
|
|
@ -1,19 +1,36 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.data.staking.converters.error.StakeKitErrorConverter
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
|
||||
internal class DefaultStakingErrorResolver(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val stakeKitErrorConverter: StakeKitErrorConverter,
|
||||
) : StakingErrorResolver {
|
||||
|
||||
override fun resolve(throwable: Throwable): StakingError {
|
||||
return if (throwable is ApiResponseError.HttpException) {
|
||||
val error = if (throwable is ApiResponseError.HttpException) {
|
||||
stakeKitErrorConverter.convert(throwable.errorBody.orEmpty())
|
||||
} else {
|
||||
StakingError.UnknownError()
|
||||
StakingError.DomainError(throwable.message)
|
||||
}
|
||||
|
||||
when (error) {
|
||||
is StakingError.StakeKitApiError -> {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.StakeKitApiError(error))
|
||||
}
|
||||
is StakingError.StakeKitUnknownError -> {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.StakeKitApiUnknownError(error))
|
||||
}
|
||||
else -> {
|
||||
// intentionally do nothing
|
||||
}
|
||||
}
|
||||
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.data.staking.converters.error
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.error.AccessDeniedErrorTypeDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorMessageDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorResponse
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
@ -11,105 +9,18 @@ internal class StakeKitErrorConverter(
|
|||
private val jsonAdapter: JsonAdapter<StakeKitErrorResponse>,
|
||||
) : Converter<String, StakingError> {
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
override fun convert(value: String): StakingError {
|
||||
return try {
|
||||
val stakeKitErrorResponse = jsonAdapter.fromJson(value) ?: return StakingError.UnknownError(value)
|
||||
val stakeKitErrorResponse = jsonAdapter.fromJson(value)
|
||||
?: return StakingError.StakeKitUnknownError(value)
|
||||
|
||||
if (stakeKitErrorResponse.type == AccessDeniedErrorTypeDTO.GEO_LOCATION) {
|
||||
StakingError.UnavailableDueToGeolocationError(
|
||||
tags = stakeKitErrorResponse.tags ?: emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
when (stakeKitErrorResponse.message) {
|
||||
StakeKitErrorMessageDTO.MINIMUM_AMOUNT_NOT_REACHED -> StakingError.MinimumAmountNotReachedError(
|
||||
amount = stakeKitErrorResponse.details?.amount ?: "",
|
||||
)
|
||||
StakeKitErrorMessageDTO.MISSING_ARGUMENTS_ERROR -> StakingError.MissingArgumentsError(
|
||||
arguments = stakeKitErrorResponse.details?.arguments ?: "",
|
||||
)
|
||||
StakeKitErrorMessageDTO.YIELD_UNDER_MAINTENANCE_ERROR -> StakingError.YieldUnderMaintenanceError(
|
||||
yieldId = stakeKitErrorResponse.details?.yieldId ?: "",
|
||||
)
|
||||
StakeKitErrorMessageDTO.INSUFFICIENT_FUNDS_ERROR ->
|
||||
StakingError.InsufficientFundsError
|
||||
StakeKitErrorMessageDTO.STAKED_POSITION_NOT_FOUND_ERROR ->
|
||||
StakingError.StakedPositionNotFoundError
|
||||
StakeKitErrorMessageDTO.INVALID_AMOUNT_SUBMITTED_ERROR ->
|
||||
StakingError.InvalidAmountSubmittedError
|
||||
StakeKitErrorMessageDTO.BALANCE_UNAVAILABLE_ERROR ->
|
||||
StakingError.BalanceUnavailableError
|
||||
StakeKitErrorMessageDTO.GAS_PRICE_UNAVAILABLE_ERROR ->
|
||||
StakingError.GasPriceUnavailableError
|
||||
StakeKitErrorMessageDTO.NOT_IMPLEMENTED_ERROR ->
|
||||
StakingError.NotImplementedError
|
||||
StakeKitErrorMessageDTO.TOKEN_NOT_FOUND_ERROR ->
|
||||
StakingError.TokenNotFoundError
|
||||
StakeKitErrorMessageDTO.BROADCAST_TRANSACTION_ERROR ->
|
||||
StakingError.BroadcastTransactionError
|
||||
StakeKitErrorMessageDTO.MISSING_GAS_PRICE_STRATEGY_ERROR ->
|
||||
StakingError.MissingGasPriceStrategyError
|
||||
StakeKitErrorMessageDTO.SUBSTRATE_MALFORMED_TRANSACTION_HASH_ERROR ->
|
||||
StakingError.SubstrateMalformedTransactionHashError
|
||||
StakeKitErrorMessageDTO.TRON_MAXIMUM_AMOUNT_OF_VALIDATORS_EXCEEDED_ERROR ->
|
||||
StakingError.TronMaximumAmountOfValidatorsExceededError
|
||||
StakeKitErrorMessageDTO.SUBSTRATE_POOL_NOT_FOUND_ERROR ->
|
||||
StakingError.SubstratePoolNotFoundError
|
||||
StakeKitErrorMessageDTO.SUBSTRATE_BONDED_AMOUNT_TOO_LOW_ERROR ->
|
||||
StakingError.SubstrateBondedAmountTooLowError
|
||||
StakeKitErrorMessageDTO.TRON_MISSING_RESOURCE_TYPE_ARGUMENT_ERROR ->
|
||||
StakingError.TronMissingResourceTypeArgumentError
|
||||
StakeKitErrorMessageDTO.AAVE_V3_POOL_FROZEN_ERROR ->
|
||||
StakingError.AaveV3PoolFrozenError
|
||||
StakeKitErrorMessageDTO.AAVE_V3_TOKEN_PAIR_NOT_FOUND_ERROR ->
|
||||
StakingError.AaveV3TokenPairNotFoundError
|
||||
StakeKitErrorMessageDTO.YEARN_VAULT_AT_MAX_CAPACITY_ERROR ->
|
||||
StakingError.YearnVaultAtMaxCapacityError
|
||||
StakeKitErrorMessageDTO.STETH_NO_WITHDRAWAL_REQUESTS_FOUND_ERROR ->
|
||||
StakingError.StETHNoWithdrawalRequestsFoundError
|
||||
StakeKitErrorMessageDTO.MORPHO_LENDING_POOL_PAUSED_ERROR ->
|
||||
StakingError.MorphoLendingPoolPausedError
|
||||
StakeKitErrorMessageDTO.NONCE_UNAVAILABLE_ERROR ->
|
||||
StakingError.NonceUnavailableError
|
||||
StakeKitErrorMessageDTO.COSMOS_ACCOUNT_NOT_FOUND_ERROR ->
|
||||
StakingError.CosmosAcccountNotFoundError
|
||||
StakeKitErrorMessageDTO.AVALANCHE_MISSING_ADDITIONAL_ADDRESSES_ARGUMENT_ERROR ->
|
||||
StakingError.AvalancheMissingAdditionalAddressesArgumentError
|
||||
StakeKitErrorMessageDTO.AVALANCHE_VALIDATOR_INFO_NOT_FOUND_ERROR ->
|
||||
StakingError.AvalancheValidatorInfoNotFoundError
|
||||
StakeKitErrorMessageDTO.SOLANA_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE_ERROR ->
|
||||
StakingError.SolanaTransactionSignatureVerificationFailureError
|
||||
StakeKitErrorMessageDTO.SOLANA_UNABLE_TO_CREATE_STAKE_ACCOUNT_ERROR ->
|
||||
StakingError.SolanaUnableTocreateStakeAccountError
|
||||
StakeKitErrorMessageDTO.SOLANA_STAKE_AMOUNT_TOO_LOW_ERROR ->
|
||||
StakingError.SolanaStakeAmountTooLowError
|
||||
StakeKitErrorMessageDTO.SOLANA_UNSTAKE_AMOUNT_TOO_LOW_ERROR ->
|
||||
StakingError.SolanaUnstakeAmountTooLowError
|
||||
StakeKitErrorMessageDTO.SOLANA_STAKE_ACCOUNTS_NOT_FOUND_ERROR ->
|
||||
StakingError.SolanaStakeAccountsNotFoundError
|
||||
StakeKitErrorMessageDTO.SOLANA_ELIGIBLE_STAKE_ACCOUNTS_NOT_FOUND_ERROR ->
|
||||
StakingError.SolanaEligibleStakeAccountsNotFoundError
|
||||
StakeKitErrorMessageDTO.TEZOS_NO_BALANCE_DELEGATED_ERROR ->
|
||||
StakingError.TezosNoBalanceDelegatedError
|
||||
StakeKitErrorMessageDTO.TEZOS_MISSING_PUBKEY_ARGUMENT_ERROR ->
|
||||
StakingError.TezosMissingPubkeyArgumentError
|
||||
StakeKitErrorMessageDTO.TEZOS_ESTIMATE_REVEAL_GAS_LIMIT_ERROR ->
|
||||
StakingError.TezosEstimateRevealGasLimitError
|
||||
StakeKitErrorMessageDTO.TEZOS_BALANCE_ALREADY_DELEGATED_ERROR ->
|
||||
StakingError.TezosBalanceAlreadyDelegatedError
|
||||
StakeKitErrorMessageDTO.BINANCE_ACCOUNT_NOT_FOUND_ERROR ->
|
||||
StakingError.BinanceAccountNotFoundError
|
||||
StakeKitErrorMessageDTO.BINANCE_MISSING_ACCOUNT_NUMBER_OR_SEQUENCE_ERROR ->
|
||||
StakingError.BinanceMissingAccountNumberOrSequenceError
|
||||
StakeKitErrorMessageDTO.GRT_STAKING_DISABLED_ERROR ->
|
||||
StakingError.GRTStakingDisabledError
|
||||
StakeKitErrorMessageDTO.GRT_STAKING_DISABLED_LEDGER_LIVE_ERROR ->
|
||||
StakingError.GRTStakingDisabledLedgerLiveError
|
||||
else -> StakingError.UnknownError(value)
|
||||
}
|
||||
return StakingError.StakeKitApiError(
|
||||
message = stakeKitErrorResponse.message,
|
||||
code = stakeKitErrorResponse.code,
|
||||
methodName = stakeKitErrorResponse.path,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
StakingError.UnknownError(value)
|
||||
StakingError.StakeKitUnknownError(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.staking.di
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.DefaultStakingErrorResolver
|
||||
import com.tangem.data.staking.DefaultStakingPendingTransactionRepository
|
||||
|
|
@ -79,10 +80,14 @@ internal object StakingDataModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideStakingErrorResolver(@NetworkMoshi moshi: Moshi): StakingErrorResolver {
|
||||
internal fun provideStakingErrorResolver(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
analyticsEventHandler: AnalyticsEventHandler,
|
||||
): StakingErrorResolver {
|
||||
val jsonAdapter = moshi.adapter(StakeKitErrorResponse::class.java)
|
||||
return DefaultStakingErrorResolver(
|
||||
stakeKitErrorConverter = StakeKitErrorConverter(jsonAdapter),
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ android {
|
|||
dependencies {
|
||||
api(projects.domain.staking.models)
|
||||
api(projects.domain.core)
|
||||
api(projects.core.analytics)
|
||||
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.jodatime)
|
||||
|
|
|
|||
|
|
@ -4,93 +4,17 @@ sealed class StakingError {
|
|||
|
||||
// region stakekit errors
|
||||
|
||||
data class MinimumAmountNotReachedError(val amount: String) : StakingError()
|
||||
data class StakeKitApiError(
|
||||
val message: String?,
|
||||
val code: Int?,
|
||||
val methodName: String?,
|
||||
) : StakingError()
|
||||
|
||||
data class MissingArgumentsError(val arguments: String) : StakingError()
|
||||
|
||||
data class YieldUnderMaintenanceError(val yieldId: String) : StakingError()
|
||||
|
||||
data object InsufficientFundsError : StakingError()
|
||||
|
||||
data object StakedPositionNotFoundError : StakingError()
|
||||
|
||||
data object InvalidAmountSubmittedError : StakingError()
|
||||
|
||||
data object BalanceUnavailableError : StakingError()
|
||||
|
||||
data object GasPriceUnavailableError : StakingError()
|
||||
|
||||
data object NotImplementedError : StakingError()
|
||||
|
||||
data object TokenNotFoundError : StakingError()
|
||||
|
||||
data object BroadcastTransactionError : StakingError()
|
||||
|
||||
data object MissingGasPriceStrategyError : StakingError()
|
||||
|
||||
data object SubstrateMalformedTransactionHashError : StakingError()
|
||||
|
||||
data object TronMaximumAmountOfValidatorsExceededError : StakingError()
|
||||
|
||||
data object SubstratePoolNotFoundError : StakingError()
|
||||
|
||||
data object SubstrateBondedAmountTooLowError : StakingError()
|
||||
|
||||
data object TronMissingResourceTypeArgumentError : StakingError()
|
||||
|
||||
data object AaveV3PoolFrozenError : StakingError()
|
||||
|
||||
data object AaveV3TokenPairNotFoundError : StakingError()
|
||||
|
||||
data object YearnVaultAtMaxCapacityError : StakingError()
|
||||
|
||||
data object StETHNoWithdrawalRequestsFoundError : StakingError()
|
||||
|
||||
data object MorphoLendingPoolPausedError : StakingError()
|
||||
|
||||
data object NonceUnavailableError : StakingError()
|
||||
|
||||
data object CosmosAcccountNotFoundError : StakingError()
|
||||
|
||||
data object AvalancheMissingAdditionalAddressesArgumentError : StakingError()
|
||||
|
||||
data object AvalancheValidatorInfoNotFoundError : StakingError()
|
||||
|
||||
data object SolanaTransactionSignatureVerificationFailureError : StakingError()
|
||||
|
||||
data object SolanaUnableTocreateStakeAccountError : StakingError()
|
||||
|
||||
data object SolanaStakeAmountTooLowError : StakingError()
|
||||
|
||||
data object SolanaUnstakeAmountTooLowError : StakingError()
|
||||
|
||||
data object SolanaStakeAccountsNotFoundError : StakingError()
|
||||
|
||||
data object SolanaEligibleStakeAccountsNotFoundError : StakingError()
|
||||
|
||||
data object TezosNoBalanceDelegatedError : StakingError()
|
||||
|
||||
data object TezosMissingPubkeyArgumentError : StakingError()
|
||||
|
||||
data object TezosEstimateRevealGasLimitError : StakingError()
|
||||
|
||||
data object TezosBalanceAlreadyDelegatedError : StakingError()
|
||||
|
||||
data object BinanceAccountNotFoundError : StakingError()
|
||||
|
||||
data object BinanceMissingAccountNumberOrSequenceError : StakingError()
|
||||
|
||||
data object GRTStakingDisabledError : StakingError()
|
||||
|
||||
data object GRTStakingDisabledLedgerLiveError : StakingError()
|
||||
|
||||
data class UnavailableDueToGeolocationError(
|
||||
val tags: List<String>,
|
||||
data class StakeKitUnknownError(
|
||||
val jsonString: String? = null,
|
||||
) : StakingError()
|
||||
|
||||
// endregion
|
||||
|
||||
data class DataError(val cause: Throwable) : StakingError()
|
||||
|
||||
data class UnknownError(val message: String? = null) : StakingError()
|
||||
data class DomainError(val message: String?) : StakingError()
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package com.tangem.domain.staking.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent.ButtonRewards.addIfValueIsNotNull
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
|
||||
sealed class StakingAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(
|
||||
category = "Staking",
|
||||
event = event,
|
||||
params = params,
|
||||
) {
|
||||
|
||||
data class StakingInfoScreenOpened(
|
||||
val validatorsCount: Int,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Staking Info Screen Opened",
|
||||
params = mapOf(
|
||||
"Validators Count" to validatorsCount.toString(),
|
||||
),
|
||||
)
|
||||
|
||||
data object WhatIsStaking : StakingAnalyticsEvent(
|
||||
event = "Link - What Is Staking",
|
||||
)
|
||||
|
||||
data object AmountScreenOpened : StakingAnalyticsEvent(
|
||||
event = "Amount Screen Opened",
|
||||
)
|
||||
|
||||
data class ConfirmationScreenOpened(
|
||||
val validator: String,
|
||||
val action: StakingActionType,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Confirmation Screen Opened",
|
||||
params = mapOf(
|
||||
"Validator" to validator,
|
||||
"Action" to action.asAnalyticName,
|
||||
),
|
||||
)
|
||||
|
||||
data class StakeInProgressScreenOpened(
|
||||
val validator: String,
|
||||
val action: StakingActionType,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Stake In Progress Screen Opened",
|
||||
params = mapOf(
|
||||
"Validator" to validator,
|
||||
"Action" to action.asAnalyticName,
|
||||
),
|
||||
)
|
||||
|
||||
data object RewardScreenOpened : StakingAnalyticsEvent(
|
||||
event = "Reward Screen Opened",
|
||||
)
|
||||
|
||||
data class AmountSelectCurrency(
|
||||
val isAppCurrency: Boolean,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Selected Currency",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TYPE to if (isAppCurrency) "App Currency" else "Token",
|
||||
),
|
||||
)
|
||||
|
||||
data object ButtonMax : StakingAnalyticsEvent(
|
||||
event = "Button - Max",
|
||||
)
|
||||
|
||||
data class ButtonCancel(
|
||||
val source: StakeScreenSource,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Button - Cancel",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class ValidatorChosen(
|
||||
val validator: String,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Validator Chosen",
|
||||
params = mapOf(
|
||||
"Validator" to validator,
|
||||
),
|
||||
)
|
||||
|
||||
data class ButtonValidator(
|
||||
val source: StakeScreenSource,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Button - Validator",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.name,
|
||||
),
|
||||
)
|
||||
|
||||
data object ButtonRewards : StakingAnalyticsEvent(
|
||||
event = "Button - Rewards",
|
||||
)
|
||||
|
||||
data class ButtonAction(
|
||||
val action: StakingActionType,
|
||||
val validator: String,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Button - ${action.asAnalyticName}",
|
||||
params = mapOf(
|
||||
"Validator" to validator,
|
||||
),
|
||||
)
|
||||
|
||||
data object ButtonShare : StakingAnalyticsEvent(event = "Button - Share")
|
||||
|
||||
data object ButtonExplore : StakingAnalyticsEvent(event = "Button - Explore")
|
||||
|
||||
data class StakeKitApiError(
|
||||
val stakingError: StakingError.StakeKitApiError,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Errors",
|
||||
params = buildMap {
|
||||
addIfValueIsNotNull(AnalyticsParam.ERROR_MESSAGE, stakingError.message)
|
||||
addIfValueIsNotNull(AnalyticsParam.ERROR_CODE, stakingError.code)
|
||||
addIfValueIsNotNull(AnalyticsParam.METHOD_NAME, stakingError.methodName)
|
||||
},
|
||||
)
|
||||
|
||||
data class StakeKitApiUnknownError(
|
||||
val stakeKitUnknownError: StakingError.StakeKitUnknownError,
|
||||
) : StakingAnalyticsEvent(
|
||||
event = "Errors",
|
||||
params = buildMap {
|
||||
addIfValueIsNotNull(AnalyticsParam.ERROR_DESCRIPTION, stakeKitUnknownError.jsonString)
|
||||
},
|
||||
)
|
||||
|
||||
fun MutableMap<String, String>.addIfValueIsNotNull(key: String, value: Any?) {
|
||||
if (value != null) {
|
||||
put(key, value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
data object TransactionError : StakingAnalyticsEvent(
|
||||
event = "Error - Transaction Rejected",
|
||||
)
|
||||
}
|
||||
|
||||
enum class StakeScreenSource {
|
||||
Info, Amount, Confirmation, Validators,
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
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.StakingError
|
||||
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.asAnalyticName,
|
||||
),
|
||||
)
|
||||
|
||||
data class StakeInProgressScreenOpened(
|
||||
val validator: String,
|
||||
val token: String,
|
||||
val action: StakingActionType,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Stake In Progress Screen Opened",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
"Validator" to validator,
|
||||
"Action" to action.asAnalyticName,
|
||||
),
|
||||
)
|
||||
|
||||
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 ValidatorChosen(
|
||||
val token: String,
|
||||
val validator: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Validator Chosen",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
"Validator" to validator,
|
||||
),
|
||||
)
|
||||
|
||||
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: StakingActionType,
|
||||
val token: String,
|
||||
val validator: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Button - ${action.asAnalyticName}",
|
||||
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 StakekitError(
|
||||
val token: String,
|
||||
val stakeKitError: StakingError,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Errors",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
if (stakeKitError is StakingError.UnknownError) {
|
||||
AnalyticsParam.ERROR_DESCRIPTION to (stakeKitError.message ?: "Unknown")
|
||||
} else {
|
||||
AnalyticsParam.ERROR_TYPE to stakeKitError.javaClass.simpleName
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.features.staking.impl.analytics
|
||||
|
||||
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
|
||||
|
||||
class StakingParamsInterceptor(private val tokenSymbol: String) : ParamsInterceptor {
|
||||
|
||||
override fun id() = ID
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean {
|
||||
return event is StakingAnalyticsEvent
|
||||
}
|
||||
|
||||
override fun intercept(params: MutableMap<String, String>) {
|
||||
params[AnalyticsParam.TOKEN_PARAM] = tokenSymbol
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ID = "StakingParamsInterceptorId"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@ 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.domain.staking.analytics.StakeScreenSource
|
||||
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
|
||||
internal class StakingAnalyticSender(
|
||||
|
|
@ -24,9 +24,8 @@ internal class StakingAnalyticSender(
|
|||
?.size ?: 0
|
||||
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakingInfoScreenOpened(
|
||||
StakingAnalyticsEvent.StakingInfoScreenOpened(
|
||||
validatorsCount = validatorCount,
|
||||
token = value.cryptoCurrencySymbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -39,8 +38,7 @@ internal class StakingAnalyticSender(
|
|||
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ConfirmationScreenOpened(
|
||||
token = value.cryptoCurrencySymbol,
|
||||
StakingAnalyticsEvent.ConfirmationScreenOpened(
|
||||
validator = validatorName,
|
||||
action = getStakingActionType(value),
|
||||
),
|
||||
|
|
@ -49,7 +47,7 @@ internal class StakingAnalyticSender(
|
|||
|
||||
fun screenCancel(value: StakingUiState) {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonCancel(
|
||||
StakingAnalyticsEvent.ButtonCancel(
|
||||
source = when (value.currentStep) {
|
||||
StakingStep.InitialInfo -> StakeScreenSource.Info
|
||||
StakingStep.Amount -> StakeScreenSource.Amount
|
||||
|
|
@ -58,7 +56,6 @@ internal class StakingAnalyticSender(
|
|||
StakingStep.RewardsValidators,
|
||||
-> StakeScreenSource.Validators
|
||||
},
|
||||
token = value.cryptoCurrencyName,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -93,9 +90,8 @@ internal class StakingAnalyticSender(
|
|||
),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakeInProgressScreenOpened(
|
||||
StakingAnalyticsEvent.StakeInProgressScreenOpened(
|
||||
validator = validatorName,
|
||||
token = value.cryptoCurrencySymbol,
|
||||
action = getStakingActionType(value),
|
||||
),
|
||||
)
|
||||
|
|
@ -107,9 +103,8 @@ internal class StakingAnalyticSender(
|
|||
val validatorName = validatorState?.chosenValidator?.name ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonAction(
|
||||
StakingAnalyticsEvent.ButtonAction(
|
||||
action = getStakingActionType(value),
|
||||
token = value.cryptoCurrencySymbol,
|
||||
validator = validatorName,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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.domain.staking.analytics.StakingAnalyticsEvent
|
||||
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
|
||||
|
||||
internal class StakingStateRouter(
|
||||
|
|
@ -60,16 +60,12 @@ internal class StakingStateRouter(
|
|||
}
|
||||
|
||||
private fun showRewardsValidators() {
|
||||
analyticsEventsHandler.send(
|
||||
StakingAnalyticsEvents.RewardScreenOpened(stateController.value.cryptoCurrencySymbol),
|
||||
)
|
||||
analyticsEventsHandler.send(StakingAnalyticsEvent.RewardScreenOpened)
|
||||
stateController.update { it.copy(currentStep = StakingStep.RewardsValidators) }
|
||||
}
|
||||
|
||||
private fun showAmount() {
|
||||
analyticsEventsHandler.send(
|
||||
StakingAnalyticsEvents.AmountScreenOpened(stateController.value.cryptoCurrencySymbol),
|
||||
)
|
||||
analyticsEventsHandler.send(StakingAnalyticsEvent.AmountScreenOpened)
|
||||
stateController.update { it.copy(currentStep = StakingStep.Amount) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
|
|||
}
|
||||
|
||||
if (result.isNullOrEmpty()) {
|
||||
onStakingFeeError(StakingError.UnknownError())
|
||||
onStakingFeeError(StakingError.DomainError("Error estimating fee"))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import arrow.core.getOrElse
|
|||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.SubmitHashData
|
||||
|
|
@ -20,7 +19,6 @@ import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalculateSubtractedAmount
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.isSolanaWithdraw
|
||||
|
|
@ -46,7 +44,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
private val submitHashUseCase: SubmitHashUseCase,
|
||||
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
|
||||
private val savePendingTransactionUseCase: SavePendingTransactionUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
@Assisted private val yield: Yield,
|
||||
|
|
@ -84,7 +81,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
|
||||
if (fullTransactionsData.isNullOrEmpty()) {
|
||||
onConstructError(StakingError.UnknownError())
|
||||
onConstructError(StakingError.DomainError("fullTransactionsData is null or empty"))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -198,12 +195,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
type = action?.type,
|
||||
),
|
||||
).getOrElse {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakekitError(
|
||||
token = state.cryptoCurrencySymbol,
|
||||
stakeKitError = it,
|
||||
),
|
||||
)
|
||||
onConstructError(it)
|
||||
return emptyList()
|
||||
}
|
||||
|
|
@ -255,13 +246,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
pendingTransaction = pendingTransaction,
|
||||
),
|
||||
)
|
||||
.onLeft { error ->
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakekitError(
|
||||
token = stateController.value.cryptoCurrencySymbol,
|
||||
stakeKitError = error,
|
||||
),
|
||||
)
|
||||
.onLeft {
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
|
|
|
|||
|
|
@ -32,14 +32,8 @@ internal class AddStakingErrorTransformer(
|
|||
}
|
||||
|
||||
private fun convertToNotification(error: StakingError): NotificationUM {
|
||||
return when (error) {
|
||||
is StakingError.StakedPositionNotFoundError -> StakingNotification.Error.StakedPositionNotFoundError(
|
||||
message = error.toString(),
|
||||
)
|
||||
// TODO staking
|
||||
else -> StakingNotification.Error.Common(
|
||||
subtitle = stringReference(error.toString()),
|
||||
)
|
||||
}
|
||||
return StakingNotification.Error.Common(
|
||||
subtitle = stringReference(error.toString()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
|||
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.ParamsInterceptorHolder
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -47,8 +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.domain.staking.analytics.StakeScreenSource
|
||||
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
|
||||
import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor
|
||||
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.*
|
||||
|
|
@ -106,6 +108,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val stakingBalanceUpdater: StakingBalanceUpdater.Factory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val paramsInterceptorHolder: ParamsInterceptorHolder,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
|
||||
|
|
@ -201,6 +204,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
paramsInterceptorHolder.removeParamsInterceptor(StakingParamsInterceptor.ID)
|
||||
approvalJobHolder.cancel()
|
||||
feeJobHolder.cancel()
|
||||
sendTransactionJobHolder.cancel()
|
||||
|
|
@ -297,18 +301,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
)
|
||||
updateNotifications()
|
||||
},
|
||||
onStakingFeeError = { error ->
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakekitError(
|
||||
value.cryptoCurrencySymbol,
|
||||
error,
|
||||
),
|
||||
)
|
||||
onStakingFeeError = {
|
||||
stateController.update(AddStakingErrorTransformer())
|
||||
updateNotifications(GetFeeError.UnknownError)
|
||||
},
|
||||
onFeeError = { error ->
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
|
||||
stateController.update(AddStakingErrorTransformer())
|
||||
updateNotifications(error)
|
||||
},
|
||||
|
|
@ -336,13 +334,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
transactionsInProgress.addAll(constructedTransactions)
|
||||
},
|
||||
onConstructError = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakekitError(
|
||||
token = value.cryptoCurrencySymbol,
|
||||
stakeKitError = error,
|
||||
),
|
||||
)
|
||||
stakingEventFactory.createStakingErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
|
|
@ -353,7 +344,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
},
|
||||
onSendError = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
|
||||
stakingEventFactory.createSendTransactionErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
|
|
@ -399,7 +390,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onInitialInfoBannerClick() {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.WhatIsStaking(value.cryptoCurrencySymbol))
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.WhatIsStaking)
|
||||
innerRouter.openUrl(WHAT_IS_STAKING_ARTICLE_URL)
|
||||
}
|
||||
|
||||
|
|
@ -446,22 +437,21 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onMaxValueClick() {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonMax(value.cryptoCurrencySymbol))
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonMax)
|
||||
stateController.update(AmountMaxValueStateTransformer(cryptoCurrencyStatus, yield))
|
||||
}
|
||||
|
||||
override fun onCurrencyChangeClick(isFiat: Boolean) {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.AmountSelectCurrency(value.cryptoCurrencySymbol, isFiat),
|
||||
StakingAnalyticsEvent.AmountSelectCurrency(isFiat),
|
||||
)
|
||||
stateController.update(AmountCurrencyChangeStateTransformer(cryptoCurrencyStatus, isFiat))
|
||||
}
|
||||
|
||||
override fun openValidators() {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonValidator(
|
||||
StakingAnalyticsEvent.ButtonValidator(
|
||||
source = StakeScreenSource.Confirmation,
|
||||
token = value.cryptoCurrencySymbol,
|
||||
),
|
||||
)
|
||||
stakingStateRouter.showValidators()
|
||||
|
|
@ -469,18 +459,15 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
override fun onValidatorSelect(validator: Yield.Validator) {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ValidatorChosen(
|
||||
value.cryptoCurrencySymbol,
|
||||
validator.name,
|
||||
StakingAnalyticsEvent.ValidatorChosen(
|
||||
validator = validator.name,
|
||||
),
|
||||
)
|
||||
stateController.update(ValidatorSelectChangeTransformer(validator))
|
||||
}
|
||||
|
||||
override fun openRewardsValidators() {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonRewards(value.cryptoCurrencySymbol),
|
||||
)
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonRewards)
|
||||
val rewardsValidators =
|
||||
stateController.value.rewardsValidatorsState as? StakingStates.RewardsValidatorsState.Data
|
||||
val rewards = rewardsValidators?.rewards
|
||||
|
|
@ -488,9 +475,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
onActiveStake(rewards.first())
|
||||
} else {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonValidator(
|
||||
StakingAnalyticsEvent.ButtonValidator(
|
||||
source = StakeScreenSource.Info,
|
||||
token = value.cryptoCurrencySymbol,
|
||||
),
|
||||
)
|
||||
onNextClick(actionTypeToOverwrite = StakingActionCommonType.PENDING_REWARDS)
|
||||
|
|
@ -542,9 +528,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
override fun onActiveStakeAnalytic() {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonValidator(
|
||||
StakingAnalyticsEvent.ButtonValidator(
|
||||
source = StakeScreenSource.Info,
|
||||
token = value.cryptoCurrencySymbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -595,7 +580,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentApprovalTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -617,7 +602,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentApprovalTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -747,7 +732,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onExploreClick() {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonExplore)
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonExplore)
|
||||
val confirmationDataState = uiState.value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
|
||||
val txUrl = transactionDoneState?.txUrl
|
||||
|
|
@ -761,7 +746,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
|
||||
val txUrl = transactionDoneState?.txUrl
|
||||
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonShare)
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonShare)
|
||||
if (txUrl != null) {
|
||||
vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click)
|
||||
stakingEventFactory.createShareDialog(txUrl = txUrl)
|
||||
|
|
@ -838,10 +823,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
if (!isInitialInfoAnalyticSent) {
|
||||
isInitialInfoAnalyticSent = true
|
||||
val balances = status.value.yieldBalance as? YieldBalance.Data
|
||||
paramsInterceptorHolder.addParamsInterceptor(
|
||||
interceptor = StakingParamsInterceptor(status.currency.symbol),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakingInfoScreenOpened(
|
||||
StakingAnalyticsEvent.StakingInfoScreenOpened(
|
||||
validatorsCount = balances?.getValidatorsCount() ?: 0,
|
||||
token = status.currency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue