Updated on 2026-08-14
This commit is contained in:
parent
dfa8619c13
commit
c49d680508
10 changed files with 52 additions and 33 deletions
|
|
@ -17,7 +17,7 @@ internal object StakingStoreModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingTokensStore(): StakingYieldsStore {
|
||||
return DefaultStakingYieldsStore()
|
||||
return DefaultStakingYieldsStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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>)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class GetStakingAvailabilityUseCase(
|
|||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Either<StakingError, StakingAvailability> {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class MockStakingRepository : StakingRepository {
|
|||
isAvailable = false,
|
||||
)
|
||||
|
||||
override fun getStakingAvailability(
|
||||
override suspend fun getStakingAvailability(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): StakingAvailability = StakingAvailability.Unavailable
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
userWalletId = userWalletId,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
|
||||
getStakingAvailabilityUseCase = getStakingAvailabilityUseCase,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue