Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-23 20:01:15 +03:00
commit cab30e9431
186 changed files with 3729 additions and 2030 deletions

View file

@ -2,7 +2,6 @@ package com.tangem.data.staking
import android.util.Base64
import arrow.core.getOrElse
import arrow.core.raise.catch
import com.squareup.moshi.Moshi
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
@ -30,8 +29,6 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrap
import com.tangem.datasource.api.stakekit.models.response.model.transaction.tron.TronStakeKitTransaction
import com.tangem.datasource.local.token.StakingBalanceStore
import com.tangem.datasource.local.token.StakingYieldsStore
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.lce.lceFlow
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
@ -57,6 +54,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -103,10 +101,6 @@ internal class DefaultStakingRepository(
private val yieldBalanceConverter = YieldBalanceConverter()
private val yieldBalanceListConverter = YieldBalanceListConverter(yieldBalanceConverter)
private val isYieldBalanceFetching = MutableStateFlow(
value = emptyMap<UserWalletId, Boolean>(),
)
private val tronStakeKitTransactionAdapter by lazy { moshi.adapter(TronStakeKitTransaction::class.java) }
override fun getIntegrationKey(cryptoCurrencyId: CryptoCurrency.ID): String = with(cryptoCurrencyId) {
@ -126,7 +120,7 @@ internal class DefaultStakingRepository(
val stakingTokensWithYields = stakeKitApi.getEnabledYields(preferredValidatorsOnly = false)
.getOrThrow()
stakingYieldsStore.store(stakingTokensWithYields.data.filter { it.isAvailable })
stakingYieldsStore.store(stakingTokensWithYields.data.filter { it.isAvailable ?: false })
},
)
}
@ -151,7 +145,7 @@ internal class DefaultStakingRepository(
val yield = getYield(cryptoCurrencyId, symbol)
StakingEntryInfo(
apr = requireNotNull(yield.validators.maxByOrNull { it.apr.orZero() }?.apr),
apr = requireNotNull(yield.preferredValidators.maxByOrNull { it.apr.orZero() }?.apr),
rewardSchedule = yield.metadata.rewardSchedule,
tokenSymbol = yield.token.symbol,
)
@ -207,23 +201,21 @@ internal class DefaultStakingRepository(
): StakingAction {
return withContext(dispatchers.io) {
val response = when (params.actionCommonType) {
StakingActionCommonType.ENTER -> stakeKitApi.createEnterAction(
StakingActionCommonType.Enter -> stakeKitApi.createEnterAction(
createActionRequestBody(
userWalletId,
network,
params,
),
)
StakingActionCommonType.EXIT -> stakeKitApi.createExitAction(
StakingActionCommonType.Exit -> stakeKitApi.createExitAction(
createActionRequestBody(
userWalletId,
network,
params,
),
)
StakingActionCommonType.PENDING_OTHER,
StakingActionCommonType.PENDING_REWARDS,
-> stakeKitApi.createPendingAction(
is StakingActionCommonType.Pending -> stakeKitApi.createPendingAction(
createPendingActionRequestBody(params),
)
}
@ -239,23 +231,21 @@ internal class DefaultStakingRepository(
): StakingGasEstimate {
return withContext(dispatchers.io) {
val gasEstimateDTO = when (params.actionCommonType) {
StakingActionCommonType.ENTER -> stakeKitApi.estimateGasOnEnter(
StakingActionCommonType.Enter -> stakeKitApi.estimateGasOnEnter(
createActionRequestBody(
userWalletId,
network,
params,
),
)
StakingActionCommonType.EXIT -> stakeKitApi.estimateGasOnExit(
StakingActionCommonType.Exit -> stakeKitApi.estimateGasOnExit(
createActionRequestBody(
userWalletId,
network,
params,
),
)
StakingActionCommonType.PENDING_REWARDS,
StakingActionCommonType.PENDING_OTHER,
-> stakeKitApi.estimateGasOnPending(
is StakingActionCommonType.Pending -> stakeKitApi.estimateGasOnPending(
createPendingActionRequestBody(params),
)
}
@ -389,88 +379,66 @@ internal class DefaultStakingRepository(
refresh: Boolean,
) = withContext(dispatchers.io) {
if (!stakingFeatureToggle.isStakingEnabled) return@withContext
try {
isYieldBalanceFetching.update {
it + (userWalletId to true)
}
cacheRegistry.invokeOnExpire(
key = getYieldBalancesKey(userWalletId),
skipCache = refresh,
block = {
val yields = getEnabledYields()
val availableCurrencies = cryptoCurrencies
.mapNotNull { currency ->
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
if (integrationId != null && yields.any { it.id == integrationId }) {
addresses to integrationId
} else {
null
}
}
.flatMap { (addresses, integrationId) ->
addresses.map { address -> address to integrationId }
}
.map { getBalanceRequestData(it.first.value, it.second) }
.ifEmpty {
cacheRegistry.invalidate(getYieldBalancesKey(userWalletId))
error("No addresses found")
}
val result = stakeKitApi.getMultipleYieldBalances(availableCurrencies).getOrThrow()
cacheRegistry.invokeOnExpire(
key = getYieldBalancesKey(userWalletId),
skipCache = refresh,
block = {
val yields = getEnabledYields().ifEmpty {
Timber.i("No enabled yields for $userWalletId")
stakingBalanceStore.store(userWalletId, emptySet())
stakingBalanceStore.store(userWalletId, result)
},
)
} finally {
isYieldBalanceFetching.update {
it - userWalletId
}
}
return@invokeOnExpire
}
val availableCurrencies = cryptoCurrencies
.mapNotNull { currency ->
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
if (integrationId != null && yields.any { it.id == integrationId }) {
addresses to integrationId
} else {
null
}
}
.flatMap { (addresses, integrationId) ->
addresses.map { address -> address to integrationId }
}
.map { getBalanceRequestData(it.first.value, it.second) }
.ifEmpty {
Timber.i("No yield balances available for $userWalletId")
stakingBalanceStore.store(userWalletId, emptySet())
cacheRegistry.invalidate(getYieldBalancesKey(userWalletId))
return@invokeOnExpire
}
val result = stakeKitApi
.getMultipleYieldBalances(availableCurrencies)
.getOrThrow()
stakingBalanceStore.store(userWalletId, result)
},
)
}
override fun getMultiYieldBalanceFlow(
override fun getMultiYieldBalanceUpdates(
userWalletId: UserWalletId,
cryptoCurrencies: List<CryptoCurrency>,
): Flow<YieldBalanceList> = channelFlow {
if (!stakingFeatureToggle.isStakingEnabled) {
send(YieldBalanceList.Empty)
} else {
launch(dispatchers.io) {
stakingBalanceStore.get(userWalletId)
.collectLatest { send(yieldBalanceListConverter.convert(it)) }
}
stakingBalanceStore.get(userWalletId)
.onEach {
val balances = yieldBalanceListConverter.convert(it)
send(balances)
}
.launchIn(scope = this + dispatchers.io)
withContext(dispatchers.io) {
fetchMultiYieldBalance(
userWalletId,
cryptoCurrencies,
)
}
}
}.cancellable()
override fun getMultiYieldBalanceLce(
userWalletId: UserWalletId,
cryptoCurrencies: List<CryptoCurrency>,
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
if (!stakingFeatureToggle.isStakingEnabled) {
send(YieldBalanceList.Empty)
} else {
launch(dispatchers.io) {
combine(
stakingBalanceStore.get(userWalletId),
isYieldBalanceFetching.map { it.getOrElse(userWalletId) { false } },
) { result, isFetching ->
val balances = yieldBalanceListConverter.convert(result)
send(balances, isStillLoading = isFetching)
}.collect()
}
withContext(dispatchers.io) {
catch(
block = { fetchMultiYieldBalance(userWalletId, cryptoCurrencies, refresh = false) },
catch = { raise(it) },
)
fetchMultiYieldBalance(userWalletId, cryptoCurrencies, refresh = false)
}
}
}
@ -583,9 +551,10 @@ internal class DefaultStakingRepository(
}
private fun getEnabledYields(): List<Yield> {
return stakingYieldsStore
.get()
.map { yieldConverter.convert(it) }
return yieldConverter.convertListIgnoreErrors(
input = stakingYieldsStore.get(),
onError = { Timber.e("Error converting enabled yields list: $it") },
)
}
private fun getBalanceRequestData(address: String, integrationId: String): YieldBalanceRequestBody {

View file

@ -17,37 +17,37 @@ class YieldConverter(
override fun convert(value: YieldDTO): Yield {
return Yield(
id = value.id,
token = tokenConverter.convert(value.token),
tokens = value.tokens.map { tokenConverter.convert(it) },
args = convertArgs(value.args),
status = convertStatus(value.status),
apy = value.apy,
rewardRate = value.rewardRate,
rewardType = convertRewardType(value.rewardType),
metadata = convertMetadata(value.metadata),
validators = value.validators
id = value.id.asMandatory("id"),
token = tokenConverter.convert(value.token.asMandatory("token")),
tokens = value.tokens.asMandatory("tokens").map { tokenConverter.convert(it) },
args = convertArgs(value.args.asMandatory("args")),
status = convertStatus(value.status.asMandatory("status")),
apy = value.apy.asMandatory("apy"),
rewardRate = value.rewardRate.asMandatory("rewardRate"),
rewardType = convertRewardType(value.rewardType.asMandatory("rewardType")),
metadata = convertMetadata(value.metadata.asMandatory("metadata")),
validators = value.validators.asMandatory("validators")
.asSequence()
.filter { it.status == ValidatorStatusDTO.ACTIVE }
.map { convertValidator(it) }
.sortedByDescending { it.isStrategicPartner }
.sortedByDescending { it.apr }
.toImmutableList(),
isAvailable = value.isAvailable,
isAvailable = value.isAvailable.asMandatory("isAvailable"),
)
}
private fun convertArgs(argsDTO: YieldDTO.ArgsDTO): Yield.Args {
return Yield.Args(
enter = convertEnter(argsDTO.enter),
enter = convertEnter(argsDTO.enter.asMandatory("enter")),
exit = argsDTO.exit?.let { convertEnter(it) },
)
}
private fun convertEnter(enterDTO: YieldDTO.ArgsDTO.Enter): Yield.Args.Enter {
return Yield.Args.Enter(
addresses = convertAddresses(enterDTO.addresses),
args = enterDTO.args
addresses = convertAddresses(enterDTO.addresses.asMandatory("addresses")),
args = enterDTO.args.asMandatory("args")
.mapKeys { convertArgType(it.key) }
.mapValues { convertAddressArgument(it.value) },
)
@ -55,7 +55,7 @@ class YieldConverter(
private fun convertAddresses(addressesDTO: YieldDTO.ArgsDTO.Enter.Addresses): Yield.Args.Enter.Addresses {
return Yield.Args.Enter.Addresses(
address = convertAddressArgument(addressesDTO.address),
address = convertAddressArgument(addressesDTO.address.asMandatory("address")),
additionalAddresses = addressesDTO.additionalAddresses
?.mapKeys { convertArgType(it.key) }
?.mapValues { convertAddressArgument(it.value) },
@ -73,48 +73,51 @@ class YieldConverter(
private fun convertStatus(statusDTO: YieldDTO.StatusDTO): Yield.Status {
return Yield.Status(
enter = statusDTO.enter,
enter = statusDTO.enter.asMandatory("enter"),
exit = statusDTO.exit,
)
}
private fun convertMetadata(metadataDTO: YieldDTO.MetadataDTO): Yield.Metadata {
return Yield.Metadata(
name = metadataDTO.name,
logoUri = metadataDTO.logoUri,
description = metadataDTO.description,
name = metadataDTO.name.asMandatory("name"),
logoUri = metadataDTO.logoUri.asMandatory("logoUri"),
description = metadataDTO.description.asMandatory("description"),
documentation = metadataDTO.documentation,
gasFeeToken = tokenConverter.convert(metadataDTO.gasFeeTokenDTO),
token = tokenConverter.convert(metadataDTO.tokenDTO),
tokens = metadataDTO.tokensDTO.map { tokenConverter.convert(it) },
type = metadataDTO.type,
rewardSchedule = convertRewardSchedule(metadataDTO.rewardSchedule),
gasFeeToken = tokenConverter.convert(metadataDTO.gasFeeTokenDTO.asMandatory("gasFeeTokenDTO")),
token = tokenConverter.convert(metadataDTO.tokenDTO.asMandatory("tokenDTO")),
tokens = metadataDTO.tokensDTO.asMandatory("tokensDTO").map { tokenConverter.convert(it) },
type = metadataDTO.type.asMandatory("type"),
rewardSchedule = convertRewardSchedule(metadataDTO.rewardSchedule.asMandatory("rewardSchedule")),
cooldownPeriod = metadataDTO.cooldownPeriod?.let { convertPeriod(it) },
warmupPeriod = convertPeriod(metadataDTO.warmupPeriod),
rewardClaiming = convertRewardClaiming(metadataDTO.rewardClaiming),
warmupPeriod = convertPeriod(metadataDTO.warmupPeriod.asMandatory("warmupPeriod")),
rewardClaiming = convertRewardClaiming(metadataDTO.rewardClaiming.asMandatory("rewardClaiming")),
defaultValidator = metadataDTO.defaultValidator,
minimumStake = metadataDTO.minimumStake,
supportsMultipleValidators = metadataDTO.supportsMultipleValidators,
revshare = convertEnabled(metadataDTO.revshare),
fee = convertEnabled(metadataDTO.fee),
supportsMultipleValidators = metadataDTO.supportsMultipleValidators.asMandatory(
"supportsMultipleValidators",
),
revshare = convertEnabled(metadataDTO.revshare.asMandatory("revshare")),
fee = convertEnabled(metadataDTO.fee.asMandatory("fee")),
)
}
private fun convertPeriod(periodDTO: YieldDTO.MetadataDTO.PeriodDTO): Yield.Metadata.Period {
return Yield.Metadata.Period(
days = periodDTO.days,
days = periodDTO.days.asMandatory("days"),
)
}
private fun convertEnabled(enabledDTO: YieldDTO.MetadataDTO.EnabledDTO): Yield.Metadata.Enabled {
return Yield.Metadata.Enabled(
enabled = enabledDTO.enabled,
enabled = enabledDTO.enabled.asMandatory("enabled"),
)
}
private fun convertValidator(validatorDTO: YieldDTO.ValidatorDTO): Yield.Validator {
val address = validatorDTO.address.asMandatory("address")
return Yield.Validator(
address = validatorDTO.address,
address = address,
status = convertValidatorStatus(validatorDTO.status),
name = validatorDTO.name,
image = validatorDTO.image,
@ -124,7 +127,7 @@ class YieldConverter(
stakedBalance = validatorDTO.stakedBalance,
votingPower = validatorDTO.votingPower,
preferred = validatorDTO.preferred,
isStrategicPartner = isStrategicPartner(validatorDTO.address),
isStrategicPartner = isStrategicPartner(address),
)
}

View file

@ -1,6 +1,5 @@
package com.tangem.data.tokens.repository
import arrow.core.raise.catch
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.compatibility.getL2CompatibilityTokenComparison
import com.tangem.blockchainsdk.utils.toCoinId
@ -27,8 +26,6 @@ import com.tangem.datasource.local.preferences.utils.storeObject
import com.tangem.datasource.local.token.ExpressAssetsStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.core.error.DataError
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.lce.lceFlow
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -42,6 +39,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import timber.log.Timber
import com.tangem.blockchain.common.FeePaidCurrency as FeePaidSdkCurrency
@ -66,10 +64,6 @@ internal class DefaultCurrenciesRepository(
private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility()
private val customTokensMerger = CustomTokensMerger(tangemTechApi, dispatchers)
private val isMultiCurrencyWalletCurrenciesFetching = MutableStateFlow(
value = emptyMap<UserWalletId, Boolean>(),
)
override suspend fun saveTokens(
userWalletId: UserWalletId,
currencies: List<CryptoCurrency>,
@ -206,18 +200,14 @@ internal class DefaultCurrenciesRepository(
}
}
override fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): LceFlow<Throwable, List<CryptoCurrency>> {
return lceFlow {
val userWallet = catch({ getUserWallet(userWalletId) }) {
raise(it)
}
override fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>> {
return channelFlow {
val userWallet = getUserWallet(userWalletId)
if (userWallet.isMultiCurrency) {
getMultiCurrencyWalletCurrenciesUpdatesLce(userWalletId).collect(::send)
getMultiCurrencyWalletCurrenciesUpdates(userWalletId).collect(::send)
} else {
val currency = catch({ getSingleCurrencyWalletPrimaryCurrency(userWalletId) }) {
raise(it)
}
val currency = getSingleCurrencyWalletPrimaryCurrency(userWalletId)
send(listOf(currency))
}
}
@ -260,40 +250,14 @@ internal class DefaultCurrenciesRepository(
val userWallet = getUserWallet(userWalletId)
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
launch(dispatchers.io) {
getMultiCurrencyWalletCurrencies(userWallet)
.collectLatest(::send)
}
getMultiCurrencyWalletCurrencies(userWallet)
.onEach { send(it) }
.launchIn(scope = this + dispatchers.io)
withContext(dispatchers.io) {
fetchTokensIfCacheExpired(userWallet, refresh = false)
}
}
.cancellable()
}
override fun getMultiCurrencyWalletCurrenciesUpdatesLce(
userWalletId: UserWalletId,
): LceFlow<Throwable, List<CryptoCurrency>> = lceFlow {
val userWallet = getUserWallet(userWalletId)
catch({ ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) }) {
raise(it)
}
launch(dispatchers.io) {
combine(
getMultiCurrencyWalletCurrencies(userWallet),
isMultiCurrencyWalletCurrenciesFetching.map { it.getOrElse(userWallet.walletId) { false } },
) { currencies, isFetching ->
send(currencies, isStillLoading = isFetching)
}.collect()
}
withContext(dispatchers.io) {
catch({ fetchTokensIfCacheExpired(userWallet, refresh = false) }) {
raise(it)
}
}
}
override suspend fun getMultiCurrencyWalletCurrenciesSync(
@ -545,19 +509,7 @@ internal class DefaultCurrenciesRepository(
cacheRegistry.invokeOnExpire(
key = getTokensCacheKey(userWallet.walletId),
skipCache = refresh,
block = {
isMultiCurrencyWalletCurrenciesFetching.update {
it + (userWallet.walletId to true)
}
try {
fetchTokens(userWallet)
} finally {
isMultiCurrencyWalletCurrenciesFetching.update {
it - userWallet.walletId
}
}
},
block = { fetchTokens(userWallet) },
)
}

View file

@ -1,6 +1,5 @@
package com.tangem.data.tokens.repository
import arrow.core.raise.catch
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType
import com.tangem.blockchainsdk.utils.fromNetworkId
@ -15,8 +14,6 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.lce.lceFlow
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
@ -28,7 +25,10 @@ import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
@Suppress("LongParameterList")
@ -46,42 +46,16 @@ internal class DefaultNetworksRepository(
private val responseCurrenciesFactory by lazy { ResponseCryptoCurrenciesFactory() }
private val networkStatusFactory by lazy { NetworkStatusFactory() }
private val isNetworkStatusesFetching = MutableStateFlow(
value = emptyMap<UserWalletId, Boolean>(),
)
override fun getNetworkStatusesUpdates(
userWalletId: UserWalletId,
networks: Set<Network>,
): Flow<Set<NetworkStatus>> = channelFlow {
launch(dispatchers.io) {
networksStatusesStore.get(userWalletId)
.collectLatest(::send)
}
networksStatusesStore.get(userWalletId)
.onEach(::send)
.launchIn(scope = this + dispatchers.io)
withContext(dispatchers.io) {
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, false)
}
}
.cancellable()
override fun getNetworkStatusesUpdatesLce(
userWalletId: UserWalletId,
networks: Set<Network>,
): LceFlow<Throwable, Set<NetworkStatus>> = lceFlow {
launch(dispatchers.io) {
combine(
networksStatusesStore.get(userWalletId),
isNetworkStatusesFetching.map { it.getOrElse(userWalletId) { false } },
) { statuses, isFetching ->
send(statuses, isStillLoading = isFetching)
}.collect()
}
withContext(dispatchers.io) {
catch({ fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh = false) }) {
raise(it)
}
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh = false)
}
}
@ -127,83 +101,36 @@ internal class DefaultNetworksRepository(
}
}
override suspend fun getNetworkAddress(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): CryptoCurrencyAddress = withContext(dispatchers.io) {
CryptoCurrencyAddress(
cryptoCurrency = currency,
address = walletManagersFacade.getAddresses(userWalletId, currency.network)
.firstOrNull { it.type == AddressType.Default }
?.value.orEmpty(),
)
}
override fun getNetworkAddressFlow(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): Flow<CryptoCurrencyAddress> = channelFlow {
launch(dispatchers.io) {
send(getNetworkAddress(userWalletId, currency))
}
}
override suspend fun getNetworkAddresses(userWalletId: UserWalletId): List<CryptoCurrencyAddress> =
withContext(dispatchers.io) {
// Get list of currencies matching [network]
val currencies = getCurrencies(userWalletId)
// There is no currencies matching given [networks] in [userWalletId]
if (currencies.toList().isEmpty()) return@withContext emptyList()
currencies.toList().map { currency ->
CryptoCurrencyAddress(
cryptoCurrency = currency,
address = walletManagersFacade.getAddresses(userWalletId, currency.network)
.firstOrNull { it.type == AddressType.Default }
?.value.orEmpty(),
)
}
}
override fun getNetworkAddressesFlow(
userWalletId: UserWalletId,
network: Network,
): Flow<List<CryptoCurrencyAddress>> = channelFlow {
launch(dispatchers.io) {
send(getNetworkAddresses(userWalletId, network))
}
}
override fun getNetworkAddressesFlow(userWalletId: UserWalletId): Flow<List<CryptoCurrencyAddress>> = channelFlow {
launch(dispatchers.io) {
send(getNetworkAddresses(userWalletId))
}
}
private suspend fun fetchNetworksStatusesIfCacheExpired(
userWalletId: UserWalletId,
networks: Set<Network>,
refresh: Boolean,
) {
val currencies = getCurrencies(userWalletId, networks)
val networksDeferred = networks.mapNotNull { network ->
fetchNetworkStatusIfCacheExpired(userWalletId, network, currencies, refresh)
) = coroutineScope {
if (refresh) {
val statusesToRefresh = networks.map { NetworkStatus(it, NetworkStatus.Refreshing) }
networksStatusesStore.storeAll(userWalletId, statusesToRefresh)
}
if (networksDeferred.isNotEmpty()) {
try {
isNetworkStatusesFetching.update {
it + (userWalletId to true)
}
val currencies = getCurrencies(userWalletId, networks)
val networksDeferred = networks.mapNotNull { network ->
coroutineScope {
val key = getNetworksStatusesCacheKey(userWalletId, network)
networksDeferred.awaitAll()
} finally {
isNetworkStatusesFetching.update {
it - userWalletId
if (refresh || cacheRegistry.isExpired(key)) {
async {
cacheRegistry.invokeOnExpire(
key = key,
skipCache = refresh,
block = { fetchNetworkStatus(userWalletId, network, currencies) },
)
}
} else {
null
}
}
}
networksDeferred.awaitAll()
}
private suspend fun fetchNetworksPendingTransactions(
@ -222,26 +149,6 @@ internal class DefaultNetworksRepository(
}
}
private suspend fun fetchNetworkStatusIfCacheExpired(
userWalletId: UserWalletId,
network: Network,
currencies: Sequence<CryptoCurrency>,
refresh: Boolean,
): Deferred<Unit>? = coroutineScope {
val key = getNetworksStatusesCacheKey(userWalletId, network)
if (refresh || cacheRegistry.isExpired(key)) {
async {
cacheRegistry.invokeOnExpire(
key = key,
skipCache = refresh,
block = { fetchNetworkStatus(userWalletId, network, currencies) },
)
}
} else {
null
}
}
private suspend fun fetchNetworkStatus(
userWalletId: UserWalletId,
network: Network,