Updated on 2026-08-14
This commit is contained in:
parent
20dc8c9d90
commit
fb34b46df6
54 changed files with 909 additions and 208 deletions
|
|
@ -55,8 +55,14 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
): GetTokenListUseCase {
|
||||
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository)
|
||||
return GetTokenListUseCase(
|
||||
currenciesRepository,
|
||||
quotesRepository,
|
||||
networksRepository,
|
||||
stakingRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -65,8 +71,9 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
): GetCardTokensListUseCase {
|
||||
return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository)
|
||||
return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository, stakingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -84,9 +91,16 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCurrencyStatusUpdatesUseCase {
|
||||
return GetCurrencyStatusUpdatesUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
|
||||
return GetCurrencyStatusUpdatesUseCase(
|
||||
currenciesRepository,
|
||||
quotesRepository,
|
||||
networksRepository,
|
||||
stakingRepository,
|
||||
dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -101,6 +115,7 @@ internal object TokensDomainModule {
|
|||
currencyChecksRepository: CurrencyChecksRepository,
|
||||
showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
|
||||
promoRepository: PromoRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCurrencyWarningsUseCase {
|
||||
return GetCurrencyWarningsUseCase(
|
||||
|
|
@ -113,6 +128,7 @@ internal object TokensDomainModule {
|
|||
swapRepository = swapRepository,
|
||||
showSwapPromoTokenUseCase = showSwapPromoTokenUseCase,
|
||||
promoRepository = promoRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -123,12 +139,14 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetPrimaryCurrencyStatusUpdatesUseCase {
|
||||
return GetPrimaryCurrencyStatusUpdatesUseCase(
|
||||
currenciesRepository,
|
||||
quotesRepository,
|
||||
networksRepository,
|
||||
stakingRepository,
|
||||
dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -149,8 +167,9 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
): FetchCardTokenListUseCase {
|
||||
return FetchCardTokenListUseCase(currenciesRepository, networksRepository, quotesRepository)
|
||||
return FetchCardTokenListUseCase(currenciesRepository, networksRepository, quotesRepository, stakingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -212,12 +231,14 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetNetworkCoinStatusUseCase {
|
||||
return GetNetworkCoinStatusUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -228,12 +249,14 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetFeePaidCryptoCurrencyStatusSyncUseCase {
|
||||
return GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -366,11 +389,13 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
): GetWalletTotalBalanceUseCase {
|
||||
return GetWalletTotalBalanceUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.squareup.moshi.Json
|
|||
data class YieldBalanceRequestBody(
|
||||
@Json(name = "addresses") val addresses: Address,
|
||||
@Json(name = "args") val args: YieldBalanceRequestArgs,
|
||||
@Json(name = "integrationId") val integrationId: String? = null,
|
||||
@Json(name = "integrationId") val integrationId: String,
|
||||
) {
|
||||
|
||||
data class YieldBalanceRequestArgs(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.utils.extensions
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Converts `BigDecimal?` to `BigDecimal`
|
||||
*
|
||||
* If `BigDecimal?` is `null`, returns `BigDecimal.ZERO`
|
||||
*/
|
||||
fun BigDecimal?.orZero(): BigDecimal = this ?: BigDecimal.ZERO
|
||||
|
|
@ -24,6 +24,8 @@ dependencies {
|
|||
implementation(projects.domain.staking)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
/** Feature Api modules */
|
||||
implementation(projects.features.staking.api)
|
||||
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import arrow.core.raise.catch
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.staking.converters.StakingNetworkTypeConverter
|
||||
|
|
@ -29,27 +30,33 @@ import com.tangem.data.staking.converters.*
|
|||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.staking.model.*
|
||||
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.wallets.models.UserWalletId
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.toFormattedString
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("LargeClass")
|
||||
internal class DefaultStakingRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val stakingBalanceStore: StakingBalanceStore,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val stakingFeatureToggle: StakingFeatureToggles,
|
||||
) : StakingRepository {
|
||||
|
||||
private val stakingNetworkTypeConverter = StakingNetworkTypeConverter()
|
||||
|
|
@ -83,15 +90,24 @@ internal class DefaultStakingRepository(
|
|||
|
||||
private val yieldBalanceListConverter = YieldBalanceListConverter()
|
||||
|
||||
private val isYieldBalanceFetching = MutableStateFlow(
|
||||
value = emptyMap<UserWalletId, Boolean>(),
|
||||
)
|
||||
|
||||
override fun isStakingSupported(currencyId: String): Boolean {
|
||||
return integrationIds.contains(currencyId)
|
||||
return integrationIdMap.containsKey(currencyId)
|
||||
}
|
||||
|
||||
override suspend fun fetchEnabledYields() {
|
||||
override suspend fun fetchEnabledYields(refresh: Boolean) {
|
||||
withContext(dispatchers.io) {
|
||||
val stakingTokensWithYields = stakeKitApi.getMultipleYields().getOrThrow()
|
||||
|
||||
stakingYieldsStore.store(stakingTokensWithYields.data)
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = YIELDS_STORE_KEY,
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val stakingTokensWithYields = stakeKitApi.getMultipleYields().getOrThrow()
|
||||
stakingYieldsStore.store(stakingTokensWithYields.data)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,24 +198,32 @@ internal class DefaultStakingRepository(
|
|||
|
||||
override suspend fun fetchSingleYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
address: CryptoCurrencyAddress,
|
||||
refresh: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) return@withContext
|
||||
|
||||
val cryptoCurrency = address.cryptoCurrency
|
||||
val rawCurrencyId =
|
||||
cryptoCurrency.id.rawCurrencyId ?: error("Staking custom tokens is not available")
|
||||
|
||||
val integrationId = integrationIdMap[rawCurrencyId] ?: return@withContext
|
||||
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val requestBody = getBalanceRequestData(address.address, integrationId)
|
||||
val result = stakeKitApi.getSingleYieldBalance(
|
||||
integrationId = integrationId,
|
||||
body = getBalanceRequestData(address, integrationId),
|
||||
integrationId = requestBody.integrationId,
|
||||
body = requestBody,
|
||||
).getOrThrow()
|
||||
|
||||
stakingBalanceStore.store(
|
||||
integrationId,
|
||||
requestBody.integrationId,
|
||||
YieldBalanceWrapperDTO(
|
||||
balances = result,
|
||||
integrationId = integrationId,
|
||||
integrationId = requestBody.integrationId,
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
@ -208,70 +232,157 @@ internal class DefaultStakingRepository(
|
|||
|
||||
override fun getSingleYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
address: CryptoCurrencyAddress,
|
||||
): Flow<YieldBalance> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
stakingBalanceStore.get(integrationId)
|
||||
.collectLatest {
|
||||
send(
|
||||
yieldBalanceConverter.convert(
|
||||
YieldBalanceConverter.Data(
|
||||
balance = it,
|
||||
integrationId = integrationId,
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
send(YieldBalance.Empty)
|
||||
} else {
|
||||
launch(dispatchers.io) {
|
||||
val integrationId = integrationIdMap[address.cryptoCurrency.id.rawCurrencyId]
|
||||
?: error("Could not get integrationId")
|
||||
stakingBalanceStore.get(integrationId)
|
||||
.collectLatest {
|
||||
send(
|
||||
yieldBalanceConverter.convert(
|
||||
YieldBalanceConverter.Data(
|
||||
balance = it,
|
||||
integrationId = integrationId,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchSingleYieldBalance(
|
||||
userWalletId,
|
||||
address,
|
||||
integrationId,
|
||||
)
|
||||
withContext(dispatchers.io) {
|
||||
fetchSingleYieldBalance(
|
||||
userWalletId,
|
||||
address,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.cancellable()
|
||||
|
||||
override suspend fun getSingleYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
address: CryptoCurrencyAddress,
|
||||
): YieldBalance = withContext(dispatchers.io) {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
YieldBalance.Empty
|
||||
} else {
|
||||
fetchSingleYieldBalance(userWalletId, address)
|
||||
|
||||
val integrationId = integrationIdMap[address.cryptoCurrency.id.rawCurrencyId]
|
||||
?: error("Could not get integrationId")
|
||||
val result = stakingBalanceStore.getSyncOrNull(integrationId) ?: return@withContext YieldBalance.Error
|
||||
yieldBalanceConverter.convert(
|
||||
YieldBalanceConverter.Data(
|
||||
balance = result,
|
||||
integrationId = integrationId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun fetchMultiYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
refresh: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val result = stakeKitApi.getMultipleYieldBalances(
|
||||
addresses.map { getBalanceRequestData(it.address, integrationId) },
|
||||
).getOrThrow()
|
||||
if (!stakingFeatureToggle.isStakingEnabled) return@withContext
|
||||
try {
|
||||
isYieldBalanceFetching.update {
|
||||
it + (userWalletId to true)
|
||||
}
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val result = stakeKitApi.getMultipleYieldBalances(
|
||||
addresses
|
||||
.mapNotNull { networkAddress ->
|
||||
val cryptoCurrency = networkAddress.cryptoCurrency
|
||||
val rawCurrencyId = cryptoCurrency.id.rawCurrencyId ?: error("Currency raw id is null")
|
||||
val integrationId = integrationIdMap[rawCurrencyId]
|
||||
|
||||
stakingBalanceStore.store(result)
|
||||
},
|
||||
)
|
||||
if (integrationId != null) {
|
||||
networkAddress.address to integrationId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.distinct()
|
||||
.map { getBalanceRequestData(it.first, it.second) },
|
||||
).getOrThrow()
|
||||
|
||||
stakingBalanceStore.store(result)
|
||||
},
|
||||
)
|
||||
} finally {
|
||||
isYieldBalanceFetching.update {
|
||||
it - userWalletId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
): Flow<YieldBalanceList> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
stakingBalanceStore.get()
|
||||
.collectLatest { send(yieldBalanceListConverter.convert(it)) }
|
||||
}
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
send(YieldBalanceList.Empty)
|
||||
} else {
|
||||
launch(dispatchers.io) {
|
||||
stakingBalanceStore.get()
|
||||
.collectLatest { send(yieldBalanceListConverter.convert(it)) }
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchMultiYieldBalance(
|
||||
userWalletId,
|
||||
addresses,
|
||||
integrationId,
|
||||
)
|
||||
withContext(dispatchers.io) {
|
||||
fetchMultiYieldBalance(
|
||||
userWalletId,
|
||||
addresses,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.cancellable()
|
||||
|
||||
override fun getMultiYieldBalanceLce(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
send(YieldBalanceList.Empty)
|
||||
} else {
|
||||
launch(dispatchers.io) {
|
||||
combine(
|
||||
stakingBalanceStore.get(),
|
||||
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, addresses, refresh = false) },
|
||||
catch = { raise(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMultiYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): YieldBalanceList = withContext(dispatchers.io) {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
YieldBalanceList.Empty
|
||||
} else {
|
||||
fetchMultiYieldBalance(userWalletId, addresses)
|
||||
val result = stakingBalanceStore.getSyncOrNull() ?: return@withContext YieldBalanceList.Error
|
||||
yieldBalanceListConverter.convert(result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: String, symbol: String): Yield? {
|
||||
return yields.find { it.token.coinGeckoId == currencyId && it.token.symbol == symbol }
|
||||
}
|
||||
|
|
@ -297,19 +408,33 @@ internal class DefaultStakingRepository(
|
|||
|
||||
private fun getYieldBalancesKey(userWalletId: UserWalletId) = "yield_balance_${userWalletId.stringValue}"
|
||||
|
||||
companion object {
|
||||
private val integrationIds = setOf(
|
||||
Blockchain.Solana.toCoinId(),
|
||||
Blockchain.Cosmos.toCoinId(),
|
||||
Blockchain.Polkadot.toCoinId(),
|
||||
Blockchain.Polygon.toCoinId(),
|
||||
Blockchain.Avalanche.toCoinId(),
|
||||
Blockchain.Tron.toCoinId(),
|
||||
Blockchain.Cronos.toCoinId(),
|
||||
Blockchain.Binance.toCoinId(),
|
||||
Blockchain.Kava.toCoinId(),
|
||||
Blockchain.Near.toCoinId(),
|
||||
Blockchain.Tezos.toCoinId(),
|
||||
private companion object {
|
||||
const val YIELDS_STORE_KEY = "yields"
|
||||
|
||||
const val SOLANA_INTEGRATION_ID = "solana-sol-native-multivalidator-staking"
|
||||
const val COSMOS_INTEGRATION_ID = "cosmos-atom-native-staking"
|
||||
const val POLKADOT_INTEGRATION_ID = "polkadot-dot-validator-staking"
|
||||
const val ETHEREUM_INTEGRATION_ID = "ethereum-matic-native-staking"
|
||||
const val AVALANCHE_INTEGRATION_ID = "avalanche-avax-native-staking"
|
||||
const val TRON_INTEGRATION_ID = "tron-trx-native-staking"
|
||||
const val CRONOS_INTEGRATION_ID = "cronos-cro-native-staking"
|
||||
const val BINANCE_INTEGRATION_ID = "binance-bnb-native-staking"
|
||||
const val KAVA_INTEGRATION_ID = "kava-kava-native-staking"
|
||||
const val NEAR_INTEGRATION_ID = "near-near-native-staking"
|
||||
const val TEZOS_INTEGRATION_ID = "tezos-xtz-native-staking"
|
||||
|
||||
val integrationIdMap = mapOf(
|
||||
Blockchain.Solana.toCoinId() to SOLANA_INTEGRATION_ID,
|
||||
Blockchain.Cosmos.toCoinId() to COSMOS_INTEGRATION_ID,
|
||||
Blockchain.Polkadot.toCoinId() to POLKADOT_INTEGRATION_ID,
|
||||
Blockchain.Polygon.toCoinId() to ETHEREUM_INTEGRATION_ID,
|
||||
Blockchain.Avalanche.toCoinId() to AVALANCHE_INTEGRATION_ID,
|
||||
Blockchain.Tron.toCoinId() to TRON_INTEGRATION_ID,
|
||||
Blockchain.Cronos.toCoinId() to CRONOS_INTEGRATION_ID,
|
||||
Blockchain.Binance.toCoinId() to BINANCE_INTEGRATION_ID,
|
||||
Blockchain.Kava.toCoinId() to KAVA_INTEGRATION_ID,
|
||||
Blockchain.Near.toCoinId() to NEAR_INTEGRATION_ID,
|
||||
Blockchain.Tezos.toCoinId() to TEZOS_INTEGRATION_ID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.datasource.api.stakekit.StakeKitApi
|
|||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -24,6 +25,7 @@ internal object StakingDataModule {
|
|||
stakingTokenStore: StakingYieldsStore,
|
||||
stakingBalanceStore: StakingBalanceStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
stakingFeatureToggle: StakingFeatureToggles,
|
||||
cacheRegistry: CacheRegistry,
|
||||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
|
|
@ -32,6 +34,7 @@ internal object StakingDataModule {
|
|||
stakingBalanceStore = stakingBalanceStore,
|
||||
dispatchers = dispatchers,
|
||||
cacheRegistry = cacheRegistry,
|
||||
stakingFeatureToggle = stakingFeatureToggle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -124,6 +124,60 @@ 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>,
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ dependencies {
|
|||
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
implementation(projects.features.staking.api)
|
||||
}
|
||||
|
|
@ -6,9 +6,23 @@ sealed class YieldBalance {
|
|||
|
||||
data class Data(
|
||||
val balance: YieldBalanceItem,
|
||||
) : YieldBalance()
|
||||
) : YieldBalance() {
|
||||
fun getTotalStakingBalance(): BigDecimal {
|
||||
return balance.items
|
||||
.filterNot { it.type == BalanceType.REWARDS }
|
||||
.sumOf { it.amount * it.pricePerShare }
|
||||
}
|
||||
|
||||
fun getRewardStakingBalance(): BigDecimal {
|
||||
return balance.items
|
||||
.filter { it.type == BalanceType.REWARDS }
|
||||
.sumOf { it.amount * it.pricePerShare }
|
||||
}
|
||||
}
|
||||
|
||||
data object Empty : YieldBalance()
|
||||
|
||||
data object Error : YieldBalance()
|
||||
}
|
||||
|
||||
data class YieldBalanceItem(
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@ sealed class YieldBalanceList {
|
|||
data class Data(
|
||||
val balances: List<YieldBalance>,
|
||||
) : YieldBalanceList() {
|
||||
fun getBalance(rawCurrencyId: String?): YieldBalance? {
|
||||
fun getBalance(rawCurrencyId: String?): YieldBalance {
|
||||
return balances.firstOrNull { yield ->
|
||||
(yield as? YieldBalance.Data)?.balance?.items
|
||||
?.any { it.rawCurrencyId == rawCurrencyId } == true
|
||||
}
|
||||
} ?: YieldBalance.Error
|
||||
}
|
||||
}
|
||||
|
||||
data object Empty : YieldBalanceList()
|
||||
|
||||
data object Error : YieldBalanceList()
|
||||
}
|
||||
|
|
@ -12,10 +12,10 @@ import com.tangem.domain.staking.repositories.StakingRepository
|
|||
class FetchStakingTokensUseCase(
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
suspend operator fun invoke(): Either<Throwable, Unit> {
|
||||
suspend operator fun invoke(isRefresh: Boolean = false): Either<Throwable, Unit> {
|
||||
return either {
|
||||
catch(
|
||||
block = { stakingRepository.fetchEnabledYields() },
|
||||
block = { stakingRepository.fetchEnabledYields(isRefresh) },
|
||||
catch = { StakingTokensError.DataError(it) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.staking
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class FetchStakingYieldBalanceUseCase(
|
||||
|
|
@ -10,14 +11,12 @@ class FetchStakingYieldBalanceUseCase(
|
|||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
address: CryptoCurrencyAddress,
|
||||
refresh: Boolean = false,
|
||||
): Either<Throwable, Unit> = Either.catch {
|
||||
stakingRepository.fetchSingleYieldBalance(
|
||||
userWalletId = userWalletId,
|
||||
address = address,
|
||||
integrationId = integrationId,
|
||||
refresh = refresh,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import arrow.core.right
|
|||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
|
@ -16,13 +17,11 @@ class GetStakingYieldBalanceUseCase(
|
|||
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
address: CryptoCurrencyAddress,
|
||||
): EitherFlow<Throwable, YieldBalance> {
|
||||
return stakingRepository.getSingleYieldBalanceFlow(
|
||||
userWalletId = userWalletId,
|
||||
address = address,
|
||||
integrationId = integrationId,
|
||||
).map<YieldBalance, Either<Throwable, YieldBalance>> { it.right() }
|
||||
.catch { emit(it.left()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.staking.model.Yield
|
|||
import com.tangem.domain.staking.model.action.EnterAction
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransaction
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.staking.model.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
|
|
@ -17,7 +18,7 @@ interface StakingRepository {
|
|||
|
||||
fun isStakingSupported(currencyId: String): Boolean
|
||||
|
||||
suspend fun fetchEnabledYields()
|
||||
suspend fun fetchEnabledYields(refresh: Boolean)
|
||||
|
||||
suspend fun getEntryInfo(integrationId: String): StakingEntryInfo
|
||||
|
||||
|
|
@ -30,30 +31,35 @@ interface StakingRepository {
|
|||
|
||||
suspend fun fetchSingleYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
address: CryptoCurrencyAddress,
|
||||
refresh: Boolean = false,
|
||||
)
|
||||
|
||||
fun getSingleYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
): Flow<YieldBalance>
|
||||
fun getSingleYieldBalanceFlow(userWalletId: UserWalletId, address: CryptoCurrencyAddress): Flow<YieldBalance>
|
||||
|
||||
suspend fun getSingleYieldBalanceSync(userWalletId: UserWalletId, address: CryptoCurrencyAddress): YieldBalance
|
||||
|
||||
suspend fun fetchMultiYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
refresh: Boolean = false,
|
||||
)
|
||||
|
||||
fun getMultiYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
): Flow<YieldBalanceList>
|
||||
|
||||
fun getMultiYieldBalanceLce(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): LceFlow<Throwable, YieldBalanceList>
|
||||
|
||||
suspend fun getMultiYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): YieldBalanceList
|
||||
|
||||
suspend fun createEnterAction(
|
||||
integrationId: String,
|
||||
amount: BigDecimal,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ dependencies {
|
|||
implementation(projects.domain.settings)
|
||||
implementation(projects.features.swap.domain.api)
|
||||
implementation(projects.features.swap.domain.models)
|
||||
|
||||
/** Project - Api */
|
||||
implementation(projects.features.send.api)
|
||||
implementation(projects.features.staking.api)
|
||||
|
||||
/** Project - Other */
|
||||
implementation(projects.core.utils)
|
||||
|
|
|
|||
|
|
@ -10,12 +10,20 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(projects.domain.txhistory.models)
|
||||
/** Project - Core */
|
||||
implementation(projects.core.analytics.models)
|
||||
|
||||
/** Project - Domain */
|
||||
implementation(projects.domain.txhistory.models)
|
||||
implementation(projects.domain.staking.models)
|
||||
|
||||
/** SDK dependencies */
|
||||
implementation(deps.tangem.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
|
||||
/** Other dependencies */
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.timber)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -45,6 +46,9 @@ data class CryptoCurrencyStatus(
|
|||
|
||||
/** The network address */
|
||||
open val networkAddress: NetworkAddress? = null
|
||||
|
||||
/** Staking yield balance */
|
||||
open val yieldBalance: YieldBalance? = null
|
||||
}
|
||||
|
||||
/** Represents the Loading state of a cryptocurrency, typically while fetching its details. */
|
||||
|
|
@ -107,6 +111,7 @@ data class CryptoCurrencyStatus(
|
|||
override val fiatAmount: BigDecimal,
|
||||
override val fiatRate: BigDecimal,
|
||||
override val priceChange: BigDecimal,
|
||||
override val yieldBalance: YieldBalance?,
|
||||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<TxHistoryItem>,
|
||||
override val networkAddress: NetworkAddress,
|
||||
|
|
@ -128,6 +133,7 @@ data class CryptoCurrencyStatus(
|
|||
override val fiatAmount: BigDecimal?,
|
||||
override val fiatRate: BigDecimal?,
|
||||
override val priceChange: BigDecimal?,
|
||||
override val yieldBalance: YieldBalance?,
|
||||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<TxHistoryItem>,
|
||||
override val networkAddress: NetworkAddress,
|
||||
|
|
@ -143,6 +149,7 @@ data class CryptoCurrencyStatus(
|
|||
*/
|
||||
data class NoQuote(
|
||||
override val amount: BigDecimal,
|
||||
override val yieldBalance: YieldBalance?,
|
||||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<TxHistoryItem>,
|
||||
override val networkAddress: NetworkAddress,
|
||||
|
|
|
|||
|
|
@ -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.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
|
@ -19,6 +20,7 @@ class FetchCardTokenListUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Either<TokenListError, Unit> {
|
||||
|
|
@ -39,8 +41,13 @@ class FetchCardTokenListUseCase(
|
|||
refresh = refresh,
|
||||
)
|
||||
}
|
||||
|
||||
awaitAll(fetchStatuses, fetchQuotes)
|
||||
val yieldBalances = async {
|
||||
fetchYieldBalances(
|
||||
userWalletId = userWalletId,
|
||||
refresh = refresh,
|
||||
)
|
||||
}
|
||||
awaitAll(fetchStatuses, fetchQuotes, yieldBalances)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,4 +76,12 @@ class FetchCardTokenListUseCase(
|
|||
catch = { /* Ignore error */ },
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun fetchYieldBalances(userWalletId: UserWalletId, refresh: Boolean) {
|
||||
val networkAddresses = networksRepository.getNetworkAddresses(userWalletId)
|
||||
catch(
|
||||
block = { stakingRepository.fetchMultiYieldBalance(userWalletId, networkAddresses, refresh) },
|
||||
catch = { /* Ignore error */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.tokens
|
|||
|
||||
import arrow.core.left
|
||||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -21,6 +22,7 @@ class GetCardTokensListUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
|
@ -43,6 +45,7 @@ class GetCardTokensListUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCardCurrenciesStatusesFlow()
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
val networkId = cryptoCurrencyStatus.currency.network.id
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -16,6 +17,7 @@ class GetCryptoCurrencyStatusSyncUseCase(
|
|||
internal val currenciesRepository: CurrenciesRepository,
|
||||
internal val quotesRepository: QuotesRepository,
|
||||
internal val networksRepository: NetworksRepository,
|
||||
internal val stakingRepository: StakingRepository,
|
||||
internal val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -29,6 +31,7 @@ class GetCryptoCurrencyStatusSyncUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrencyStatusSync(cryptoCurrencyId, isSingleWalletWithTokens)
|
||||
|
|
@ -41,6 +44,7 @@ class GetCryptoCurrencyStatusSyncUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getPrimaryCurrencyStatusSync()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -15,6 +16,7 @@ class GetCryptoCurrencyStatusesSyncUseCase(
|
|||
internal val currenciesRepository: CurrenciesRepository,
|
||||
internal val quotesRepository: QuotesRepository,
|
||||
internal val networksRepository: NetworksRepository,
|
||||
internal val stakingRepository: StakingRepository,
|
||||
internal val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -24,6 +26,7 @@ class GetCryptoCurrencyStatusesSyncUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrenciesStatusesSync()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -24,6 +25,7 @@ class GetCurrencyStatusUpdatesUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -60,6 +62,7 @@ class GetCurrencyStatusUpdatesUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.tokens.model.warnings.HederaWarnings
|
||||
|
|
@ -26,6 +27,7 @@ class GetCurrencyWarningsUseCase(
|
|||
private val networksRepository: NetworksRepository,
|
||||
private val swapRepository: SwapRepository,
|
||||
private val marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val promoRepository: PromoRepository,
|
||||
private val showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -43,6 +45,7 @@ class GetCurrencyWarningsUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
return combine(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.tokens
|
|||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
|
|
@ -16,6 +17,7 @@ class GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
|||
internal val currenciesRepository: CurrenciesRepository,
|
||||
internal val quotesRepository: QuotesRepository,
|
||||
internal val networksRepository: NetworksRepository,
|
||||
internal val stakingRepository: StakingRepository,
|
||||
internal val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -30,6 +32,7 @@ class GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return either {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -17,6 +18,7 @@ class GetNetworkCoinStatusUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -49,6 +51,7 @@ class GetNetworkCoinStatusUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
val maybeCurrency = if (isSingleWalletWithTokens) {
|
||||
|
|
@ -69,6 +72,7 @@ class GetNetworkCoinStatusUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
val networkFlow = if (isSingleWalletWithTokens) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -24,6 +25,7 @@ class GetPrimaryCurrencyStatusUpdatesUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -46,6 +48,7 @@ class GetPrimaryCurrencyStatusUpdatesUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.core.utils.EitherFlow
|
|||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.core.utils.toLce
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -26,6 +27,7 @@ class GetTokenListUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
|
@ -35,6 +37,7 @@ class GetTokenListUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrenciesStatusesFlow().transformLatest { maybeTokens ->
|
||||
|
|
@ -55,6 +58,7 @@ class GetTokenListUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrenciesStatuses(userWalletId).transformLatest { maybeCurrencies ->
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.core.lce.Lce
|
|||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lce
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||
|
|
@ -23,6 +24,7 @@ class GetWalletTotalBalanceUseCase(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
|
|
@ -72,6 +74,7 @@ class GetWalletTotalBalanceUseCase(
|
|||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrenciesStatuses(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
|||
internal fun CurrenciesStatusesOperations.Error.mapToCurrencyError(): CurrencyStatusError {
|
||||
return when (this) {
|
||||
is CurrenciesStatusesOperations.Error.DataError -> CurrencyStatusError.DataError(this.cause)
|
||||
is CurrenciesStatusesOperations.Error.EmptyYieldBalances,
|
||||
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ internal fun CurrenciesStatusesOperations.Error.mapToTokenListError(): TokenList
|
|||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus,
|
||||
is CurrenciesStatusesOperations.Error.EmptyYieldBalances,
|
||||
-> TokenListError.EmptyTokens
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import com.tangem.domain.core.lce.LceFlow
|
|||
import com.tangem.domain.core.lce.lce
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.staking.model.YieldBalanceList
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
|
|
@ -20,6 +23,7 @@ internal class CurrenciesStatusesLceOperations(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
fun getCurrenciesStatuses(
|
||||
|
|
@ -65,11 +69,18 @@ internal class CurrenciesStatusesLceOperations(
|
|||
|
||||
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
|
||||
|
||||
val addresses = networksRepository.getNetworkAddresses(userWalletId)
|
||||
combine(
|
||||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(userWalletId, networks),
|
||||
) { maybeQuotes, maybeNetworksStatuses ->
|
||||
val statuses = createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses)
|
||||
getYieldBalances(userWalletId, addresses),
|
||||
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
|
||||
val statuses = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeQuotes = maybeQuotes,
|
||||
maybeNetworkStatuses = maybeNetworksStatuses,
|
||||
maybeYieldBalances = maybeYieldBalances,
|
||||
)
|
||||
emit(statuses)
|
||||
}.collect()
|
||||
}
|
||||
|
|
@ -84,9 +95,10 @@ internal class CurrenciesStatusesLceOperations(
|
|||
lceLoading()
|
||||
} else {
|
||||
createCurrenciesStatuses(
|
||||
nonEmptyCurrencies,
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
maybeYieldBalances = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +125,7 @@ internal class CurrenciesStatusesLceOperations(
|
|||
currencies: NonEmptyList<CryptoCurrency>,
|
||||
maybeQuotes: Either<TokenListError, Set<Quote>>?,
|
||||
maybeNetworkStatuses: Lce<TokenListError, Set<NetworkStatus>>?,
|
||||
maybeYieldBalances: Lce<TokenListError, YieldBalanceList>?,
|
||||
): Lce<TokenListError, List<CryptoCurrencyStatus>> = lce {
|
||||
isLoading.set(maybeNetworkStatuses == null)
|
||||
|
||||
|
|
@ -127,11 +140,20 @@ internal class CurrenciesStatusesLceOperations(
|
|||
null
|
||||
}
|
||||
|
||||
val yieldBalances = maybeYieldBalances?.getOrNull()
|
||||
|
||||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
val yieldBalance = (yieldBalances as? YieldBalanceList.Data)?.getBalance(currency.id.rawCurrencyId)
|
||||
|
||||
createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed)
|
||||
createCurrencyStatus(
|
||||
currency = currency,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
yieldBalance = yieldBalance,
|
||||
ignoreQuote = quotesRetrievingFailed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,12 +161,14 @@ internal class CurrenciesStatusesLceOperations(
|
|||
currency: CryptoCurrency,
|
||||
quote: Quote?,
|
||||
networkStatus: NetworkStatus?,
|
||||
yieldBalance: YieldBalance?,
|
||||
ignoreQuote: Boolean,
|
||||
): CryptoCurrencyStatus {
|
||||
val currencyStatusOperations = CurrencyStatusOperations(
|
||||
currency = currency,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
yieldBalance = yieldBalance,
|
||||
ignoreQuote = ignoreQuote,
|
||||
)
|
||||
|
||||
|
|
@ -167,6 +191,18 @@ internal class CurrenciesStatusesLceOperations(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getYieldBalances(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): LceFlow<TokenListError, YieldBalanceList> {
|
||||
return stakingRepository.getMultiYieldBalanceLce(
|
||||
userWalletId = userWalletId,
|
||||
addresses = addresses,
|
||||
).map { maybeBalances ->
|
||||
maybeBalances.mapError { TokenListError.DataError(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIds(currencies: List<CryptoCurrency>): Pair<NonEmptySet<Network>, NonEmptySet<CryptoCurrency.ID>> {
|
||||
val currencyIdToNetworkId = currencies.associate { currency ->
|
||||
currency.id to currency.network
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ package com.tangem.domain.tokens.operations
|
|||
import arrow.core.*
|
||||
import arrow.core.raise.*
|
||||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.staking.model.YieldBalanceList
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
|
|
@ -17,6 +20,7 @@ internal class CurrenciesStatusesOperations(
|
|||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val userWalletId: UserWalletId,
|
||||
) {
|
||||
|
||||
|
|
@ -42,6 +46,7 @@ internal class CurrenciesStatusesOperations(
|
|||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
maybeYieldBalances = null,
|
||||
)
|
||||
|
||||
emit(maybeLoadingCurrenciesStatuses)
|
||||
|
|
@ -51,8 +56,14 @@ internal class CurrenciesStatusesOperations(
|
|||
val currenciesFlow = combine(
|
||||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(networks),
|
||||
) { maybeQuotes, maybeNetworksStatuses ->
|
||||
createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses)
|
||||
getYieldBalances(),
|
||||
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
|
||||
createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeQuotes = maybeQuotes,
|
||||
maybeNetworkStatuses = maybeNetworksStatuses,
|
||||
maybeYieldBalances = maybeYieldBalances,
|
||||
)
|
||||
}
|
||||
|
||||
emitAll(currenciesFlow)
|
||||
|
|
@ -70,7 +81,9 @@ internal class CurrenciesStatusesOperations(
|
|||
val quotes = quotesRepository.getQuotesSync(currenciesIds, false).right()
|
||||
val networkStatuses =
|
||||
networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right()
|
||||
return createCurrenciesStatuses(nonEmptyCurrencies, quotes, networkStatuses)
|
||||
val yieldBalances = getYieldBalancesSync()
|
||||
|
||||
return createCurrenciesStatuses(nonEmptyCurrencies, quotes, networkStatuses, yieldBalances)
|
||||
},
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
)
|
||||
|
|
@ -98,7 +111,9 @@ internal class CurrenciesStatusesOperations(
|
|||
).firstOrNull {
|
||||
it.network == currency.network
|
||||
}.right()
|
||||
return createCurrencyStatus(currency, quotes, networkStatuses)
|
||||
val yieldBalances = getYieldBalanceSync(currency)
|
||||
|
||||
return createCurrencyStatus(currency, quotes, networkStatuses, yieldBalances)
|
||||
},
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
)
|
||||
|
|
@ -142,8 +157,9 @@ internal class CurrenciesStatusesOperations(
|
|||
},
|
||||
catch = { Error.DataError(it).left() },
|
||||
)
|
||||
val yieldBalances = getYieldBalanceSync(currency)
|
||||
|
||||
return createCurrencyStatus(currency, quotes, networkStatus)
|
||||
return createCurrencyStatus(currency, quotes, networkStatus, yieldBalances)
|
||||
}
|
||||
|
||||
fun getCardCurrenciesStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
|
||||
|
|
@ -167,6 +183,7 @@ internal class CurrenciesStatusesOperations(
|
|||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
maybeYieldBalances = null,
|
||||
)
|
||||
|
||||
emit(maybeLoadingCurrenciesStatuses)
|
||||
|
|
@ -176,8 +193,9 @@ internal class CurrenciesStatusesOperations(
|
|||
val currenciesFlow = combine(
|
||||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(networks),
|
||||
) { maybeQuotes, maybeNetworksStatuses ->
|
||||
createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses)
|
||||
getYieldBalances(),
|
||||
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
|
||||
createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses, maybeYieldBalances)
|
||||
}
|
||||
|
||||
emitAll(currenciesFlow)
|
||||
|
|
@ -255,8 +273,10 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
}
|
||||
|
||||
return combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus ->
|
||||
createCurrencyStatus(currency, maybeQuote, maybeNetworkStatus)
|
||||
val yieldBalanceFlow = getYieldBalance(currency)
|
||||
|
||||
return combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance ->
|
||||
createCurrencyStatus(currency, maybeQuote, maybeNetworkStatus, maybeYieldBalance)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -264,6 +284,7 @@ internal class CurrenciesStatusesOperations(
|
|||
currencies: NonEmptyList<CryptoCurrency>,
|
||||
maybeQuotes: Either<Error, Set<Quote>>?,
|
||||
maybeNetworkStatuses: Either<Error, Set<NetworkStatus>>?,
|
||||
maybeYieldBalances: Either<Error, YieldBalanceList>?,
|
||||
): Either<Error, List<CryptoCurrencyStatus>> = either {
|
||||
var quotesRetrievingFailed = false
|
||||
|
||||
|
|
@ -281,11 +302,19 @@ internal class CurrenciesStatusesOperations(
|
|||
},
|
||||
)
|
||||
|
||||
val yieldBalances = maybeYieldBalances?.getOrNull()
|
||||
|
||||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
|
||||
createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed)
|
||||
val yieldBalance = (yieldBalances as? YieldBalanceList.Data)?.getBalance(currency.id.rawCurrencyId)
|
||||
createCurrencyStatus(
|
||||
currency = currency,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
ignoreQuote = quotesRetrievingFailed,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,6 +322,7 @@ internal class CurrenciesStatusesOperations(
|
|||
currency: CryptoCurrency,
|
||||
maybeQuote: Either<Error, Quote?>,
|
||||
maybeNetworkStatus: Either<Error, NetworkStatus?>,
|
||||
maybeYieldBalance: Either<Error, YieldBalance>?,
|
||||
): Either<Error, CryptoCurrencyStatus> = either {
|
||||
var quoteRetrievingFailed = false
|
||||
|
||||
|
|
@ -301,8 +331,15 @@ internal class CurrenciesStatusesOperations(
|
|||
quoteRetrievingFailed = true
|
||||
null
|
||||
}
|
||||
val yieldBalance = maybeYieldBalance?.getOrNull()
|
||||
|
||||
createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quoteRetrievingFailed)
|
||||
createCurrencyStatus(
|
||||
currency = currency,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
ignoreQuote = quoteRetrievingFailed,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCurrencyStatus(
|
||||
|
|
@ -310,12 +347,14 @@ internal class CurrenciesStatusesOperations(
|
|||
quote: Quote?,
|
||||
networkStatus: NetworkStatus?,
|
||||
ignoreQuote: Boolean,
|
||||
yieldBalance: YieldBalance?,
|
||||
): CryptoCurrencyStatus {
|
||||
val currencyStatusOperations = CurrencyStatusOperations(
|
||||
currency = currency,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
ignoreQuote = ignoreQuote,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
|
||||
return currencyStatusOperations.createTokenStatus()
|
||||
|
|
@ -396,6 +435,65 @@ 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 suspend fun getYieldBalancesSync(): Either<Error.EmptyYieldBalances, YieldBalanceList> {
|
||||
return catch(
|
||||
block = {
|
||||
val networkAddresses = networksRepository.getNetworkAddresses(userWalletId)
|
||||
stakingRepository.getMultiYieldBalanceSync(
|
||||
userWalletId,
|
||||
networkAddresses,
|
||||
).right()
|
||||
},
|
||||
catch = {
|
||||
Error.EmptyYieldBalances.left()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getYieldBalanceSync(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Either<Error.EmptyYieldBalances, YieldBalance> {
|
||||
return catch(
|
||||
block = {
|
||||
val address = networksRepository.getNetworkAddress(userWalletId, cryptoCurrency)
|
||||
stakingRepository.getSingleYieldBalanceSync(
|
||||
userWalletId,
|
||||
address,
|
||||
).right()
|
||||
},
|
||||
catch = {
|
||||
Error.EmptyYieldBalances.left()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@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()) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIds(
|
||||
currencies: NonEmptyList<CryptoCurrency>,
|
||||
): Pair<NonEmptySet<Network>, NonEmptySet<CryptoCurrency.ID>> {
|
||||
|
|
@ -413,14 +511,16 @@ internal class CurrenciesStatusesOperations(
|
|||
|
||||
sealed class Error {
|
||||
|
||||
object EmptyCurrencies : Error()
|
||||
data object EmptyCurrencies : Error()
|
||||
|
||||
object EmptyQuotes : Error()
|
||||
data object EmptyQuotes : Error()
|
||||
|
||||
object EmptyNetworksStatuses : Error()
|
||||
data object EmptyNetworksStatuses : Error()
|
||||
|
||||
object UnableToCreateCurrencyStatus : Error()
|
||||
data object UnableToCreateCurrencyStatus : Error()
|
||||
|
||||
data class DataError(val cause: Throwable) : Error()
|
||||
|
||||
data object EmptyYieldBalances : Error()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -7,6 +8,7 @@ internal class CurrencyStatusOperations(
|
|||
private val currency: CryptoCurrency,
|
||||
private val quote: Quote?,
|
||||
private val networkStatus: NetworkStatus?,
|
||||
private val yieldBalance: YieldBalance?,
|
||||
private val ignoreQuote: Boolean,
|
||||
) {
|
||||
|
||||
|
|
@ -18,7 +20,7 @@ internal class CurrencyStatusOperations(
|
|||
is NetworkStatus.MissedDerivation -> createMissedDerivationStatus()
|
||||
is NetworkStatus.Unreachable -> createUnreachableStatus(status)
|
||||
is NetworkStatus.NoAccount -> createNoAccountStatus(status)
|
||||
is NetworkStatus.Verified -> createStatus(status)
|
||||
is NetworkStatus.Verified -> createStatus(status, yieldBalance)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +44,7 @@ internal class CurrencyStatusOperations(
|
|||
networkAddress = status.address,
|
||||
)
|
||||
|
||||
private fun createStatus(status: NetworkStatus.Verified): CryptoCurrencyStatus.Value {
|
||||
private fun createStatus(status: NetworkStatus.Verified, yieldBalance: YieldBalance?): CryptoCurrencyStatus.Value {
|
||||
val amount = when (val amount = status.amounts[currency.id]) {
|
||||
null -> {
|
||||
return CryptoCurrencyStatus.Loading
|
||||
|
|
@ -62,6 +64,7 @@ internal class CurrencyStatusOperations(
|
|||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom(
|
||||
amount = amount,
|
||||
|
|
@ -71,6 +74,7 @@ internal class CurrencyStatusOperations(
|
|||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
quote == null -> CryptoCurrencyStatus.Loading
|
||||
else -> CryptoCurrencyStatus.Loaded(
|
||||
|
|
@ -81,6 +85,7 @@ internal class CurrencyStatusOperations(
|
|||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import arrow.core.NonEmptyList
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenListFiatBalanceOperations(
|
||||
|
|
@ -56,10 +58,13 @@ internal class TokenListFiatBalanceOperations(
|
|||
currentBalance: TotalFiatBalance,
|
||||
): TotalFiatBalance {
|
||||
return with(currentBalance) {
|
||||
val stakingBalance = (status.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
val fiatStakingBalance = status.fiatRate.times(stakingBalance)
|
||||
|
||||
(this as? TotalFiatBalance.Loaded)?.copy(
|
||||
amount = this.amount + status.fiatAmount,
|
||||
amount = this.amount + status.fiatAmount + fiatStakingBalance,
|
||||
) ?: TotalFiatBalance.Loaded(
|
||||
amount = status.fiatAmount,
|
||||
amount = status.fiatAmount + fiatStakingBalance,
|
||||
isAllAmountsSummarized = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -71,12 +76,13 @@ internal class TokenListFiatBalanceOperations(
|
|||
): TotalFiatBalance {
|
||||
return with(currentBalance) {
|
||||
val isTokenAmountCanBeSummarized = status.fiatAmount != null
|
||||
|
||||
val yieldBalance = (status.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
val fiatYieldBalance = status.fiatRate?.times(yieldBalance).orZero()
|
||||
(this as? TotalFiatBalance.Loaded)?.copy(
|
||||
amount = this.amount + (status.fiatAmount ?: BigDecimal.ZERO),
|
||||
amount = this.amount + status.fiatAmount.orZero() + fiatYieldBalance,
|
||||
isAllAmountsSummarized = isTokenAmountCanBeSummarized,
|
||||
) ?: TotalFiatBalance.Loaded(
|
||||
amount = status.fiatAmount ?: BigDecimal.ZERO,
|
||||
amount = status.fiatAmount.orZero() + fiatYieldBalance,
|
||||
isAllAmountsSummarized = isTokenAmountCanBeSummarized,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
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.tokens.model.NetworkStatus
|
||||
|
|
@ -62,8 +63,33 @@ interface NetworksRepository {
|
|||
|
||||
fun isNeedToCreateAccountWithoutReserve(network: Network): Boolean
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info of added currencies of [network] in selected wallet [userWalletId]
|
||||
*/
|
||||
fun getNetworkAddressesFlow(userWalletId: UserWalletId, network: Network): Flow<List<CryptoCurrencyAddress>>
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info of added currencies of [network] in selected wallet [userWalletId]
|
||||
*/
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List<CryptoCurrencyAddress>
|
||||
|
||||
/**
|
||||
* Returns address of [cryptoCurrency] in selected wallet [userWalletId]
|
||||
*/
|
||||
suspend fun getNetworkAddress(userWalletId: UserWalletId, currency: CryptoCurrency): CryptoCurrencyAddress
|
||||
|
||||
/**
|
||||
* Returns address of [cryptoCurrency] in selected wallet [userWalletId]
|
||||
*/
|
||||
fun getNetworkAddressFlow(userWalletId: UserWalletId, currency: CryptoCurrency): Flow<CryptoCurrencyAddress>
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info in selected wallet [userWalletId]
|
||||
*/
|
||||
fun getNetworkAddressesFlow(userWalletId: UserWalletId): Flow<List<CryptoCurrencyAddress>>
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info in selected wallet [userWalletId]
|
||||
*/
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId): List<CryptoCurrencyAddress>
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import arrow.core.Either
|
|||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.mock.MockNetworks
|
||||
import com.tangem.domain.tokens.mock.MockQuotes
|
||||
|
|
@ -13,6 +14,7 @@ import com.tangem.domain.tokens.model.*
|
|||
import com.tangem.domain.tokens.repository.MockCurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.MockNetworksRepository
|
||||
import com.tangem.domain.tokens.repository.MockQuotesRepository
|
||||
import com.tangem.domain.tokens.repository.MockStakingRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import junit.framework.TestCase.assertEquals
|
||||
|
|
@ -121,6 +123,7 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
|
|||
networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
yieldBalance = YieldBalance.Error,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -171,5 +174,6 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
|
|||
),
|
||||
quotesRepository = MockQuotesRepository(quotes),
|
||||
networksRepository = MockNetworksRepository(statuses),
|
||||
stakingRepository = MockStakingRepository(),
|
||||
)
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import com.tangem.domain.tokens.model.TokenList
|
|||
import com.tangem.domain.tokens.repository.MockCurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.MockNetworksRepository
|
||||
import com.tangem.domain.tokens.repository.MockQuotesRepository
|
||||
import com.tangem.domain.tokens.repository.MockStakingRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -316,5 +317,6 @@ internal class GetTokenListUseCaseTest {
|
|||
),
|
||||
quotesRepository = MockQuotesRepository(quotes),
|
||||
networksRepository = MockNetworksRepository(statuses),
|
||||
stakingRepository = MockStakingRepository(),
|
||||
)
|
||||
}
|
||||
|
|
@ -49,7 +49,8 @@ internal object MockTokenLists {
|
|||
|
||||
val loadingUngroupedTokenList = with(failedUngroupedTokenList) {
|
||||
copy(
|
||||
currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptyListOrNull() ?: emptyList(),
|
||||
currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptyListOrNull()
|
||||
?: emptyList(),
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
)
|
||||
}
|
||||
|
|
@ -60,7 +61,9 @@ internal object MockTokenLists {
|
|||
groups = groups.map { group ->
|
||||
group.copy(
|
||||
currencies = group.currencies
|
||||
.map { it.copy(value = CryptoCurrencyStatus.Loading) },
|
||||
.map { it.copy(value = CryptoCurrencyStatus.Loading) }
|
||||
.toNonEmptyListOrNull()
|
||||
?: emptyList(),
|
||||
)
|
||||
}.toNonEmptyListOrNull()!!,
|
||||
)
|
||||
|
|
@ -109,7 +112,7 @@ internal object MockTokenLists {
|
|||
|
||||
val sortedGroupedTokenList: TokenList.GroupedByNetwork
|
||||
get() {
|
||||
val groups = sortedNetworksGroups
|
||||
val groups = sortedNetworksGroups.toNonEmptyList()
|
||||
|
||||
return unsortedGroupedTokenList.copy(
|
||||
groups = groups,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens.mock
|
||||
|
||||
import arrow.core.nonEmptyListOf
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAmountStatus
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
|
|
@ -151,6 +152,7 @@ internal object MockTokensStates {
|
|||
pendingTransactions = emptySet(),
|
||||
hasCurrentNetworkTransactions = false,
|
||||
networkAddress = requireNotNull(networkStatus.value as? NetworkStatus.Verified).address,
|
||||
yieldBalance = YieldBalance.Error,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -166,6 +168,7 @@ internal object MockTokensStates {
|
|||
.first { it.network == status.currency.network }
|
||||
.value as? NetworkStatus.Verified,
|
||||
).address,
|
||||
yieldBalance = YieldBalance.Error,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@ package com.tangem.domain.tokens.repository
|
|||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.utils.toLce
|
||||
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.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
|
|
@ -44,10 +46,38 @@ internal class MockNetworksRepository(
|
|||
}
|
||||
|
||||
override fun isNeedToCreateAccountWithoutReserve(network: Network) = false
|
||||
|
||||
override fun getNetworkAddressesFlow(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Flow<List<CryptoCurrencyAddress>> = channelFlow {
|
||||
send(emptyList())
|
||||
}
|
||||
|
||||
override fun getNetworkAddressesFlow(userWalletId: UserWalletId): Flow<List<CryptoCurrencyAddress>> = channelFlow {
|
||||
send(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddresses(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): List<CryptoCurrencyAddress> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddresses(userWalletId: UserWalletId): List<CryptoCurrencyAddress> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddress(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): CryptoCurrencyAddress = CryptoCurrencyAddress(currency, "")
|
||||
|
||||
override fun getNetworkAddressFlow(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): Flow<CryptoCurrencyAddress> = channelFlow {
|
||||
send(CryptoCurrencyAddress(currency, ""))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.staking.model.*
|
||||
import com.tangem.domain.staking.model.action.EnterAction
|
||||
import com.tangem.domain.staking.model.action.StakingActionStatus
|
||||
import com.tangem.domain.staking.model.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransaction
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransactionStatus
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransactionType
|
||||
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.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
||||
class MockStakingRepository : StakingRepository {
|
||||
override fun isStakingSupported(currencyId: String): Boolean = true
|
||||
|
||||
override suspend fun fetchEnabledYields(refresh: Boolean) { /* no-op */
|
||||
}
|
||||
|
||||
override suspend fun getEntryInfo(integrationId: String): StakingEntryInfo = StakingEntryInfo(
|
||||
interestRate = 1.toBigDecimal(),
|
||||
periodInDays = 2,
|
||||
tokenSymbol = "SOL",
|
||||
)
|
||||
|
||||
override suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield = Yield(
|
||||
id = "1",
|
||||
token = Token(
|
||||
name = "Solana",
|
||||
network = NetworkType.SOLANA,
|
||||
symbol = "SOL",
|
||||
decimals = 18,
|
||||
address = null,
|
||||
coinGeckoId = "solana",
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
tokens = listOf(),
|
||||
args = Yield.Args(
|
||||
enter = Yield.Args.Enter(
|
||||
addresses = Yield.Args.Enter.Addresses(
|
||||
address = AddressArgument(
|
||||
required = false,
|
||||
network = null,
|
||||
minimum = null,
|
||||
maximum = null,
|
||||
),
|
||||
additionalAddresses = mapOf(),
|
||||
),
|
||||
args = mapOf(),
|
||||
),
|
||||
exit = null,
|
||||
),
|
||||
status = Yield.Status(enter = false, exit = null),
|
||||
apy = 1.toBigDecimal(),
|
||||
rewardRate = 2.3,
|
||||
rewardType = Yield.RewardType.APR,
|
||||
metadata = Yield.Metadata(
|
||||
name = "Yield",
|
||||
logoUri = "",
|
||||
description = "",
|
||||
documentation = null,
|
||||
gasFeeToken = Token(
|
||||
name = "Solana",
|
||||
network = NetworkType.SOLANA,
|
||||
symbol = "SOL",
|
||||
decimals = 18,
|
||||
address = null,
|
||||
coinGeckoId = null,
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
token = Token(
|
||||
name = "Solana",
|
||||
network = NetworkType.SOLANA,
|
||||
symbol = "SOL",
|
||||
decimals = 18,
|
||||
address = null,
|
||||
coinGeckoId = null,
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
tokens = listOf(),
|
||||
type = "auto",
|
||||
rewardSchedule = "1",
|
||||
cooldownPeriod = Yield.Metadata.Period(days = 1),
|
||||
warmupPeriod = Yield.Metadata.Period(days = 1),
|
||||
rewardClaiming = "1",
|
||||
defaultValidator = null,
|
||||
minimumStake = null,
|
||||
supportsMultipleValidators = false,
|
||||
revshare = Yield.Metadata.Enabled(enabled = false),
|
||||
fee = Yield.Metadata.Enabled(enabled = false),
|
||||
),
|
||||
validators = listOf(),
|
||||
isAvailable = false,
|
||||
)
|
||||
|
||||
override suspend fun getStakingAvailabilityForActions(
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
symbol: String,
|
||||
): StakingAvailability = StakingAvailability.Unavailable
|
||||
|
||||
override suspend fun fetchSingleYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
address: CryptoCurrencyAddress,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
override fun getSingleYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
address: CryptoCurrencyAddress,
|
||||
): Flow<YieldBalance> = channelFlow {
|
||||
send(YieldBalance.Error)
|
||||
}
|
||||
|
||||
override suspend fun getSingleYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
address: CryptoCurrencyAddress,
|
||||
): YieldBalance = YieldBalance.Error
|
||||
|
||||
override suspend fun fetchMultiYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): Flow<YieldBalanceList> = channelFlow {
|
||||
send(
|
||||
YieldBalanceList.Data(
|
||||
balances = listOf(YieldBalance.Error),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceLce(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
|
||||
send(
|
||||
YieldBalanceList.Data(
|
||||
balances = listOf(YieldBalance.Error),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getMultiYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
): YieldBalanceList = YieldBalanceList.Data(
|
||||
balances = listOf(YieldBalance.Error),
|
||||
)
|
||||
|
||||
override suspend fun createEnterAction(
|
||||
integrationId: String,
|
||||
amount: BigDecimal,
|
||||
address: String,
|
||||
validatorAddress: String,
|
||||
token: Token,
|
||||
): EnterAction = EnterAction(
|
||||
id = "quis",
|
||||
integrationId = "persequeris",
|
||||
status = StakingActionStatus.PROCESSING,
|
||||
type = StakingActionType.CLAIM_REWARDS,
|
||||
currentStepIndex = 8701,
|
||||
amount = BigDecimal.ZERO,
|
||||
validatorAddress = null,
|
||||
validatorAddresses = listOf(),
|
||||
transactions = listOf(),
|
||||
createdAt = DateTime.now(),
|
||||
)
|
||||
|
||||
override suspend fun constructTransaction(transactionId: String): StakingTransaction = StakingTransaction(
|
||||
id = "id",
|
||||
network = NetworkType.SOLANA,
|
||||
status = StakingTransactionStatus.SIGNED,
|
||||
type = StakingTransactionType.FREEZE_ENERGY,
|
||||
hash = null,
|
||||
signedTransaction = null,
|
||||
unsignedTransaction = null,
|
||||
stepIndex = 9368,
|
||||
error = null,
|
||||
gasEstimate = null,
|
||||
stakeId = null,
|
||||
explorerUrl = null,
|
||||
ledgerHwAppId = null,
|
||||
isMessage = false,
|
||||
)
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ dependencies {
|
|||
implementation(projects.domain.txhistory.models)
|
||||
implementation(projects.features.swap.domain.api)
|
||||
implementation(projects.features.swap.domain.models)
|
||||
implementation(projects.domain.staking)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.utils)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
|||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.GetCardTokensListUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
|
|
@ -95,12 +96,14 @@ class SwapDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCryptoCurrencyStatusesSyncUseCase {
|
||||
return GetCryptoCurrencyStatusesSyncUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -112,11 +115,13 @@ class SwapDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
): GetCardTokensListUseCase {
|
||||
return GetCardTokensListUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ dependencies {
|
|||
implementation(projects.features.swap.domain)
|
||||
implementation(projects.features.swap.domain.api)
|
||||
implementation(projects.features.swap.domain.models)
|
||||
implementation(projects.domain.staking)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.activity.compose)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.feature.swap.di
|
||||
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.feature.swap.domain.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -23,11 +23,13 @@ class SwapPresentationModule {
|
|||
dispatcherProvider: CoroutineDispatcherProvider,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
): GetCryptoCurrencyStatusSyncUseCase {
|
||||
return GetCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
dispatchers = dispatcherProvider,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ internal object TokenDetailsPreviewData {
|
|||
onBalanceSelect = {},
|
||||
displayCryptoBalance = "966,96 XLM",
|
||||
displayFiatBalance = "91,50$",
|
||||
isBalanceSelectorEnabled = false,
|
||||
)
|
||||
val balanceError = TokenDetailsBalanceBlockState.Error(
|
||||
actionButtons = actionButtons,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ internal sealed class TokenDetailsBalanceBlockState {
|
|||
val onBalanceSelect: (TokenBalanceSegmentedButtonConfig) -> Unit,
|
||||
val displayCryptoBalance: String,
|
||||
val displayFiatBalance: String,
|
||||
val isBalanceSelectorEnabled: Boolean,
|
||||
) : TokenDetailsBalanceBlockState()
|
||||
|
||||
data class Error(
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryTransactionStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -32,21 +34,17 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
private val symbol: String,
|
||||
private val decimals: Int,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
) : Converter<TokenDetailsLoadedBalanceConverter.Data, TokenDetailsState> {
|
||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
) : Converter<Either<CurrencyStatusError, CryptoCurrencyStatus>, TokenDetailsState> {
|
||||
|
||||
private val txHistoryItemConverter by lazy {
|
||||
TokenDetailsTxHistoryTransactionStateConverter(symbol, decimals, clickIntents)
|
||||
}
|
||||
|
||||
data class Data(
|
||||
val maybeCryptoCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
val maybeYieldBalance: Either<Throwable, YieldBalance>?,
|
||||
)
|
||||
|
||||
override fun convert(value: Data): TokenDetailsState {
|
||||
return value.maybeCryptoCurrencyStatus.fold(
|
||||
override fun convert(value: Either<CurrencyStatusError, CryptoCurrencyStatus>): TokenDetailsState {
|
||||
return value.fold(
|
||||
ifLeft = { convertError() },
|
||||
ifRight = { convert(it, value.maybeYieldBalance?.getOrNull() ?: YieldBalance.Empty) },
|
||||
ifRight = { convert(it) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -64,23 +62,17 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
)
|
||||
}
|
||||
|
||||
private fun convert(status: CryptoCurrencyStatus, yieldBalance: YieldBalance): TokenDetailsState {
|
||||
private fun convert(status: CryptoCurrencyStatus): TokenDetailsState {
|
||||
val state = currentStateProvider()
|
||||
val currencyName = state.marketPriceBlockState.currencySymbol
|
||||
val pendingTxs = status.value.pendingTransactions.map(txHistoryItemConverter::convert).toPersistentList()
|
||||
val stakingCryptoAmount = (yieldBalance as? YieldBalance.Data)?.let {
|
||||
yieldBalance.balance.items.sumOf { it.amount }
|
||||
}
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) }
|
||||
|
||||
return state.copy(
|
||||
tokenBalanceBlockState = getBalanceState(
|
||||
currentState = state.tokenBalanceBlockState,
|
||||
status = status,
|
||||
stakingCryptoAmount = stakingCryptoAmount,
|
||||
stakingFiatAmount = stakingFiatAmount,
|
||||
),
|
||||
stakingBlocksState = getYieldBalance(status, yieldBalance, state),
|
||||
stakingBlocksState = getYieldBalance(status, state),
|
||||
marketPriceBlockState = getMarketPriceState(status = status.value, currencySymbol = currencyName),
|
||||
pendingTxs = pendingTxs,
|
||||
txHistoryState = if (state.txHistoryState is TxHistoryState.NotSupported) {
|
||||
|
|
@ -94,9 +86,10 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
private fun getBalanceState(
|
||||
currentState: TokenDetailsBalanceBlockState,
|
||||
status: CryptoCurrencyStatus,
|
||||
stakingCryptoAmount: BigDecimal?,
|
||||
stakingFiatAmount: BigDecimal?,
|
||||
): TokenDetailsBalanceBlockState {
|
||||
val stakingCryptoAmount = (status.value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance()
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) }
|
||||
val isBalanceSelectorEnabled = stakingFeatureToggles.isStakingEnabled && !stakingCryptoAmount.isNullOrZero()
|
||||
return when (status.value) {
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
|
|
@ -120,6 +113,7 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig,
|
||||
onBalanceSelect = clickIntents::onBalanceSelect,
|
||||
selectedBalanceType = currentState.selectedBalanceType,
|
||||
isBalanceSelectorEnabled = isBalanceSelectorEnabled,
|
||||
)
|
||||
is CryptoCurrencyStatus.Loading -> TokenDetailsBalanceBlockState.Loading(
|
||||
actionButtons = currentState.actionButtons,
|
||||
|
|
@ -137,19 +131,14 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getYieldBalance(
|
||||
status: CryptoCurrencyStatus,
|
||||
yieldBalance: YieldBalance,
|
||||
state: TokenDetailsState,
|
||||
): StakingBlocksState {
|
||||
val stakingCryptoAmount = (yieldBalance as? YieldBalance.Data)?.let {
|
||||
yieldBalance.balance.items.sumOf { it.amount }
|
||||
}
|
||||
private fun getYieldBalance(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlocksState {
|
||||
val yieldBalance = status.value.yieldBalance as? YieldBalance.Data
|
||||
|
||||
val stakingCryptoAmount = yieldBalance?.getTotalStakingBalance()
|
||||
val stakingRewardAmount = yieldBalance?.getRewardStakingBalance()
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) }
|
||||
val stakingRewardAmount = (yieldBalance as? YieldBalance.Data)?.let {
|
||||
yieldBalance.balance.items.sumOf { it.amount.multiply(it.pricePerShare) }
|
||||
}
|
||||
val stakingBalance = if (stakingCryptoAmount == null) {
|
||||
|
||||
val stakingBalance = if (stakingCryptoAmount.isNullOrZero()) {
|
||||
StakingBalance.Empty
|
||||
} else {
|
||||
StakingBalance.Content(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
|
|
@ -32,6 +31,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.t
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -46,6 +46,7 @@ internal class TokenDetailsStateFactory(
|
|||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val featureToggles: TokenDetailsFeatureToggles,
|
||||
stakingFeatureToggles: StakingFeatureToggles,
|
||||
symbol: String,
|
||||
decimals: Int,
|
||||
) {
|
||||
|
|
@ -68,6 +69,7 @@ internal class TokenDetailsStateFactory(
|
|||
symbol = symbol,
|
||||
decimals = decimals,
|
||||
clickIntents = clickIntents,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -121,11 +123,8 @@ internal class TokenDetailsStateFactory(
|
|||
|
||||
fun getCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
yieldBalanceEither: Either<Throwable, YieldBalance>?,
|
||||
): TokenDetailsState {
|
||||
return tokenDetailsLoadedBalanceConverter.convert(
|
||||
TokenDetailsLoadedBalanceConverter.Data(cryptoCurrencyEither, yieldBalanceEither),
|
||||
)
|
||||
return tokenDetailsLoadedBalanceConverter.convert(cryptoCurrencyEither)
|
||||
}
|
||||
|
||||
fun getManageButtonsState(actions: List<TokenActionsState.ActionState>): TokenDetailsState {
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
|
|||
modifier = itemModifier,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
state = state.tokenBalanceBlockState,
|
||||
isStakingAvailable = state.isStakingBlockShown,
|
||||
)
|
||||
}
|
||||
items(
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
internal fun TokenDetailsBalanceBlock(
|
||||
state: TokenDetailsBalanceBlockState,
|
||||
isBalanceHidden: Boolean,
|
||||
isStakingAvailable: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
|
|
@ -54,7 +53,7 @@ internal fun TokenDetailsBalanceBlock(
|
|||
.weight(1f)
|
||||
.padding(top = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
if (isStakingAvailable) BalanceButtons(state)
|
||||
BalanceButtons(state)
|
||||
}
|
||||
FiatBalance(
|
||||
state = state,
|
||||
|
|
@ -138,7 +137,7 @@ private fun CryptoBalance(
|
|||
|
||||
@Composable
|
||||
private fun BalanceButtons(state: TokenDetailsBalanceBlockState) {
|
||||
if (state !is TokenDetailsBalanceBlockState.Content) return
|
||||
if (state !is TokenDetailsBalanceBlockState.Content || !state.isBalanceSelectorEnabled) return
|
||||
|
||||
SegmentedButtons(
|
||||
config = state.balanceSegmentedButtonConfig,
|
||||
|
|
@ -172,7 +171,7 @@ private fun Preview_TokenDetailsBalanceBlock(
|
|||
@PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TokenDetailsBalanceBlock(state = state, isBalanceHidden = false, isStakingAvailable = true)
|
||||
TokenDetailsBalanceBlock(state = state, isBalanceHidden = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import com.tangem.domain.redux.ReduxStateHolder
|
|||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.GetStakingYieldBalanceUseCase
|
||||
import com.tangem.domain.staking.GetYieldUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.tokens.*
|
||||
|
|
@ -108,7 +107,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase,
|
||||
private val getYieldUseCase: GetYieldUseCase,
|
||||
private val getStakingYieldBalanceUseCase: GetStakingYieldBalanceUseCase,
|
||||
private val swapRepository: SwapRepository,
|
||||
private val swapTransactionRepository: SwapTransactionRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
|
|
@ -135,7 +133,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
?: error("This screen can't open without `CryptoCurrency`")
|
||||
|
||||
private val userWallet: UserWallet
|
||||
private var stakingIntegrationId: String? = null
|
||||
|
||||
lateinit var router: InnerTokenDetailsRouter
|
||||
|
||||
|
|
@ -157,6 +154,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
symbol = cryptoCurrency.symbol,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
featureToggles = tokenDetailsFeatureToggles,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
)
|
||||
|
||||
private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -220,6 +218,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateContent() {
|
||||
subscribeOnCurrencyStatusUpdates()
|
||||
subscribeOnExchangeTransactionsUpdates()
|
||||
updateTxHistory(refresh = false, showItemsLoading = true)
|
||||
|
||||
|
|
@ -281,36 +280,12 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
)
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeCurrencyStatus ->
|
||||
maybeCurrencyStatus.fold(
|
||||
ifLeft = {
|
||||
internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(
|
||||
maybeCurrencyStatus,
|
||||
null,
|
||||
)
|
||||
},
|
||||
ifRight = { status ->
|
||||
cryptoCurrencyStatus = status
|
||||
|
||||
if (stakingIntegrationId != null) {
|
||||
getStakingYieldBalanceUseCase(
|
||||
userWalletId = userWalletId,
|
||||
address = status.value.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||
integrationId = stakingIntegrationId.orEmpty(),
|
||||
).onEach { maybeYieldBalance ->
|
||||
internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(
|
||||
maybeCurrencyStatus,
|
||||
maybeYieldBalance,
|
||||
)
|
||||
}.launchIn(viewModelScope)
|
||||
} else {
|
||||
internalUiState.value =
|
||||
stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus, null)
|
||||
}
|
||||
|
||||
updateButtons(currencyStatus = status)
|
||||
updateWarnings(status)
|
||||
},
|
||||
)
|
||||
internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus)
|
||||
maybeCurrencyStatus.onRight { status ->
|
||||
cryptoCurrencyStatus = status
|
||||
updateButtons(currencyStatus = status)
|
||||
updateWarnings(status)
|
||||
}
|
||||
currencyStatusAnalyticsSender.send(maybeCurrencyStatus)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
|
|
@ -411,12 +386,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
)
|
||||
internalUiState.value = stateFactory.getStateWithUpdatedStakingAvailability(stakingAvailability)
|
||||
if (stakingAvailability is StakingAvailability.Available) {
|
||||
stakingIntegrationId = stakingAvailability.integrationId
|
||||
val stakingInfo = getStakingEntryInfoUseCase(stakingAvailability.integrationId)
|
||||
subscribeOnCurrencyStatusUpdates()
|
||||
internalUiState.value = stateFactory.getStateWithStaking(stakingInfo)
|
||||
} else {
|
||||
subscribeOnCurrencyStatusUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
|
|||
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenItemStateConverter(
|
||||
|
|
@ -61,13 +63,16 @@ internal class TokenItemStateConverter(
|
|||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
|
||||
val yieldBalance = (value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
val amount = value.amount?.plus(yieldBalance) ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatAmount = value.fiatAmount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
|
||||
val yieldBalance = (value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
val fiatYieldBalance = value.fiatRate?.times(yieldBalance).orZero()
|
||||
val fiatAmount = value.fiatAmount?.plus(fiatYieldBalance) ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue