Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-25 13:44:57 +04:00
parent 2f767f566a
commit 496072829c
14 changed files with 47 additions and 146 deletions

View file

@ -173,18 +173,6 @@ internal object StakingDomainModule {
)
}
@Provides
@Singleton
fun provideIsApproveNeededUseCase(
stakingRepository: StakingRepository,
stakingErrorResolver: StakingErrorResolver,
): IsApproveNeededUseCase {
return IsApproveNeededUseCase(
stakingRepository = stakingRepository,
stakingErrorResolver = stakingErrorResolver,
)
}
@Provides
@Singleton
fun provideGetConstructedStakingTransactionUseCase(
@ -209,12 +197,6 @@ internal object StakingDomainModule {
)
}
@Provides
@Singleton
fun provideGetStakingIntegrationIdUseCase(stakingRepository: StakingRepository): GetStakingIntegrationIdUseCase {
return GetStakingIntegrationIdUseCase(stakingRepository)
}
@Provides
@Singleton
fun provideCheckAccountInitializedUseCase(
@ -225,9 +207,7 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideGetActionRequirementAmountUseCase(
stakingRepository: StakingRepository,
): GetActionRequirementAmountUseCase {
return GetActionRequirementAmountUseCase(stakingRepository)
fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase {
return GetActionRequirementAmountUseCase()
}
}

View file

@ -10,8 +10,6 @@ import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toCoinId
import com.tangem.blockchainsdk.utils.toMigratedCoinId
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toCompressedPublicKey
import com.tangem.data.staking.converters.YieldConverter
@ -38,9 +36,9 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.model.stakekit.NetworkType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.YieldBalance
@ -63,7 +61,6 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.math.BigDecimal
import kotlin.time.Duration.Companion.seconds
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
internal class DefaultStakingRepository(
@ -95,10 +92,6 @@ internal class DefaultStakingRepository(
private val networkTypeAdapter by lazy { moshi.adapter(NetworkTypeDTO::class.java) }
private val stakingActionStatusAdapter by lazy { moshi.adapter(StakingActionStatusDTO::class.java) }
override fun getSupportedIntegrationId(cryptoCurrencyId: CryptoCurrency.ID): String? {
return stakingIdFactory.createIntegrationId(currencyId = cryptoCurrencyId)
}
override suspend fun fetchEnabledYields() {
withContext(dispatchers.io) {
when (val stakingTokensWithYields = stakeKitApi.getEnabledYields(preferredValidatorsOnly = false)) {
@ -202,7 +195,7 @@ internal class DefaultStakingRepository(
return@channelFlow
}
val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not()
val isSupportedInMobileApp = StakingIntegrationID.create(currencyId = cryptoCurrency.id) != null
getEnabledYields()
.distinctUntilChanged()
@ -248,7 +241,7 @@ internal class DefaultStakingRepository(
return StakingAvailability.Unavailable
}
val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not()
val isSupportedInMobileApp = StakingIntegrationID.create(currencyId = cryptoCurrency.id) != null
val yields = getEnabledYieldsSync()
if (yields.isEmpty()) {
@ -481,17 +474,6 @@ internal class DefaultStakingRepository(
}
}
override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval {
val integrationId = stakingIdFactory.createIntegrationId(currencyId = cryptoCurrency.id)
return when (integrationId) {
Blockchain.Ethereum.id + Blockchain.Polygon.toCoinId(),
Blockchain.Ethereum.id + Blockchain.Polygon.toMigratedCoinId(),
-> StakingApproval.Needed(ETHEREUM_POLYGON_APPROVE_SPENDER)
else -> StakingApproval.Empty
}
}
private fun getTransactionDataType(networkId: String, unsignedTransaction: String): TransactionData.Compiled.Data {
return when (Blockchain.fromId(networkId)) {
Blockchain.Solana,
@ -543,14 +525,7 @@ internal class DefaultStakingRepository(
}
}
@Suppress("unused")
companion object {
private const val YIELDS_STORE_KEY = "yields"
private const val ETHEREUM_POLYGON_APPROVE_SPENDER = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908"
internal val YIELDS_WATITING_TIMEOUT = 15.seconds
private val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79")
}
}

View file

@ -14,7 +14,6 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.express.ExpressRepository
import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.swap.SwapErrorResolver
import com.tangem.domain.swap.SwapRepositoryV2
import com.tangem.domain.swap.SwapTransactionRepository
@ -49,7 +48,6 @@ internal object SwapDataModule {
dataSignatureVerifier: DataSignatureVerifier,
singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
singleQuoteStatusFetcher: SingleQuoteStatusFetcher,
stakingRepository: StakingRepository,
@NetworkMoshi moshi: Moshi,
): SwapRepositoryV2 {
return DefaultSwapRepositoryV2(
@ -61,7 +59,7 @@ internal object SwapDataModule {
moshi = moshi,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleQuoteStatusFetcher = singleQuoteStatusFetcher,
currencyStatusProxyCreator = CurrencyStatusProxyCreator(stakingRepository),
currencyStatusProxyCreator = CurrencyStatusProxyCreator(),
)
}

View file

@ -1,16 +1,18 @@
package com.tangem.domain.staking
import arrow.core.Either
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
import com.tangem.domain.staking.repositories.StakingRepository
import java.math.BigDecimal
class GetActionRequirementAmountUseCase(
private val stakingRepository: StakingRepository,
) {
class GetActionRequirementAmountUseCase {
operator fun invoke(integrationId: String, actionType: StakingActionType): Either<Throwable, BigDecimal?> =
Either.catch {
stakingRepository.getActionRequirementAmount(integrationId, actionType)
operator fun invoke(integrationId: String, actionType: StakingActionType): BigDecimal? {
return if (StakingIntegrationID.EthereumToken.Polygon.value == integrationId &&
actionType == StakingActionType.CLAIM_REWARDS
) {
BigDecimal.ONE
} else {
null
}
}
}

View file

@ -1,12 +0,0 @@
package com.tangem.domain.staking
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.repositories.StakingRepository
class GetStakingIntegrationIdUseCase(
private val stakingRepository: StakingRepository,
) {
operator fun invoke(cryptoCurrencyId: CryptoCurrency.ID) =
stakingRepository.getSupportedIntegrationId(cryptoCurrencyId)
}

View file

@ -1,19 +0,0 @@
package com.tangem.domain.staking
import arrow.core.Either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
class IsApproveNeededUseCase(
private val stakingRepository: StakingRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
operator fun invoke(cryptoCurrency: CryptoCurrency): Either<StakingError, StakingApproval> {
return Either
.catch { stakingRepository.getStakingApproval(cryptoCurrency) }
.mapLeft { stakingErrorResolver.resolve(it) }
}
}

View file

@ -6,7 +6,6 @@ import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.model.stakekit.NetworkType
@ -24,8 +23,6 @@ import java.math.BigDecimal
@Suppress("TooManyFunctions")
interface StakingRepository {
fun getSupportedIntegrationId(cryptoCurrencyId: CryptoCurrency.ID): String?
suspend fun fetchEnabledYields()
suspend fun getEntryInfo(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): StakingEntryInfo
@ -66,9 +63,6 @@ interface StakingRepository {
transactionId: String,
): Pair<StakingTransaction, TransactionData.Compiled>
/** Returns staking approval */
fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval
suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean
/**

View file

@ -52,7 +52,7 @@ abstract class BaseCurrencyStatusOperations(
private val tokensFeatureToggles: TokensFeatureToggles,
) {
protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator(stakingRepository)
protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator()
protected abstract fun getQuotes(id: CryptoCurrency.RawID): Flow<Either<Error, Set<QuoteStatus>>>

View file

@ -24,8 +24,8 @@ import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.YieldBalance.Unsupported.integrationId
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
@ -46,7 +46,7 @@ import kotlinx.coroutines.flow.*
class CachedCurrenciesStatusesOperations(
private val currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
stakingRepository: StakingRepository,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -270,9 +270,7 @@ class CachedCurrenciesStatusesOperations(
): YieldBalance? {
if (yieldBalances.isNullOrEmpty()) return null
val supportedIntegration = stakingRepository.getSupportedIntegrationId(currency.id)
if (supportedIntegration.isNullOrBlank()) return null
val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value ?: return null
val address = extractAddress(networkStatus)

View file

@ -7,9 +7,8 @@ import arrow.core.toNonEmptySetOrNull
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.YieldBalance.Unsupported.integrationId
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.operations.CurrencyStatusOperations
@ -17,13 +16,9 @@ import com.tangem.domain.tokens.operations.CurrencyStatusOperations
/**
* Proxy creator of [CryptoCurrencyStatus]. Used [CurrencyStatusOperations] to create statuses.
*
* @property stakingRepository staking repository
*
[REDACTED_AUTHOR]
*/
class CurrencyStatusProxyCreator(
private val stakingRepository: StakingRepository,
) {
class CurrencyStatusProxyCreator {
fun createCurrencyStatus(
currency: CryptoCurrency,
@ -78,8 +73,8 @@ class CurrencyStatusProxyCreator(
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
val address = extractAddress(networkStatus)
val supportedIntegration = stakingRepository.getSupportedIntegrationId(currency.id)
val yieldBalance = if (supportedIntegration.isNullOrEmpty().not()) {
val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value
val yieldBalance = if (supportedIntegration != null) {
yieldBalances?.firstOrNull { it.integrationId == supportedIntegration && it.address == address }
?: YieldBalance.Error(integrationId = supportedIntegration, address = address)
} else {

View file

@ -29,10 +29,14 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.staking.*
import com.tangem.domain.staking.analytics.StakeScreenSource
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.staking.model.stakekit.*
import com.tangem.domain.staking.model.stakekit.action.StakingAction
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
@ -45,9 +49,6 @@ import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.staking.api.StakingComponent
import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor
@ -103,7 +104,6 @@ internal class StakingModel @Inject constructor(
private val sendTransactionUseCase: SendTransactionUseCase,
private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase,
private val getAllowanceUseCase: GetAllowanceUseCase,
private val isApproveNeededUseCase: IsApproveNeededUseCase,
private val vibratorHapticManager: VibratorHapticManager,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
@ -534,7 +534,7 @@ internal class StakingModel @Inject constructor(
getActionRequirementAmountUseCase.invoke(
integrationId = yieldBalance.integrationId,
actionType = StakingActionType.CLAIM_REWARDS,
).getOrNull()
)
} else {
minimumAmount
}
@ -904,21 +904,18 @@ internal class StakingModel @Inject constructor(
}
private suspend fun setupApprovalNeeded() {
stakingApproval = isApproveNeededUseCase(cryptoCurrencyStatus.currency).fold(
ifRight = { approval ->
if (approval is StakingApproval.Needed) {
stakingAllowance = getAllowanceUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
spenderAddress = approval.spenderAddress,
).getOrElse { BigDecimal.ZERO }
}
approval
},
ifLeft = {
StakingApproval.Empty
},
)
val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval
?: StakingApproval.Empty
stakingApproval = approval
if (approval is StakingApproval.Needed) {
stakingAllowance = getAllowanceUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
spenderAddress = approval.spenderAddress,
).getOrElse { BigDecimal.ZERO }
}
}
private suspend fun setupIsAnyTokenStaked() {

View file

@ -38,13 +38,14 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.promo.ShouldShowPromoTokenUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
import com.tangem.domain.staking.GetYieldUseCase
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.tokens.*
@ -67,8 +68,6 @@ import com.tangem.domain.transaction.usecase.RetryIncompleteTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
@ -133,7 +132,6 @@ internal class TokenDetailsModel @Inject constructor(
paramsContainer: ParamsContainer,
expressStatusFactory: ExpressStatusFactory.Factory,
getUserWalletUseCase: GetUserWalletUseCase,
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
private val appRouter: AppRouter,
private val router: InnerTokenDetailsRouter,
private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener,
@ -169,7 +167,6 @@ internal class TokenDetailsModel @Inject constructor(
networkHasDerivationUseCase = networkHasDerivationUseCase,
getUserWalletUseCase = getUserWalletUseCase,
userWalletId = userWalletId,
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
symbol = cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)

View file

@ -11,8 +11,8 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
internal class TokenDetailsSkeletonStateConverter(
private val clickIntents: TokenDetailsClickIntents,
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
private val getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val userWalletId: UserWalletId,
) : Converter<CryptoCurrency, TokenDetailsState> {
@ -38,7 +37,7 @@ internal class TokenDetailsSkeletonStateConverter(
override fun convert(value: CryptoCurrency): TokenDetailsState {
val iconState = iconStateConverter.convert(value)
val isSupportedInMobileApp = getStakingIntegrationIdUseCase(value.id).isNullOrBlank().not()
val isSupportedInMobileApp = StakingIntegrationID.create(currencyId = value.id) != null
return TokenDetailsState(
topAppBarConfig = TokenDetailsTopAppBarConfig(

View file

@ -18,7 +18,8 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.tokens.error.CurrencyStatusError
@ -28,8 +29,6 @@ import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
@ -55,7 +54,6 @@ internal class TokenDetailsStateFactory(
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val userWalletId: UserWalletId,
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
symbol: String,
decimals: Int,
) {
@ -64,7 +62,6 @@ internal class TokenDetailsStateFactory(
TokenDetailsSkeletonStateConverter(
clickIntents = clickIntents,
networkHasDerivationUseCase = networkHasDerivationUseCase,
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
getUserWalletUseCase = getUserWalletUseCase,
userWalletId = userWalletId,
)