diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
index c72518ff14..3b3adab9bf 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
@@ -357,7 +357,10 @@ internal object TokensDomainModule {
@Provides
@Singleton
- fun provideGetCurrencyCheckUseCase(currencyChecksRepository: CurrencyChecksRepository): GetCurrencyCheckUseCase {
- return GetCurrencyCheckUseCase(currencyChecksRepository)
+ fun provideGetCurrencyCheckUseCase(
+ currencyChecksRepository: CurrencyChecksRepository,
+ dispatchers: CoroutineDispatcherProvider,
+ ): GetCurrencyCheckUseCase {
+ return GetCurrencyCheckUseCase(currencyChecksRepository, dispatchers)
}
}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt
index 51df42310b..5ba4b5f528 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt
@@ -67,7 +67,7 @@ object NotificationsFactory {
if (!isAccountFunded && reserveAmount != null && reserveAmount > sendingAmount) {
add(
NotificationUM.Error.ReserveAmount(
- sendingAmount.format {
+ reserveAmount.format {
crypto(cryptoCurrency)
},
),
diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
index 9600cda393..b646142403 100644
--- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
+++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
@@ -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"
diff --git a/core/analytics/src/main/java/com/tangem/core/analytics/di/AnalyticsModule.kt b/core/analytics/src/main/java/com/tangem/core/analytics/di/AnalyticsModule.kt
index 13715236c5..18a95d3a02 100644
--- a/core/analytics/src/main/java/com/tangem/core/analytics/di/AnalyticsModule.kt
+++ b/core/analytics/src/main/java/com/tangem/core/analytics/di/AnalyticsModule.kt
@@ -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)
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/adapter/UnknownEnumMoshiAdapter.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/adapter/UnknownEnumMoshiAdapter.kt
index 2cc525ffd0..97372a83b2 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/api/common/adapter/UnknownEnumMoshiAdapter.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/adapter/UnknownEnumMoshiAdapter.kt
@@ -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 {
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt
index fdb3436051..086837d4b1 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt
@@ -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,
-}
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 9fa2f27509..745138a008 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -339,7 +339,7 @@
\nCoinMarketCap
Blockchain, auf der die Kryptowährung ursprünglich erstellt wurde
Natives Netzwerk
- Die Verwendung von nicht nativen Netzwerken für Token ermöglicht eine blockchainübergreifende Interoperabilität, sodass Vermögenswerte in verschiedenen dezentralen Anwendungen und Smart Contracts plattformübergreifend genutzt werden können. Dies erfordert jedoch häufig eine Verwahrstelle oder einen intelligenten Vertrag, um den ursprünglichen Vermögenswert sicher zu verwahren, was zu einer Zentralisierung und einem Kontrahentenrisiko führt.
+ Die Verwendung nicht-nativer Netzwerke für Token ermöglicht Interoperabilität zwischen Blockchains, sodass Vermögenswerte plattformübergreifend in verschiedenen dezentralen Anwendungen und Smart Contracts genutzt werden können. Dies erfordert jedoch häufig einen Verwahrer oder Smart Contract, um den ursprünglichen Vermögenswert sicher aufzubewahren, was zu Zentralisierung und Kontrahentenrisiken führt.
Nicht die ursprüngliche oder primäre Blockchain, in der der Token gehostet wird
Nicht-native Netzwerke
Netzwerke auswählen
@@ -710,7 +710,7 @@
Marktbewertung
Metriken
Mindestanforderungen
- Keine Belohnungen
+ Keine Belohnungen verfügbar
Belohnungen beanspruchen
Eine Möglichkeit, Staking-Belohnungen zu erhalten. Es kann automatisch oder manuell beansprucht werden.
Belohnungszeitplan
@@ -760,7 +760,7 @@
Täglich
Alle %s
Täglich
- Alle %s
+ Alle %s Tage
Jeder
Pro Epoche
Pro Ära
diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml
index d9837db78c..73f301ed3c 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -520,7 +520,7 @@
Ваша seed-фраза
-
+ - слово
- %d слова
- %d слів
- %d слів
@@ -741,7 +741,7 @@
Період, який ви повинні чекати після запиту на виведення коштів зі стейкінгу, перш ніж токени стануть доступними.
Період блокування
Відведений час для активації участі в стейкінгу.
- Комісія мережі за схвалення токену, щоб верифікувати, що саме ви даєте дозвіл на використання вашого токена для стейкінгу.
+ Комісія за схвалення токену, щоб верифікувати, що саме ви дозволяєте використання токена для стейкінгу.
Використовуючи функцію стейкінгу, ви погоджуєтесь з %1$s та %2$s
Заблоковано
Перемістити
diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingErrorResolver.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingErrorResolver.kt
index 8568980711..34ebad7aa5 100644
--- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingErrorResolver.kt
+++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingErrorResolver.kt
@@ -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
}
}
\ No newline at end of file
diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt
index 7ed9eb23e3..52993093c5 100644
--- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt
+++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt
@@ -127,7 +127,7 @@ class YieldConverter(
stakedBalance = validatorDTO.stakedBalance,
votingPower = validatorDTO.votingPower,
preferred = validatorDTO.preferred,
- isStrategicPartner = isStrategicPartner(address),
+ isStrategicPartner = isStrategicPartner(validatorDTO.address, validatorDTO.name),
)
}
@@ -180,11 +180,17 @@ class YieldConverter(
}
}
- private fun isStrategicPartner(validatorAddress: String): Boolean = PARTNERS.any { it == validatorAddress }
+ private fun isStrategicPartner(validatorAddress: String?, validatorName: String): Boolean {
+ return PARTNERS.any { it == validatorAddress } || PARTNERS_NAMES.any { it.equals(validatorName, true) }
+ }
private companion object {
val PARTNERS = listOf(
"cosmosvaloper1wrx0x9m9ykdhw9sg04v7uljme53wuj03aa5d4f",
+ "H2tJNyMHnRF6ahCQLQ1sSycM4FGchymuzyYzUqKEuydk",
+ )
+ val PARTNERS_NAMES = listOf(
+ "Meria",
)
}
}
\ No newline at end of file
diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/error/StakeKitErrorConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/error/StakeKitErrorConverter.kt
index 8b303f9292..fdc9226b7d 100644
--- a/data/staking/src/main/java/com/tangem/data/staking/converters/error/StakeKitErrorConverter.kt
+++ b/data/staking/src/main/java/com/tangem/data/staking/converters/error/StakeKitErrorConverter.kt
@@ -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,
) : Converter {
- @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)
}
}
}
\ No newline at end of file
diff --git a/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt b/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt
index 2226847178..f3a7879d5a 100644
--- a/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt
+++ b/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt
@@ -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.*
import com.tangem.data.staking.DefaultStakingErrorResolver
@@ -83,10 +84,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,
)
}
}
\ No newline at end of file
diff --git a/domain/staking/build.gradle.kts b/domain/staking/build.gradle.kts
index 5107e30cf7..9832ec7010 100644
--- a/domain/staking/build.gradle.kts
+++ b/domain/staking/build.gradle.kts
@@ -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)
diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/StakingError.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/StakingError.kt
index b02b33741f..0d0ca16611 100644
--- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/StakingError.kt
+++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/StakingError.kt
@@ -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,
+ 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()
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/StakingAnalyticsEvents.kt b/domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt
similarity index 50%
rename from features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/StakingAnalyticsEvents.kt
rename to domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt
index cc612804f3..dbe640d0b0 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/StakingAnalyticsEvents.kt
+++ b/domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt
@@ -1,11 +1,12 @@
-package com.tangem.features.staking.impl.analytics
+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
-internal sealed class StakingAnalyticsEvents(
+sealed class StakingAnalyticsEvent(
event: String,
params: Map = mapOf(),
) : AnalyticsEvent(
@@ -16,37 +17,27 @@ internal sealed class StakingAnalyticsEvents(
data class StakingInfoScreenOpened(
val validatorsCount: Int,
- val token: String,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
event = "Staking Info Screen Opened",
params = mapOf(
"Validators Count" to validatorsCount.toString(),
- AnalyticsParam.TOKEN_PARAM to token,
),
)
- data class WhatIsStaking(val token: String) : StakingAnalyticsEvents(
+ data object WhatIsStaking : StakingAnalyticsEvent(
event = "Link - What Is Staking",
- params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
- ),
)
- data class AmountScreenOpened(val token: String) : StakingAnalyticsEvents(
+ data object AmountScreenOpened : StakingAnalyticsEvent(
event = "Amount Screen Opened",
- params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
- ),
)
data class ConfirmationScreenOpened(
- val token: String,
val validator: String,
val action: StakingActionType,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
event = "Confirmation Screen Opened",
params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
"Validator" to validator,
"Action" to action.asAnalyticName,
),
@@ -54,126 +45,108 @@ internal sealed class StakingAnalyticsEvents(
data class StakeInProgressScreenOpened(
val validator: String,
- val token: String,
val action: StakingActionType,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
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(
+ data object RewardScreenOpened : StakingAnalyticsEvent(
event = "Reward Screen Opened",
- params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
- ),
)
data class AmountSelectCurrency(
- val token: String,
val isAppCurrency: Boolean,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
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(
+ data object ButtonMax : StakingAnalyticsEvent(
event = "Button - Max",
- params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
- ),
)
data class ButtonCancel(
val source: StakeScreenSource,
- val token: String,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
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(
+ ) : StakingAnalyticsEvent(
event = "Validator Chosen",
params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
"Validator" to validator,
),
)
data class ButtonValidator(
val source: StakeScreenSource,
- val token: String,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
event = "Button - Validator",
params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
AnalyticsParam.SOURCE to source.name,
),
)
- data class ButtonRewards(
- val token: String,
- ) : StakingAnalyticsEvents(
+ data object ButtonRewards : StakingAnalyticsEvent(
event = "Button - Rewards",
- params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
- ),
)
data class ButtonAction(
val action: StakingActionType,
- val token: String,
val validator: String,
- ) : StakingAnalyticsEvents(
+ ) : StakingAnalyticsEvent(
event = "Button - ${action.asAnalyticName}",
params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
"Validator" to validator,
),
)
- data object ButtonShare : StakingAnalyticsEvents(event = "Button - Share")
+ data object ButtonShare : StakingAnalyticsEvent(event = "Button - Share")
- data object ButtonExplore : StakingAnalyticsEvents(event = "Button - Explore")
+ data object ButtonExplore : StakingAnalyticsEvent(event = "Button - Explore")
- data class StakekitError(
- val token: String,
- val stakeKitError: StakingError,
- ) : StakingAnalyticsEvents(
+ data class StakeKitApiError(
+ val stakingError: StakingError.StakeKitApiError,
+ ) : StakingAnalyticsEvent(
event = "Errors",
- params = mapOf(
- AnalyticsParam.TOKEN_PARAM to token,
- AnalyticsParam.ERROR_TYPE to if (stakeKitError is StakingError.UnknownError) {
- stakeKitError.message ?: "Unknown"
- } else {
- stakeKitError.javaClass.simpleName
- },
- ),
+ params = buildMap {
+ addIfValueIsNotNull(AnalyticsParam.ERROR_MESSAGE, stakingError.message)
+ addIfValueIsNotNull(AnalyticsParam.ERROR_CODE, stakingError.code)
+ addIfValueIsNotNull(AnalyticsParam.METHOD_NAME, stakingError.methodName)
+ },
)
- data class TransactionError(
- val token: String,
- ) : StakingAnalyticsEvents(
+ data class StakeKitApiUnknownError(
+ val stakeKitUnknownError: StakingError.StakeKitUnknownError,
+ ) : StakingAnalyticsEvent(
+ event = "Errors",
+ params = buildMap {
+ addIfValueIsNotNull(AnalyticsParam.ERROR_DESCRIPTION, stakeKitUnknownError.jsonString)
+ },
+ )
+
+ fun MutableMap.addIfValueIsNotNull(key: String, value: Any?) {
+ if (value != null) {
+ put(key, value.toString())
+ }
+ }
+
+ data object TransactionError : StakingAnalyticsEvent(
event = "Error - Transaction Rejected",
- params = mapOf(AnalyticsParam.TOKEN_PARAM to token),
)
}
enum class StakeScreenSource {
- Info,
- Amount,
- Confirmation,
- Validators,
+ Info, Amount, Confirmation, Validators,
}
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt
index 04018ed4aa..780c6c48fe 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt
@@ -8,4 +8,5 @@ data class CryptoCurrencyCheck(
val reserveAmount: BigDecimal?,
val existentialDeposit: BigDecimal?,
val utxoAmountLimit: UtxoAmountLimit?,
+ val isAccountFunded: Boolean,
)
\ No newline at end of file
diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt
index 4e74a21591..aa488b2671 100644
--- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt
+++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt
@@ -4,10 +4,13 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.wallets.models.UserWalletId
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import kotlinx.coroutines.withContext
import java.math.BigDecimal
class GetCurrencyCheckUseCase(
private val currencyChecksRepository: CurrencyChecksRepository,
+ private val dispatchers: CoroutineDispatcherProvider,
) {
suspend operator fun invoke(
@@ -15,27 +18,38 @@ class GetCurrencyCheckUseCase(
currencyStatus: CryptoCurrencyStatus,
amount: BigDecimal?,
fee: BigDecimal?,
+ recipientAddress: String? = null,
): CryptoCurrencyCheck {
- val network = currencyStatus.currency.network
- val dustValue = currencyChecksRepository.getDustValue(userWalletId, network)
- val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network)
- val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network)
- val utxoAmountLimit = if (amount != null && fee != null) {
- currencyChecksRepository.checkUtxoAmountLimit(
- userWalletId = userWalletId,
- network = network,
- amount = amount,
- fee = fee,
- )
- } else {
- null
- }
+ return withContext(dispatchers.io) {
+ val network = currencyStatus.currency.network
+ val dustValue = currencyChecksRepository.getDustValue(userWalletId, network)
+ val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network)
+ val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network)
+ val isAccountFunded = recipientAddress?.let {
+ currencyChecksRepository.checkIfAccountFunded(
+ userWalletId,
+ network,
+ recipientAddress,
+ )
+ } ?: false
+ val utxoAmountLimit = if (amount != null && fee != null) {
+ currencyChecksRepository.checkUtxoAmountLimit(
+ userWalletId = userWalletId,
+ network = network,
+ amount = amount,
+ fee = fee,
+ )
+ } else {
+ null
+ }
- return CryptoCurrencyCheck(
- dustValue = dustValue,
- reserveAmount = reserveAmount,
- existentialDeposit = existentialDeposit,
- utxoAmountLimit = utxoAmountLimit,
- )
+ CryptoCurrencyCheck(
+ dustValue = dustValue,
+ reserveAmount = reserveAmount,
+ existentialDeposit = existentialDeposit,
+ utxoAmountLimit = utxoAmountLimit,
+ isAccountFunded = isAccountFunded,
+ )
+ }
}
}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt
index 23f483ef38..48ec5be5b1 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt
@@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.map
import java.math.BigDecimal
@Suppress("LongParameterList", "LargeClass")
-internal class SendNotificationFactory constructor(
+internal class SendNotificationFactory(
private val analyticsEventHandler: AnalyticsEventHandler,
private val validateTransactionUseCase: ValidateTransactionUseCase,
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
@@ -90,11 +90,14 @@ internal class SendNotificationFactory constructor(
reduceAmountBy = reduceAmountBy,
)
val feeError = (feeState.feeSelectorState as? FeeSelectorState.Error)?.error
+
+ val recipientAddress = state.recipientState?.addressTextField?.value
val currencyCheck = getCurrencyCheckUseCase(
userWalletId = userWalletId,
currencyStatus = cryptoCurrencyStatus,
amount = sendingAmount,
fee = feeValue,
+ recipientAddress = recipientAddress,
)
buildList {
addErrorNotifications(
@@ -187,7 +190,7 @@ internal class SendNotificationFactory constructor(
reserveAmount = currencyCheck.reserveAmount,
sendingAmount = sendingAmount,
cryptoCurrency = currency,
- isAccountFunded = false,
+ isAccountFunded = currencyCheck.isAccountFunded,
)
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/StakingParamsInterceptor.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/StakingParamsInterceptor.kt
new file mode 100644
index 0000000000..a11ff1ac92
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/StakingParamsInterceptor.kt
@@ -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) {
+ params[AnalyticsParam.TOKEN_PARAM] = tokenSymbol
+ }
+
+ companion object {
+ const val ID = "StakingParamsInterceptorId"
+ }
+}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt
index 7fcb116702..114d86d579 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt
@@ -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
@@ -59,7 +57,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),
),
)
@@ -106,9 +102,8 @@ internal class StakingAnalyticSender(
val validatorName = validatorState?.chosenValidator?.name ?: return
analyticsEventHandler.send(
- StakingAnalyticsEvents.ButtonAction(
+ StakingAnalyticsEvent.ButtonAction(
action = getStakingActionType(value),
- token = value.cryptoCurrencySymbol,
validator = validatorName,
),
)
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt
index e0e42070ac..bcddf3b0df 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt
@@ -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(
@@ -68,16 +68,12 @@ internal class StakingStateRouter(
}
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) }
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt
index 0b19f03422..a56aa88c94 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt
@@ -135,7 +135,7 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
}
if (result.isNullOrEmpty()) {
- onStakingFeeError(StakingError.UnknownError())
+ onStakingFeeError(StakingError.DomainError("Error estimating fee"))
return
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt
index e61018217b..3a7bacaf46 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt
@@ -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.SubmitHashData
import com.tangem.domain.staking.model.stakekit.*
@@ -19,11 +18,7 @@ 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.FeeState
-import com.tangem.features.staking.impl.presentation.state.StakingStateController
-import com.tangem.features.staking.impl.presentation.state.StakingStates
-import com.tangem.features.staking.impl.presentation.state.StakingUiState
+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.isComposePendingActions
import com.tangem.utils.extensions.orZero
@@ -46,7 +41,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val submitHashUseCase: SubmitHashUseCase,
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
- private val analyticsEventHandler: AnalyticsEventHandler,
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
@Assisted private val userWallet: UserWallet,
@Assisted private val yield: Yield,
@@ -84,7 +78,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
)
if (fullTransactionsData.isNullOrEmpty()) {
- onConstructError(StakingError.UnknownError())
+ onConstructError(StakingError.DomainError("fullTransactionsData is null or empty"))
return
}
@@ -193,12 +187,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
type = action?.type,
),
).getOrElse {
- analyticsEventHandler.send(
- StakingAnalyticsEvents.StakekitError(
- token = state.cryptoCurrencySymbol,
- stakeKitError = it,
- ),
- )
onConstructError(it)
return emptyList()
}
@@ -243,13 +231,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
transactionHash = transactionHash,
),
)
- .onLeft { error ->
- analyticsEventHandler.send(
- StakingAnalyticsEvents.StakekitError(
- token = stateController.value.cryptoCurrencySymbol,
- stakeKitError = error,
- ),
- )
+ .onLeft {
saveUnsubmittedHashUseCase.invoke(
transactionId = transaction.id,
transactionHash = transactionHash,
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt
index 5acae90ea3..85f0bf2323 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt
@@ -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()),
+ )
}
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt
index 68fef18387..f53849a3f9 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt
@@ -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
@@ -46,8 +47,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.*
@@ -73,6 +75,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
+import timber.log.Timber
import java.math.BigDecimal
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
@@ -107,6 +110,7 @@ internal class StakingViewModel @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getActionsUseCase: GetActionsUseCase,
+ private val paramsInterceptorHolder: ParamsInterceptorHolder,
@DelayedWork private val coroutineScope: CoroutineScope,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
@@ -205,6 +209,7 @@ internal class StakingViewModel @Inject constructor(
override fun onCleared() {
super.onCleared()
+ paramsInterceptorHolder.removeParamsInterceptor(StakingParamsInterceptor.ID)
approvalJobHolder.cancel()
feeJobHolder.cancel()
sendTransactionJobHolder.cancel()
@@ -239,18 +244,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)
},
@@ -278,12 +277,6 @@ internal class StakingViewModel @Inject constructor(
transactionsInProgress.addAll(constructedTransactions)
},
onConstructError = { error ->
- analyticsEventHandler.send(
- StakingAnalyticsEvents.StakekitError(
- token = value.cryptoCurrencySymbol,
- stakeKitError = error,
- ),
- )
stakingEventFactory.createStakingErrorAlert(error)
stateController.update(SetConfirmationStateResetAssentTransformer)
},
@@ -293,7 +286,7 @@ internal class StakingViewModel @Inject constructor(
stateController.update(SetConfirmationStateCompletedTransformer(txUrl))
},
onSendError = { error ->
- analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
+ analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
stakingEventFactory.createSendTransactionErrorAlert(error)
stateController.update(SetConfirmationStateResetAssentTransformer)
},
@@ -339,7 +332,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)
}
@@ -381,22 +374,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()
@@ -404,9 +396,8 @@ 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(
@@ -418,9 +409,7 @@ internal class StakingViewModel @Inject constructor(
}
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
@@ -428,9 +417,8 @@ internal class StakingViewModel @Inject constructor(
onActiveStake(rewards.first())
} else {
analyticsEventHandler.send(
- StakingAnalyticsEvents.ButtonValidator(
+ StakingAnalyticsEvent.ButtonValidator(
source = StakeScreenSource.Info,
- token = value.cryptoCurrencySymbol,
),
)
stateController.update {
@@ -470,9 +458,8 @@ internal class StakingViewModel @Inject constructor(
override fun onActiveStakeAnalytic() {
analyticsEventHandler.send(
- StakingAnalyticsEvents.ButtonValidator(
+ StakingAnalyticsEvent.ButtonValidator(
source = StakeScreenSource.Info,
- token = value.cryptoCurrencySymbol,
),
)
}
@@ -522,7 +509,8 @@ internal class StakingViewModel @Inject constructor(
userWalletId = userWalletId,
).fold(
ifLeft = { error ->
- analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
+ Timber.e(error.toString())
+ analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
stateController.update(
SetConfirmationStateAssentApprovalTransformer(
appCurrencyProvider = Provider { appCurrency },
@@ -543,7 +531,8 @@ internal class StakingViewModel @Inject constructor(
network = tokenCryptoCurrency.network,
).fold(
ifLeft = { error ->
- analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
+ Timber.e(error.toString())
+ analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
stateController.update(
SetConfirmationStateAssentApprovalTransformer(
appCurrencyProvider = Provider { appCurrency },
@@ -673,7 +662,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
@@ -687,7 +676,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)
@@ -769,10 +758,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,
),
)
}
diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml
index d010c3fb81..a907455383 100644
--- a/gradle/dependencies.toml
+++ b/gradle/dependencies.toml
@@ -88,7 +88,7 @@ markdownComposeView = "0.5.4"
# endregion Other libraries
# region Tangem
-tangemBlockchainSdk = "develop-836"
+tangemBlockchainSdk = "develop-841"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-391"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^