Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-15 20:29:25 +05:00
parent 14163a3088
commit 20eb597613
18 changed files with 217 additions and 158 deletions

View file

@ -158,8 +158,9 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): FetchCurrencyStatusUseCase {
return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository)
return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository, stakingRepository)
}
@Provides

View file

@ -34,7 +34,7 @@ interface StakeKitApi {
@POST("yields/balances")
suspend fun getMultipleYieldBalances(
@Body body: List<YieldBalanceRequestBody>,
): ApiResponse<List<YieldBalanceWrapperDTO>>
): ApiResponse<Set<YieldBalanceWrapperDTO>>
@POST("yields/{integrationId}/balances")
suspend fun getSingleYieldBalance(

View file

@ -3,49 +3,53 @@ package com.tangem.datasource.local.token
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
internal class DefaultStakingBalanceStore(
private val dataStore: StringKeyDataStore<List<YieldBalanceWrapperDTO>>,
private val dataStore: StringKeyDataStore<Set<YieldBalanceWrapperDTO>>,
) : StakingBalanceStore {
override fun get(): Flow<List<YieldBalanceWrapperDTO>> {
return dataStore.get(STAKING_BALANCE_KEY)
private val mutex = Mutex()
override fun get(userWalletId: UserWalletId): Flow<Set<YieldBalanceWrapperDTO>> {
return dataStore.get(userWalletId.stringValue)
}
override suspend fun getSyncOrNull(): List<YieldBalanceWrapperDTO>? {
return dataStore.getSyncOrNull(STAKING_BALANCE_KEY)
override suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalanceWrapperDTO>? {
return dataStore.getSyncOrNull(userWalletId.stringValue)
}
override suspend fun store(items: List<YieldBalanceWrapperDTO>) {
return dataStore.store(STAKING_BALANCE_KEY, items)
override suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>) {
mutex.withLock {
dataStore.store(userWalletId.stringValue, items)
}
}
override fun get(integrationId: String): Flow<List<BalanceDTO>> {
return dataStore.get(STAKING_BALANCE_KEY)
override fun get(userWalletId: UserWalletId, integrationId: String): Flow<List<BalanceDTO>> {
return dataStore.get(userWalletId.stringValue)
.map { balances ->
balances.filter { it.integrationId == integrationId }
.flatMap { it.balances }
}
}
override suspend fun getSyncOrNull(integrationId: String): List<BalanceDTO>? {
return dataStore.getSyncOrNull(STAKING_BALANCE_KEY)
override suspend fun getSyncOrNull(userWalletId: UserWalletId, integrationId: String): List<BalanceDTO>? {
return dataStore.getSyncOrNull(userWalletId.stringValue)
?.firstOrNull { it.integrationId == integrationId }?.balances
}
override suspend fun store(integrationId: String, item: YieldBalanceWrapperDTO) {
val balances = dataStore.getSyncOrNull(STAKING_BALANCE_KEY)
?.toMutableList()
?.addOrReplace(item) { item.integrationId == integrationId }
?: listOf(item)
override suspend fun store(userWalletId: UserWalletId, integrationId: String, item: YieldBalanceWrapperDTO) {
mutex.withLock {
val balances = dataStore.getSyncOrNull(userWalletId.stringValue)
?.addOrReplace(item) { it.integrationId == integrationId }
?: setOf(item)
return dataStore.store(STAKING_BALANCE_KEY, balances)
}
companion object {
private const val STAKING_BALANCE_KEY = "STAKING_BALANCE_KEY"
dataStore.store(userWalletId.stringValue, balances)
}
}
}

View file

@ -2,19 +2,20 @@ package com.tangem.datasource.local.token
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
interface StakingBalanceStore {
fun get(): Flow<List<YieldBalanceWrapperDTO>>
fun get(userWalletId: UserWalletId): Flow<Set<YieldBalanceWrapperDTO>>
suspend fun getSyncOrNull(): List<YieldBalanceWrapperDTO>?
suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalanceWrapperDTO>?
suspend fun store(items: List<YieldBalanceWrapperDTO>)
suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>)
fun get(integrationId: String): Flow<List<BalanceDTO>>
fun get(userWalletId: UserWalletId, integrationId: String): Flow<List<BalanceDTO>>
suspend fun getSyncOrNull(integrationId: String): List<BalanceDTO>?
suspend fun getSyncOrNull(userWalletId: UserWalletId, integrationId: String): List<BalanceDTO>?
suspend fun store(integrationId: String, item: YieldBalanceWrapperDTO)
suspend fun store(userWalletId: UserWalletId, integrationId: String, item: YieldBalanceWrapperDTO)
}

View file

@ -45,7 +45,6 @@ import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
@ -261,25 +260,27 @@ internal class DefaultStakingRepository(
override suspend fun fetchSingleYieldBalance(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
refresh: Boolean,
) = withContext(dispatchers.io) {
if (!stakingFeatureToggle.isStakingEnabled) return@withContext
val cryptoCurrency = address.cryptoCurrency
val integrationId = integrationIdMap[cryptoCurrency.id.getIntegrationKey()] ?: return@withContext
val address = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network).orEmpty()
cacheRegistry.invokeOnExpire(
key = getYieldBalancesKey(userWalletId),
skipCache = refresh,
block = {
val requestBody = getBalanceRequestData(address.address, integrationId)
val requestBody = getBalanceRequestData(address, integrationId)
val result = stakeKitApi.getSingleYieldBalance(
integrationId = requestBody.integrationId,
body = requestBody,
).getOrThrow()
stakingBalanceStore.store(
userWalletId,
requestBody.integrationId,
YieldBalanceWrapperDTO(
balances = result,
@ -292,15 +293,15 @@ internal class DefaultStakingRepository(
override fun getSingleYieldBalanceFlow(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
): Flow<YieldBalance> = channelFlow {
if (!stakingFeatureToggle.isStakingEnabled) {
send(YieldBalance.Empty)
} else {
launch(dispatchers.io) {
val integrationId = integrationIdMap[address.cryptoCurrency.id.getIntegrationKey()]
val integrationId = integrationIdMap[cryptoCurrency.id.getIntegrationKey()]
?: error("Could not get integrationId")
stakingBalanceStore.get(integrationId)
stakingBalanceStore.get(userWalletId, integrationId)
.collectLatest {
send(
yieldBalanceConverter.convert(
@ -316,7 +317,7 @@ internal class DefaultStakingRepository(
withContext(dispatchers.io) {
fetchSingleYieldBalance(
userWalletId,
address,
cryptoCurrency,
)
}
}
@ -324,16 +325,18 @@ internal class DefaultStakingRepository(
override suspend fun getSingleYieldBalanceSync(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
): YieldBalance = withContext(dispatchers.io) {
if (!stakingFeatureToggle.isStakingEnabled) {
YieldBalance.Empty
} else {
fetchSingleYieldBalance(userWalletId, address)
fetchSingleYieldBalance(userWalletId, cryptoCurrency)
val integrationId = integrationIdMap[address.cryptoCurrency.id.getIntegrationKey()]
val integrationId = integrationIdMap[cryptoCurrency.id.getIntegrationKey()]
?: error("Could not get integrationId")
val result = stakingBalanceStore.getSyncOrNull(integrationId) ?: return@withContext YieldBalance.Error
val result = stakingBalanceStore.getSyncOrNull(userWalletId, integrationId)
?: return@withContext YieldBalance.Error
yieldBalanceConverter.convert(
YieldBalanceConverter.Data(
balance = result,
@ -345,7 +348,7 @@ internal class DefaultStakingRepository(
override suspend fun fetchMultiYieldBalance(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
refresh: Boolean,
) = withContext(dispatchers.io) {
if (!stakingFeatureToggle.isStakingEnabled) return@withContext
@ -357,23 +360,23 @@ internal class DefaultStakingRepository(
key = getYieldBalancesKey(userWalletId),
skipCache = refresh,
block = {
val result = stakeKitApi.getMultipleYieldBalances(
addresses
.mapNotNull { networkAddress ->
val cryptoCurrency = networkAddress.cryptoCurrency
val integrationId = integrationIdMap[cryptoCurrency.id.getIntegrationKey()]
val availableCurrencies = cryptoCurrencies
.mapNotNull { currency ->
val address = walletManagersFacade.getDefaultAddress(userWalletId, currency.network)
val integrationId = integrationIdMap[currency.id.getIntegrationKey()]
if (integrationId != null) {
networkAddress.address to integrationId
} else {
null
}
if (integrationId != null && address != null) {
address to integrationId
} else {
null
}
.distinct()
.map { getBalanceRequestData(it.first, it.second) },
).getOrThrow()
}
.distinct()
.map { getBalanceRequestData(it.first, it.second) }
.ifEmpty { return@invokeOnExpire }
val result = stakeKitApi.getMultipleYieldBalances(availableCurrencies).getOrThrow()
stakingBalanceStore.store(result)
stakingBalanceStore.store(userWalletId, result)
},
)
} finally {
@ -385,20 +388,20 @@ internal class DefaultStakingRepository(
override fun getMultiYieldBalanceFlow(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): Flow<YieldBalanceList> = channelFlow {
if (!stakingFeatureToggle.isStakingEnabled) {
send(YieldBalanceList.Empty)
} else {
launch(dispatchers.io) {
stakingBalanceStore.get()
stakingBalanceStore.get(userWalletId)
.collectLatest { send(yieldBalanceListConverter.convert(it)) }
}
withContext(dispatchers.io) {
fetchMultiYieldBalance(
userWalletId,
addresses,
cryptoCurrencies,
)
}
}
@ -406,14 +409,14 @@ internal class DefaultStakingRepository(
override fun getMultiYieldBalanceLce(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
if (!stakingFeatureToggle.isStakingEnabled) {
send(YieldBalanceList.Empty)
} else {
launch(dispatchers.io) {
combine(
stakingBalanceStore.get(),
stakingBalanceStore.get(userWalletId),
isYieldBalanceFetching.map { it.getOrElse(userWalletId) { false } },
) { result, isFetching ->
val balances = yieldBalanceListConverter.convert(result)
@ -422,7 +425,7 @@ internal class DefaultStakingRepository(
}
withContext(dispatchers.io) {
catch(
block = { fetchMultiYieldBalance(userWalletId, addresses, refresh = false) },
block = { fetchMultiYieldBalance(userWalletId, cryptoCurrencies, refresh = false) },
catch = { raise(it) },
)
}
@ -431,13 +434,13 @@ internal class DefaultStakingRepository(
override suspend fun getMultiYieldBalanceSync(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): YieldBalanceList = withContext(dispatchers.io) {
if (!stakingFeatureToggle.isStakingEnabled) {
YieldBalanceList.Empty
} else {
fetchMultiYieldBalance(userWalletId, addresses)
val result = stakingBalanceStore.getSyncOrNull() ?: return@withContext YieldBalanceList.Error
fetchMultiYieldBalance(userWalletId, cryptoCurrencies)
val result = stakingBalanceStore.getSyncOrNull(userWalletId) ?: return@withContext YieldBalanceList.Error
yieldBalanceListConverter.convert(result)
}
}

View file

@ -4,13 +4,13 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrap
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
import com.tangem.utils.converter.Converter
internal class YieldBalanceListConverter : Converter<List<YieldBalanceWrapperDTO>, YieldBalanceList> {
internal class YieldBalanceListConverter : Converter<Set<YieldBalanceWrapperDTO>, YieldBalanceList> {
internal val converter by lazy(LazyThreadSafetyMode.NONE) {
YieldBalanceConverter()
}
override fun convert(value: List<YieldBalanceWrapperDTO>): YieldBalanceList {
override fun convert(value: Set<YieldBalanceWrapperDTO>): YieldBalanceList {
return if (value.isEmpty()) {
YieldBalanceList.Empty
} else {

View file

@ -376,15 +376,12 @@ class DefaultWalletManagersFacade(
return walletManagersStore.getAllSync(userWalletId)
}
@Deprecated(
"Use NetworkAddress from CryptoCurrencyStatus",
ReplaceWith("cryptoCurrencyStatus.value.networkAddress"),
)
override suspend fun getAddress(userWalletId: UserWalletId, network: Network): List<Address> {
return getAddresses(userWalletId, network).sortedBy { it.type }
override suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String? {
return getAddresses(userWalletId, network)
.firstOrNull { it.type == AddressType.Default }
?.value
}
@Deprecated("Use NetworkAddress from CryptoCurrencyStatus")
override suspend fun getAddresses(userWalletId: UserWalletId, network: Network): Set<Address> {
val manager = getOrCreateWalletManager(
userWalletId = userWalletId,

View file

@ -117,20 +117,18 @@ interface WalletManagersFacade {
suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List<WalletManager>
/**
* Returns ordered list of addresses for selected wallet for given currency
* Returns default network address for selected wallet in given network
*
* @param userWalletId selected wallet id
* @param network network of currency
*/
@Deprecated("Use NetworkAddress from CryptoCurrencyStatus")
suspend fun getAddress(userWalletId: UserWalletId, network: Network): List<Address>
suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String?
/** Returns list of all addresses for all currencies in selected wallet
*
* @param userWalletId selected wallet id
* @param network required to create wallet manager
*/
@Deprecated("Use NetworkAddress from CryptoCurrencyStatus")
suspend fun getAddresses(userWalletId: UserWalletId, network: Network): Set<Address>
/**

View file

@ -6,7 +6,7 @@ import arrow.core.raise.either
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
class FetchStakingYieldBalanceUseCase(
@ -16,7 +16,7 @@ class FetchStakingYieldBalanceUseCase(
suspend operator fun invoke(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
refresh: Boolean = false,
): Either<StakingError, Unit> {
return either {
@ -24,7 +24,7 @@ class FetchStakingYieldBalanceUseCase(
block = {
stakingRepository.fetchSingleYieldBalance(
userWalletId = userWalletId,
address = address,
cryptoCurrency = cryptoCurrency,
refresh = refresh,
)
},

View file

@ -8,7 +8,7 @@ import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
@ -20,11 +20,11 @@ class GetStakingYieldBalanceUseCase(
operator fun invoke(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
): EitherFlow<StakingError, YieldBalance> {
return stakingRepository.getSingleYieldBalanceFlow(
userWalletId = userWalletId,
address = address,
cryptoCurrency = cryptoCurrency,
).map<YieldBalance, Either<StakingError, YieldBalance>> { it.right() }
.catch { emit(stakingErrorResolver.resolve(it).left()) }
}

View file

@ -15,7 +15,6 @@ import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
@ -38,33 +37,33 @@ interface StakingRepository {
suspend fun fetchSingleYieldBalance(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
refresh: Boolean = false,
)
fun getSingleYieldBalanceFlow(userWalletId: UserWalletId, address: CryptoCurrencyAddress): Flow<YieldBalance>
fun getSingleYieldBalanceFlow(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Flow<YieldBalance>
suspend fun getSingleYieldBalanceSync(userWalletId: UserWalletId, address: CryptoCurrencyAddress): YieldBalance
suspend fun getSingleYieldBalanceSync(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): YieldBalance
suspend fun fetchMultiYieldBalance(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
refresh: Boolean = false,
)
fun getMultiYieldBalanceFlow(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): Flow<YieldBalanceList>
fun getMultiYieldBalanceLce(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): LceFlow<Throwable, YieldBalanceList>
suspend fun getMultiYieldBalanceSync(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): YieldBalanceList
suspend fun createAction(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingAction

View file

@ -44,6 +44,7 @@ class FetchCardTokenListUseCase(
val yieldBalances = async {
fetchYieldBalances(
userWalletId = userWalletId,
currencies = currencies,
refresh = refresh,
)
}
@ -77,10 +78,13 @@ class FetchCardTokenListUseCase(
)
}
private suspend fun fetchYieldBalances(userWalletId: UserWalletId, refresh: Boolean) {
val networkAddresses = networksRepository.getNetworkAddresses(userWalletId)
private suspend fun fetchYieldBalances(
userWalletId: UserWalletId,
currencies: List<CryptoCurrency>,
refresh: Boolean,
) {
catch(
block = { stakingRepository.fetchMultiYieldBalance(userWalletId, networkAddresses, refresh) },
block = { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies, refresh) },
catch = { /* Ignore error */ },
)
}

View file

@ -4,6 +4,7 @@ import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
@ -29,6 +30,7 @@ class FetchCurrencyStatusUseCase(
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
) {
/**
@ -80,8 +82,11 @@ class FetchCurrencyStatusUseCase(
val fetchQuote = async {
fetchQuote(currency.id, refresh)
}
val fetchStakingBalance = async {
fetchStakingBalance(userWalletId, currency, refresh)
}
awaitAll(fetchStatus, fetchQuote)
awaitAll(fetchStatus, fetchQuote, fetchStakingBalance)
}
private suspend fun Raise<CurrencyStatusError>.getCurrency(
@ -122,4 +127,16 @@ class FetchCurrencyStatusUseCase(
raise(CurrencyStatusError.DataError(it))
}
}
private suspend fun Raise<CurrencyStatusError>.fetchStakingBalance(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
refresh: Boolean,
) {
catch(
block = { stakingRepository.fetchSingleYieldBalance(userWalletId, cryptoCurrency, refresh) },
) {
raise(CurrencyStatusError.DataError(it))
}
}
}

View file

@ -69,11 +69,10 @@ internal class CurrenciesStatusesLceOperations(
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
val addresses = networksRepository.getNetworkAddresses(userWalletId)
combine(
getQuotes(currenciesIds),
getNetworksStatuses(userWalletId, networks),
getYieldBalances(userWalletId, addresses),
getYieldBalances(userWalletId, nonEmptyCurrencies),
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
val statuses = createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
@ -196,11 +195,11 @@ internal class CurrenciesStatusesLceOperations(
private fun getYieldBalances(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): LceFlow<TokenListError, YieldBalanceList> {
return stakingRepository.getMultiYieldBalanceLce(
userWalletId = userWalletId,
addresses = addresses,
cryptoCurrencies = cryptoCurrencies,
).map { maybeBalances ->
maybeBalances.mapError { TokenListError.DataError(it) }
}

View file

@ -11,7 +11,6 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
// FIXME: Refactor - [REDACTED_JIRA]
@ -35,7 +34,7 @@ internal class CurrenciesStatusesOperations(
val quotes = quotesRepository.getQuotesSync(currenciesIds, false).right()
val networkStatuses =
networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right()
val yieldBalances = getYieldBalancesSync()
val yieldBalances = getYieldBalancesSync(nonEmptyCurrencies)
return createCurrenciesStatuses(nonEmptyCurrencies, quotes, networkStatuses, yieldBalances)
},
@ -147,7 +146,7 @@ internal class CurrenciesStatusesOperations(
val currenciesFlow = combine(
getQuotes(currenciesIds),
getNetworksStatuses(networks),
getYieldBalances(),
getYieldBalances(nonEmptyCurrencies),
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses, maybeYieldBalances)
}
@ -385,25 +384,23 @@ internal class CurrenciesStatusesOperations(
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun getYieldBalances(): EitherFlow<Error, YieldBalanceList> {
return networksRepository.getNetworkAddressesFlow(userWalletId).flatMapLatest { addresses ->
stakingRepository.getMultiYieldBalanceFlow(
userWalletId = userWalletId,
addresses = addresses,
).map<YieldBalanceList, Either<Error, YieldBalanceList>> { it.right() }
.catch { emit(Error.DataError(it).left()) }
.onEmpty { emit(Error.EmptyYieldBalances.left()) }
}
private fun getYieldBalances(cryptoCurrencies: List<CryptoCurrency>): Flow<Either<Error, YieldBalanceList>> {
return stakingRepository.getMultiYieldBalanceFlow(
userWalletId = userWalletId,
cryptoCurrencies = cryptoCurrencies,
).map<YieldBalanceList, Either<Error, YieldBalanceList>> { it.right() }
.catch { emit(Error.DataError(it).left()) }
.onEmpty { emit(Error.EmptyYieldBalances.left()) }
}
private suspend fun getYieldBalancesSync(): Either<Error.EmptyYieldBalances, YieldBalanceList> {
private suspend fun getYieldBalancesSync(
cryptoCurrencies: List<CryptoCurrency>,
): Either<Error.EmptyYieldBalances, YieldBalanceList> {
return catch(
block = {
val networkAddresses = networksRepository.getNetworkAddresses(userWalletId)
stakingRepository.getMultiYieldBalanceSync(
userWalletId,
networkAddresses,
cryptoCurrencies,
).right()
},
catch = {
@ -417,10 +414,9 @@ internal class CurrenciesStatusesOperations(
): Either<Error.EmptyYieldBalances, YieldBalance> {
return catch(
block = {
val address = networksRepository.getNetworkAddress(userWalletId, cryptoCurrency)
stakingRepository.getSingleYieldBalanceSync(
userWalletId,
address,
cryptoCurrency,
).right()
},
catch = {
@ -429,19 +425,13 @@ internal class CurrenciesStatusesOperations(
)
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun getYieldBalance(cryptoCurrency: CryptoCurrency): EitherFlow<Error, YieldBalance> {
return networksRepository.getNetworkAddressFlow(
userWalletId,
cryptoCurrency,
).flatMapLatest { address ->
stakingRepository.getSingleYieldBalanceFlow(
userWalletId = userWalletId,
address = address,
).map<YieldBalance, Either<Error, YieldBalance>> { it.right() }
.catch { emit(Error.DataError(it).left()) }
.onEmpty { emit(Error.EmptyYieldBalances.left()) }
}
return stakingRepository.getSingleYieldBalanceFlow(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
).map<YieldBalance, Either<Error, YieldBalance>> { it.right() }
.catch { emit(Error.DataError(it).left()) }
.onEmpty { emit(Error.EmptyYieldBalances.left()) }
}
private fun getIds(

View file

@ -16,7 +16,6 @@ import com.tangem.domain.staking.model.stakekit.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.transaction.*
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
@ -118,7 +117,7 @@ class MockStakingRepository : StakingRepository {
override suspend fun fetchSingleYieldBalance(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
refresh: Boolean,
) {
/* no-op */
@ -126,19 +125,19 @@ class MockStakingRepository : StakingRepository {
override fun getSingleYieldBalanceFlow(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
): Flow<YieldBalance> = channelFlow {
send(YieldBalance.Error)
}
override suspend fun getSingleYieldBalanceSync(
userWalletId: UserWalletId,
address: CryptoCurrencyAddress,
cryptoCurrency: CryptoCurrency,
): YieldBalance = YieldBalance.Error
override suspend fun fetchMultiYieldBalance(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
refresh: Boolean,
) {
/* no-op */
@ -146,7 +145,7 @@ class MockStakingRepository : StakingRepository {
override fun getMultiYieldBalanceFlow(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): Flow<YieldBalanceList> = channelFlow {
send(
YieldBalanceList.Data(
@ -157,7 +156,7 @@ class MockStakingRepository : StakingRepository {
override fun getMultiYieldBalanceLce(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
send(
YieldBalanceList.Data(
@ -168,7 +167,7 @@ class MockStakingRepository : StakingRepository {
override suspend fun getMultiYieldBalanceSync(
userWalletId: UserWalletId,
addresses: List<CryptoCurrencyAddress>,
cryptoCurrencies: List<CryptoCurrency>,
): YieldBalanceList = YieldBalanceList.Data(
balances = listOf(YieldBalance.Error),
)

View file

@ -25,16 +25,19 @@ import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
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.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -54,8 +57,8 @@ import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
@ -80,12 +83,17 @@ internal class StakingViewModel @Inject constructor(
private val submitHashUseCase: SubmitHashUseCase,
private val isStakeMoreAvailableUseCase: IsStakeMoreAvailableUseCase,
private val stakingYieldBalanceUseCase: FetchStakingYieldBalanceUseCase,
private val updateDelayedNetworkStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase,
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase,
private val getAllowanceUseCase: GetAllowanceUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val isApproveNeededUseCase: IsApproveNeededUseCase,
private val clipboardManager: ClipboardManager,
private val vibratorHapticManager: VibratorHapticManager,
@DelayedWork private val coroutineScope: CoroutineScope,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
@ -575,7 +583,7 @@ internal class StakingViewModel @Inject constructor(
},
ifRight = { txHash ->
submitHash(transactionId, txHash)
updateStakeBalance()
scheduleUpdates()
val txUrl = getExplorerTransactionUrlUseCase(
txHash = txHash,
networkId = cryptoCurrencyStatus.currency.network.id,
@ -608,14 +616,55 @@ internal class StakingViewModel @Inject constructor(
}
}
private fun updateStakeBalance() {
viewModelScope.launch {
stakingYieldBalanceUseCase(
private fun scheduleUpdates() {
coroutineScope.launch {
listOf(
// we should update network to find pending tx after 1 sec
async {
fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrencyStatus.currency.network))
},
// we should update tx history and network for new balances
async {
updateStakeBalance()
},
async {
updateTxHistory()
},
async {
updateNetworkStatuses()
},
).awaitAll()
}
}
private suspend fun updateNetworkStatuses() {
updateDelayedNetworkStatusUseCase(
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
delayMillis = BALANCE_UPDATE_DELAY,
refresh = true,
)
}
private suspend fun updateStakeBalance() {
stakingYieldBalanceUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
refresh = true,
)
}
private suspend fun updateTxHistory() {
delay(BALANCE_UPDATE_DELAY)
val txHistoryItemsCountEither = getTxHistoryItemsCountUseCase(
userWalletId = userWalletId,
currency = cryptoCurrencyStatus.currency,
)
txHistoryItemsCountEither.onRight {
getTxHistoryItemsUseCase(
userWalletId = userWalletId,
address = CryptoCurrencyAddress(
cryptoCurrencyStatus.currency,
cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value.orEmpty(),
),
currency = cryptoCurrencyStatus.currency,
refresh = true,
)
}
@ -630,5 +679,6 @@ internal class StakingViewModel @Inject constructor(
private companion object {
const val WHAT_IS_STAKING_ARTICLE_URL = "TODO staking"
const val ALLOWANCE_UPDATE_DELAY = 10_000L
const val BALANCE_UPDATE_DELAY = 11_000L
}
}

View file

@ -223,19 +223,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
)
viewModelScope.launch(dispatchers.main) {
walletManagersFacade.getAddress(
walletManagersFacade.getDefaultAddress(
userWalletId = stateHolder.getSelectedWalletId(),
network = cryptoCurrencyStatus.currency.network,
)
.find { it.type == AddressType.Default }
?.value
?.let {
stateHolder.update(CloseBottomSheetTransformer(userWalletId = stateHolder.getSelectedWalletId()))
)?.let {
stateHolder.update(CloseBottomSheetTransformer(userWalletId = stateHolder.getSelectedWalletId()))
walletEventSender.send(
event = WalletEvent.CopyAddress(address = it),
)
}
walletEventSender.send(
event = WalletEvent.CopyAddress(address = it),
)
}
}
}