Updated on 2026-08-14
This commit is contained in:
parent
f39e22c82e
commit
bb32493ab0
13 changed files with 100 additions and 75 deletions
|
|
@ -74,18 +74,6 @@ internal object StakingDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingYieldBalanceUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetStakingYieldBalanceUseCase {
|
||||
return GetStakingYieldBalanceUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInitializeStakingProcessUseCase(
|
||||
|
|
|
|||
|
|
@ -29,22 +29,31 @@ internal class DefaultStakingBalanceStore(
|
|||
}
|
||||
}
|
||||
|
||||
override fun get(userWalletId: UserWalletId, integrationId: String): Flow<YieldBalanceWrapperDTO> {
|
||||
override fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalanceWrapperDTO> {
|
||||
return dataStore.get(userWalletId.stringValue)
|
||||
.mapNotNull { balances ->
|
||||
balances.firstOrNull { it.integrationId == integrationId }
|
||||
balances.firstOrNull { it.integrationId == integrationId && it.addresses.address == address }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId, integrationId: String): YieldBalanceWrapperDTO? {
|
||||
override suspend fun getSyncOrNull(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
): YieldBalanceWrapperDTO? {
|
||||
return dataStore.getSyncOrNull(userWalletId.stringValue)
|
||||
?.firstOrNull { it.integrationId == integrationId }
|
||||
?.firstOrNull { it.integrationId == integrationId && it.addresses.address == address }
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, integrationId: String, item: YieldBalanceWrapperDTO) {
|
||||
override suspend fun store(
|
||||
userWalletId: UserWalletId,
|
||||
integrationId: String,
|
||||
address: String,
|
||||
item: YieldBalanceWrapperDTO,
|
||||
) {
|
||||
mutex.withLock {
|
||||
val balances = dataStore.getSyncOrNull(userWalletId.stringValue)
|
||||
?.addOrReplace(item) { it.integrationId == integrationId }
|
||||
?.addOrReplace(item) { it.integrationId == integrationId && it.addresses.address == address }
|
||||
?: setOf(item)
|
||||
|
||||
dataStore.store(userWalletId.stringValue, balances)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ interface StakingBalanceStore {
|
|||
|
||||
suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>)
|
||||
|
||||
fun get(userWalletId: UserWalletId, integrationId: String): Flow<YieldBalanceWrapperDTO>
|
||||
fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalanceWrapperDTO>
|
||||
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId, integrationId: String): YieldBalanceWrapperDTO?
|
||||
suspend fun getSyncOrNull(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
): YieldBalanceWrapperDTO?
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, integrationId: String, item: YieldBalanceWrapperDTO)
|
||||
suspend fun store(userWalletId: UserWalletId, integrationId: String, address: String, item: YieldBalanceWrapperDTO)
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@ import com.tangem.domain.staking.model.stakekit.NetworkType
|
|||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
|
|
@ -310,6 +309,7 @@ internal class DefaultStakingRepository(
|
|||
stakingBalanceStore.store(
|
||||
userWalletId,
|
||||
requestBody.integrationId,
|
||||
address,
|
||||
YieldBalanceWrapperDTO(
|
||||
balances = result,
|
||||
integrationId = requestBody.integrationId,
|
||||
|
|
@ -328,9 +328,10 @@ internal class DefaultStakingRepository(
|
|||
send(YieldBalance.Empty)
|
||||
} else {
|
||||
launch(dispatchers.io) {
|
||||
val address = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network).orEmpty()
|
||||
val integrationId = integrationIdMap[getIntegrationKey(cryptoCurrency.id)]
|
||||
?: error("Could not get integrationId")
|
||||
stakingBalanceStore.get(userWalletId, integrationId)
|
||||
stakingBalanceStore.get(userWalletId, address, integrationId)
|
||||
.collectLatest {
|
||||
send(yieldBalanceConverter.convert(it))
|
||||
}
|
||||
|
|
@ -354,9 +355,12 @@ internal class DefaultStakingRepository(
|
|||
} else {
|
||||
fetchSingleYieldBalance(userWalletId, cryptoCurrency)
|
||||
|
||||
val address = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network).orEmpty()
|
||||
|
||||
val integrationId = integrationIdMap[getIntegrationKey(cryptoCurrency.id)]
|
||||
?: error("Could not get integrationId")
|
||||
val result = stakingBalanceStore.getSyncOrNull(userWalletId, integrationId)
|
||||
|
||||
val result = stakingBalanceStore.getSyncOrNull(userWalletId, address, integrationId)
|
||||
?: return@withContext YieldBalance.Error
|
||||
|
||||
yieldBalanceConverter.convert(result)
|
||||
|
|
@ -379,17 +383,19 @@ internal class DefaultStakingRepository(
|
|||
block = {
|
||||
val availableCurrencies = cryptoCurrencies
|
||||
.mapNotNull { currency ->
|
||||
val address = walletManagersFacade.getDefaultAddress(userWalletId, currency.network)
|
||||
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
|
||||
|
||||
if (integrationId != null && address != null) {
|
||||
address to integrationId
|
||||
if (integrationId != null) {
|
||||
addresses to integrationId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.distinctBy { it.second }
|
||||
.map { getBalanceRequestData(it.first, it.second) }
|
||||
.flatMap { (addresses, integrationId) ->
|
||||
addresses.map { address -> address to integrationId }
|
||||
}
|
||||
.map { getBalanceRequestData(it.first.value, it.second) }
|
||||
.ifEmpty { return@invokeOnExpire }
|
||||
val result = stakeKitApi.getMultipleYieldBalances(availableCurrencies).getOrThrow()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,13 @@ sealed class YieldBalanceList {
|
|||
data class Data(
|
||||
val balances: List<YieldBalance>,
|
||||
) : YieldBalanceList() {
|
||||
fun getBalance(rawCurrencyId: String?): YieldBalance {
|
||||
return balances.firstOrNull { yield ->
|
||||
(yield as? YieldBalance.Data)?.balance?.items
|
||||
?.any { rawCurrencyId == it.rawCurrencyId } == true
|
||||
|
||||
fun getBalance(address: String?, rawCurrencyId: String?): YieldBalance {
|
||||
return balances.firstOrNull { yieldBalance ->
|
||||
val data = yieldBalance as? YieldBalance.Data
|
||||
data?.balance?.items?.any {
|
||||
address == data.address && rawCurrencyId == it.rawCurrencyId
|
||||
} == true
|
||||
} ?: YieldBalance.Error
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.core.utils.EitherFlow
|
||||
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.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GetStakingYieldBalanceUseCase(
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): EitherFlow<StakingError, YieldBalance> {
|
||||
return stakingRepository.getSingleYieldBalanceFlow(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
).map<YieldBalance, Either<StakingError, YieldBalance>> { it.right() }
|
||||
.catch { emit(stakingErrorResolver.resolve(it).left()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ class GetCurrencyStatusUpdatesUseCase(
|
|||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
return flow {
|
||||
emitAll(
|
||||
getCurrency(
|
||||
getCurrencyStatus(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = currencyId,
|
||||
isSingleWalletWithTokens = isSingleWalletWithTokens,
|
||||
|
|
@ -53,7 +53,7 @@ class GetCurrencyStatusUpdatesUseCase(
|
|||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private suspend fun getCurrency(
|
||||
private suspend fun getCurrencyStatus(
|
||||
userWalletId: UserWalletId,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
isSingleWalletWithTokens: Boolean,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class GetNodlTokenListUseCase(
|
|||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getNodlCurrencyStatusesFlow()
|
||||
return operations.getCardCurrenciesStatusesFlow()
|
||||
.map { maybeCurrenciesStatuses ->
|
||||
maybeCurrenciesStatuses.mapLeft(CurrenciesStatusesOperations.Error::mapToTokenListError)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.domain.tokens.model.TokenList
|
|||
|
||||
sealed class TokenListError {
|
||||
|
||||
object EmptyTokens : TokenListError()
|
||||
data object EmptyTokens : TokenListError()
|
||||
|
||||
data class UnableToSortTokenList(val unsortedTokenList: TokenList.Ungrouped) : TokenListError()
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ internal fun CurrenciesStatusesOperations.Error.mapToCurrencyError(): CurrencySt
|
|||
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
is CurrenciesStatusesOperations.Error.EmptyAddresses,
|
||||
is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus,
|
||||
-> CurrencyStatusError.UnableToCreateCurrency
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ internal fun CurrenciesStatusesOperations.Error.mapToTokenListError(): TokenList
|
|||
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
is CurrenciesStatusesOperations.Error.EmptyAddresses,
|
||||
is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus,
|
||||
is CurrenciesStatusesOperations.Error.EmptyYieldBalances,
|
||||
-> TokenListError.EmptyTokens
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ internal class CurrenciesStatusesLceOperations(
|
|||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(userWalletId, networks),
|
||||
getYieldBalances(userWalletId, nonEmptyCurrencies),
|
||||
|
||||
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
|
||||
val statuses = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
|
|
@ -144,11 +145,15 @@ internal class CurrenciesStatusesLceOperations(
|
|||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
val address = extractAddress(networkStatus)
|
||||
val isStakingSupported = stakingRepository.isStakingSupported(
|
||||
stakingRepository.getIntegrationKey(currency.id),
|
||||
)
|
||||
val yieldBalance = if (isStakingSupported) {
|
||||
(yieldBalances as? YieldBalanceList.Data)?.getBalance(currency.id.rawCurrencyId)
|
||||
(yieldBalances as? YieldBalanceList.Data)?.getBalance(
|
||||
address = address,
|
||||
rawCurrencyId = currency.id.rawCurrencyId,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
@ -221,4 +226,13 @@ internal class CurrenciesStatusesLceOperations(
|
|||
|
||||
return networks to currenciesIds
|
||||
}
|
||||
|
||||
private fun extractAddress(networkStatus: NetworkStatus?): String? {
|
||||
return when (val value = networkStatus?.value) {
|
||||
is NetworkStatus.NoAccount -> value.address.defaultAddress.value
|
||||
is NetworkStatus.Unreachable -> value.address?.defaultAddress?.value
|
||||
is NetworkStatus.Verified -> value.address.defaultAddress.value
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,12 @@ internal class CurrenciesStatusesOperations(
|
|||
networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right()
|
||||
val yieldBalances = getYieldBalancesSync(nonEmptyCurrencies)
|
||||
|
||||
return createCurrenciesStatuses(nonEmptyCurrencies, quotes, networkStatuses, yieldBalances)
|
||||
return createCurrenciesStatuses(
|
||||
nonEmptyCurrencies,
|
||||
quotes,
|
||||
networkStatuses,
|
||||
yieldBalances,
|
||||
)
|
||||
},
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
)
|
||||
|
|
@ -115,8 +120,7 @@ internal class CurrenciesStatusesOperations(
|
|||
return createCurrencyStatus(currency, quotes, networkStatus, yieldBalances)
|
||||
}
|
||||
|
||||
/** Get flow of currency statuses for NODL card */
|
||||
fun getNodlCurrencyStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
|
||||
fun getCardCurrenciesStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
|
||||
return flow {
|
||||
val nonEmptyCurrencies = recover(
|
||||
block = { getCurrenciesFromCard(userWalletId) },
|
||||
|
|
@ -147,12 +151,13 @@ internal class CurrenciesStatusesOperations(
|
|||
val currenciesFlow = combine(
|
||||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(networks),
|
||||
) { maybeQuotes, maybeNetworksStatuses ->
|
||||
getYieldBalances(nonEmptyCurrencies),
|
||||
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
|
||||
createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeQuotes = maybeQuotes,
|
||||
maybeNetworkStatuses = maybeNetworksStatuses,
|
||||
maybeYieldBalances = null,
|
||||
maybeYieldBalances = maybeYieldBalances,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -265,11 +270,16 @@ internal class CurrenciesStatusesOperations(
|
|||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
val address = extractAddress(networkStatus)
|
||||
|
||||
val isStakingSupported = stakingRepository.isStakingSupported(
|
||||
stakingRepository.getIntegrationKey(currency.id),
|
||||
)
|
||||
val yieldBalance = if (isStakingSupported) {
|
||||
(yieldBalances as? YieldBalanceList.Data)?.getBalance(currency.id.rawCurrencyId)
|
||||
(yieldBalances as? YieldBalanceList.Data)?.getBalance(
|
||||
address = address,
|
||||
rawCurrencyId = currency.id.rawCurrencyId,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
@ -393,6 +403,15 @@ internal class CurrenciesStatusesOperations(
|
|||
.onEmpty { emit(Error.EmptyNetworksStatuses.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(
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): Either<Error.EmptyYieldBalances, YieldBalanceList> {
|
||||
|
|
@ -449,6 +468,15 @@ internal class CurrenciesStatusesOperations(
|
|||
return networks to currenciesIds
|
||||
}
|
||||
|
||||
private fun extractAddress(networkStatus: NetworkStatus?): String? {
|
||||
return when (val value = networkStatus?.value) {
|
||||
is NetworkStatus.NoAccount -> value.address.defaultAddress.value
|
||||
is NetworkStatus.Unreachable -> value.address?.defaultAddress?.value
|
||||
is NetworkStatus.Verified -> value.address.defaultAddress.value
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Error {
|
||||
|
||||
data object EmptyCurrencies : Error()
|
||||
|
|
@ -457,6 +485,8 @@ internal class CurrenciesStatusesOperations(
|
|||
|
||||
data object EmptyNetworksStatuses : Error()
|
||||
|
||||
data object EmptyAddresses : Error()
|
||||
|
||||
data object UnableToCreateCurrencyStatus : Error()
|
||||
|
||||
data class DataError(val cause: Throwable) : Error()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue