From c49d680508db7833e78d41a3dbf1c33034f273ec Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Nov 2024 15:02:44 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../datasource/di/StakingStoreModule.kt | 2 +- .../local/token/DefaultStakingYieldsStore.kt | 28 +++++++++++++++---- .../local/token/StakingYieldsStore.kt | 7 +++-- .../data/staking/DefaultStakingRepository.kt | 27 +++++++++++++----- .../staking/GetStakingAvailabilityUseCase.kt | 2 +- .../staking/repositories/StakingRepository.kt | 2 +- .../repository/MockStakingRepository.kt | 2 +- .../TokenDetailsSkeletonStateConverter.kt | 11 +------- .../state/factory/TokenDetailsStateFactory.kt | 3 -- .../viewmodels/TokenDetailsViewModel.kt | 1 - 10 files changed, 52 insertions(+), 33 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt index 3dda78cc57..34b84bebc9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt @@ -17,7 +17,7 @@ internal object StakingStoreModule { @Provides @Singleton fun provideStakingTokensStore(): StakingYieldsStore { - return DefaultStakingYieldsStore() + return DefaultStakingYieldsStore(dataStore = RuntimeDataStore()) } @Provides diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt index d30a83e4c3..eb27aad46b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt @@ -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>, +) : StakingYieldsStore { - private var yields = listOf() + private val mutex = Mutex() - override fun get(): List { - return yields + override fun get(): Flow> { + return dataStore.get(KEY) } - override fun store(items: List) { - yields = items + override suspend fun getSync(): List { + return dataStore.getSyncOrNull(KEY) ?: emptyList() + } + + override suspend fun store(items: List) { + mutex.withLock { + dataStore.store(KEY, items) + } + } + + companion object { + private const val KEY = "DefaultStakingYieldsStore" } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt index dc3379969d..428aa1986c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt @@ -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 + fun get(): Flow> - fun store(items: List) + suspend fun getSync(): List + + suspend fun store(items: List) } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index 3261381510..466e1346ff 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -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 { + private suspend fun getEnabledYieldsSync(): List { 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 diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt index b2ab9c2b1e..7133219a03 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt @@ -16,7 +16,7 @@ class GetStakingAvailabilityUseCase( private val stakingErrorResolver: StakingErrorResolver, ) { - operator fun invoke( + suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): Either { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index e68adf73a9..cc53ade06f 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -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, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt index fa3e6119d7..b66b4ccaf9 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt @@ -112,7 +112,7 @@ class MockStakingRepository : StakingRepository { isAvailable = false, ) - override fun getStakingAvailability( + override suspend fun getStakingAvailability( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): StakingAvailability = StakingAvailability.Unavailable diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 0f92424ba2..98850f08f2 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -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 { @@ -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(), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 08c2f45190..c3bc29d4a4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -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, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 1427b63a24..34c9d9f591 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -166,7 +166,6 @@ internal class TokenDetailsViewModel @Inject constructor( userWalletId = userWalletId, stakingFeatureToggles = stakingFeatureToggles, getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase, - getStakingAvailabilityUseCase = getStakingAvailabilityUseCase, symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, )