Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-13 15:02:44 +02:00
parent dfa8619c13
commit c49d680508
10 changed files with 52 additions and 33 deletions

View file

@ -17,7 +17,7 @@ internal object StakingStoreModule {
@Provides
@Singleton
fun provideStakingTokensStore(): StakingYieldsStore {
return DefaultStakingYieldsStore()
return DefaultStakingYieldsStore(dataStore = RuntimeDataStore())
}
@Provides

View file

@ -1,16 +1,32 @@
package com.tangem.datasource.local.token
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
internal class DefaultStakingYieldsStore : StakingYieldsStore {
internal class DefaultStakingYieldsStore(
private val dataStore: StringKeyDataStore<List<YieldDTO>>,
) : StakingYieldsStore {
private var yields = listOf<YieldDTO>()
private val mutex = Mutex()
override fun get(): List<YieldDTO> {
return yields
override fun get(): Flow<List<YieldDTO>> {
return dataStore.get(KEY)
}
override fun store(items: List<YieldDTO>) {
yields = items
override suspend fun getSync(): List<YieldDTO> {
return dataStore.getSyncOrNull(KEY) ?: emptyList()
}
override suspend fun store(items: List<YieldDTO>) {
mutex.withLock {
dataStore.store(KEY, items)
}
}
companion object {
private const val KEY = "DefaultStakingYieldsStore"
}
}

View file

@ -1,10 +1,13 @@
package com.tangem.datasource.local.token
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
import kotlinx.coroutines.flow.Flow
interface StakingYieldsStore {
fun get(): List<YieldDTO>
fun get(): Flow<List<YieldDTO>>
fun store(items: List<YieldDTO>)
suspend fun getSync(): List<YieldDTO>
suspend fun store(items: List<YieldDTO>)
}

View file

@ -56,7 +56,9 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import timber.log.Timber
import kotlin.time.Duration.Companion.seconds
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
internal class DefaultStakingRepository(
@ -133,7 +135,7 @@ internal class DefaultStakingRepository(
val rawCurrencyId = cryptoCurrencyId.rawCurrencyId ?: error("Staking custom tokens is not available")
val prefetchedYield = findPrefetchedYield(
yields = getEnabledYields(),
yields = getEnabledYieldsSync(),
currencyId = rawCurrencyId,
symbol = symbol,
)
@ -188,7 +190,7 @@ internal class DefaultStakingRepository(
}
}
override fun getStakingAvailability(
override suspend fun getStakingAvailability(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
): StakingAvailability {
@ -199,7 +201,7 @@ internal class DefaultStakingRepository(
val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not()
val prefetchedYield = findPrefetchedYield(
yields = getEnabledYields(),
yields = getEnabledYieldsSync(),
currencyId = rawCurrencyId,
symbol = cryptoCurrency.symbol,
)
@ -420,12 +422,22 @@ internal class DefaultStakingRepository(
key = getYieldBalancesKey(userWalletId),
skipCache = refresh,
block = {
val yields = getEnabledYields().ifEmpty {
val yieldDTOs = withTimeoutOrNull(YIELDS_WATITING_TIMEOUT) {
runCatching { stakingYieldsStore.get().firstOrNull() }.getOrNull()
}
if (yieldDTOs == null) {
Timber.i("No enabled yields for $userWalletId")
stakingBalanceStore.store(userWalletId, emptySet())
return@invokeOnExpire
}
val yields = yieldConverter.convertListIgnoreErrors(
input = yieldDTOs,
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
)
val availableCurrencies = cryptoCurrencies
.mapNotNull { currency ->
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
@ -442,7 +454,6 @@ internal class DefaultStakingRepository(
}
.map { getBalanceRequestData(it.first.value, it.second) }
.ifEmpty {
Timber.i("No yield balances available for $userWalletId")
stakingBalanceStore.store(userWalletId, emptySet())
cacheRegistry.invalidate(getYieldBalancesKey(userWalletId))
@ -586,9 +597,9 @@ internal class DefaultStakingRepository(
}
}
private fun getEnabledYields(): List<Yield> {
private suspend fun getEnabledYieldsSync(): List<Yield> {
return yieldConverter.convertListIgnoreErrors(
input = stakingYieldsStore.get(),
input = stakingYieldsStore.getSync(),
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
)
}
@ -636,6 +647,8 @@ internal class DefaultStakingRepository(
const val ETHEREUM_POLYGON_APPROVE_SPENDER = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908"
val YIELDS_WATITING_TIMEOUT = 15.seconds
val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79")
// uncomment items as implementation is ready

View file

@ -16,7 +16,7 @@ class GetStakingAvailabilityUseCase(
private val stakingErrorResolver: StakingErrorResolver,
) {
operator fun invoke(
suspend operator fun invoke(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
): Either<StakingError, StakingAvailability> {

View file

@ -33,7 +33,7 @@ interface StakingRepository {
suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield
fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability
suspend fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability
suspend fun getActions(
userWalletId: UserWalletId,

View file

@ -112,7 +112,7 @@ class MockStakingRepository : StakingRepository {
isAvailable = false,
)
override fun getStakingAvailability(
override suspend fun getStakingAvailability(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
): StakingAvailability = StakingAvailability.Unavailable

View file

@ -10,9 +10,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.card.NetworkHasDerivationUseCase
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWalletId
@ -32,7 +30,6 @@ internal class TokenDetailsSkeletonStateConverter(
private val clickIntents: TokenDetailsClickIntents,
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
private val getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val userWalletId: UserWalletId,
) : Converter<CryptoCurrency, TokenDetailsState> {
@ -42,8 +39,6 @@ internal class TokenDetailsSkeletonStateConverter(
override fun convert(value: CryptoCurrency): TokenDetailsState {
val iconState = iconStateConverter.convert(value)
val isSupportedInMobileApp = getStakingIntegrationIdUseCase(value.id).isNullOrBlank().not()
val stakingAvailability = getStakingAvailabilityUseCase(userWalletId, value)
.getOrElse { StakingAvailability.Unavailable }
return TokenDetailsState(
topAppBarConfig = TokenDetailsTopAppBarConfig(
@ -68,11 +63,7 @@ internal class TokenDetailsSkeletonStateConverter(
selectedBalanceType = BalanceType.ALL,
),
marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol),
stakingBlocksState = if (stakingAvailability == StakingAvailability.TemporaryUnavailable) {
StakingBlockUM.TemporaryUnavailable
} else {
StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp }
},
stakingBlocksState = StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp },
notifications = persistentListOf(),
pendingTxs = persistentListOf(),
swapTxs = persistentListOf(),

View file

@ -17,7 +17,6 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.NetworkHasDerivationUseCase
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
@ -59,7 +58,6 @@ internal class TokenDetailsStateFactory(
private val userWalletId: UserWalletId,
stakingFeatureToggles: StakingFeatureToggles,
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase,
symbol: String,
decimals: Int,
) {
@ -69,7 +67,6 @@ internal class TokenDetailsStateFactory(
clickIntents = clickIntents,
networkHasDerivationUseCase = networkHasDerivationUseCase,
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
getStakingAvailabilityUseCase = getStakingAvailabilityUseCase,
getUserWalletUseCase = getUserWalletUseCase,
userWalletId = userWalletId,
)

View file

@ -166,7 +166,6 @@ internal class TokenDetailsViewModel @Inject constructor(
userWalletId = userWalletId,
stakingFeatureToggles = stakingFeatureToggles,
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
getStakingAvailabilityUseCase = getStakingAvailabilityUseCase,
symbol = cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)