Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-07 16:44:31 +05:00
parent 1ed65c48cc
commit e38f1e0a05
16 changed files with 103 additions and 52 deletions

View file

@ -16,7 +16,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.*
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSyncUseCase
@ -454,21 +454,17 @@ internal object WalletsDomainModule {
@Provides
@Singleton
fun provideYieldSupplyApyFlowUseCase(
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
): YieldSupplyApyFlowUseCase {
fun provideYieldSupplyApyFlowUseCase(yieldSupplyRepository: YieldSupplyRepository): YieldSupplyApyFlowUseCase {
return YieldSupplyApyFlowUseCase(
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
yieldSupplyRepository = yieldSupplyRepository,
)
}
@Provides
@Singleton
fun provideYieldSupplyApyUpdateUseCase(
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
): YieldSupplyApyUpdateUseCase {
fun provideYieldSupplyApyUpdateUseCase(yieldSupplyRepository: YieldSupplyRepository): YieldSupplyApyUpdateUseCase {
return YieldSupplyApyUpdateUseCase(
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
yieldSupplyRepository = yieldSupplyRepository,
)
}
}

View file

@ -4,7 +4,7 @@ import com.tangem.domain.blockaid.BlockAidGasEstimate
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.error.FeeErrorResolver
import com.tangem.domain.yield.supply.YieldSupplyErrorResolver
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
import com.tangem.domain.yield.supply.usecase.*
import dagger.Module
@ -82,30 +82,36 @@ internal object YieldSupplyDomainModule {
@Provides
@Singleton
fun provideYieldSupplyGetTokenStatusUseCase(
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
yieldSupplyRepository: YieldSupplyRepository,
): YieldSupplyGetTokenStatusUseCase {
return YieldSupplyGetTokenStatusUseCase(
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
yieldSupplyRepository = yieldSupplyRepository,
)
}
@Provides
@Singleton
fun provideYieldSupplyGetApyUseCase(
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
): YieldSupplyGetApyUseCase {
fun provideYieldSupplyGetApyUseCase(yieldSupplyRepository: YieldSupplyRepository): YieldSupplyGetApyUseCase {
return YieldSupplyGetApyUseCase(
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
yieldSupplyRepository = yieldSupplyRepository,
)
}
@Provides
@Singleton
fun provideYieldSupplyGetChartUseCase(
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
): YieldSupplyGetChartUseCase {
fun provideYieldSupplyGetChartUseCase(yieldSupplyRepository: YieldSupplyRepository): YieldSupplyGetChartUseCase {
return YieldSupplyGetChartUseCase(
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
yieldSupplyRepository = yieldSupplyRepository,
)
}
@Provides
@Singleton
fun provideYieldSupplyIsAvailableUseCase(
yieldSupplyRepository: YieldSupplyRepository,
): YieldSupplyIsAvailableUseCase {
return YieldSupplyIsAvailableUseCase(
yieldSupplyRepository = yieldSupplyRepository,
)
}
}

View file

@ -1,7 +1,9 @@
package com.tangem.data.yield.supply
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.yieldsupply.YieldSupplyProvider
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
@ -10,7 +12,9 @@ import com.tangem.datasource.api.tangemTech.YieldSupplyApi
import com.tangem.data.yield.supply.converters.YieldTokenStatusConverter
import com.tangem.data.yield.supply.converters.YieldTokenChartConverter
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldMarketToken
import com.tangem.domain.yield.supply.models.YieldMarketTokenStatus
import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData
@ -20,11 +24,12 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import kotlin.collections.map
internal class DefaultYieldSupplyMarketRepository(
internal class DefaultYieldSupplyRepository(
private val yieldSupplyApi: YieldSupplyApi,
private val store: YieldMarketsStore,
private val walletManagersFacade: WalletManagersFacade,
private val dispatchers: CoroutineDispatcherProvider,
) : YieldSupplyMarketRepository {
) : YieldSupplyRepository {
override suspend fun getCachedMarkets(): List<YieldMarketToken>? = withContext(dispatchers.io) {
val cache = store.getSyncOrNull().orEmpty()
@ -57,6 +62,17 @@ internal class DefaultYieldSupplyMarketRepository(
return YieldTokenChartConverter.convert(response)
}
override suspend fun isYieldSupplySupported(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean =
withContext(dispatchers.io) {
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = cryptoCurrency.network.toBlockchain(),
derivationPath = cryptoCurrency.network.derivationPath.value,
) ?: error("Wallet manager not found")
(walletManager as? YieldSupplyProvider)?.isSupported() ?: false
}
private fun List<YieldMarketToken>.enrichNetworkIds(): List<YieldMarketToken> {
val chainIdMap = Blockchain.entries.associate { it.getChainId() to it.toNetworkId() }
return this.map { token ->

View file

@ -1,12 +1,12 @@
package com.tangem.data.yield.supply.di
import com.tangem.data.yield.supply.DefaultYieldSupplyMarketRepository
import com.tangem.data.yield.supply.DefaultYieldSupplyRepository
import com.tangem.data.yield.supply.DefaultYieldSupplyErrorResolver
import com.tangem.data.yield.supply.DefaultYieldSupplyTransactionRepository
import com.tangem.datasource.api.tangemTech.YieldSupplyApi
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.YieldSupplyErrorResolver
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -37,12 +37,14 @@ internal object YieldSupplyDataModule {
fun provideYieldSupplyMarketRepository(
yieldSupplyApi: YieldSupplyApi,
store: YieldMarketsStore,
walletManagersFacade: WalletManagersFacade,
dispatchers: CoroutineDispatcherProvider,
): YieldSupplyMarketRepository {
return DefaultYieldSupplyMarketRepository(
): YieldSupplyRepository {
return DefaultYieldSupplyRepository(
yieldSupplyApi = yieldSupplyApi,
store = store,
dispatchers = dispatchers,
walletManagersFacade = walletManagersFacade,
)
}

View file

@ -1,12 +1,13 @@
package com.tangem.domain.yield.supply
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.yield.supply.models.YieldMarketToken
import com.tangem.domain.yield.supply.models.YieldMarketTokenStatus
import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData
import kotlinx.coroutines.flow.Flow
interface YieldSupplyMarketRepository {
interface YieldSupplyRepository {
/**
* Get cached yield markets or null if nothing cached yet.
@ -35,4 +36,6 @@ interface YieldSupplyMarketRepository {
*/
@Throws
suspend fun getTokenChart(cryptoCurrencyToken: CryptoCurrency.Token): YieldSupplyMarketChartData
suspend fun isYieldSupplySupported(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean
}

View file

@ -1,6 +1,6 @@
package com.tangem.domain.yield.supply.usecase
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.map
* - value: APY as string
*/
class YieldSupplyApyFlowUseCase(
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
private val yieldSupplyRepository: YieldSupplyRepository,
) {
operator fun invoke(): Flow<Map<String, String>> {
return yieldSupplyMarketRepository.getMarketsFlow()
return yieldSupplyRepository.getMarketsFlow()
.map { yieldMarketTokenList ->
yieldMarketTokenList.filter { it.isActive }.associate { token ->
token.yieldSupplyKey to token.apy.toString()

View file

@ -1,7 +1,7 @@
package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import kotlin.collections.filter
/**
@ -12,11 +12,11 @@ import kotlin.collections.filter
* - value: APY as string
*/
class YieldSupplyApyUpdateUseCase(
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
private val yieldSupplyRepository: YieldSupplyRepository,
) {
suspend operator fun invoke(): Either<Throwable, Map<String, String>> = Either.catch {
yieldSupplyMarketRepository.updateMarkets()
yieldSupplyRepository.updateMarkets()
.filter { it.isActive }
.associate {
it.tokenAddress to it.apy.toString()

View file

@ -1,14 +1,14 @@
package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
class YieldSupplyGetApyUseCase(
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
private val yieldSupplyRepository: YieldSupplyRepository,
) {
suspend operator fun invoke(tokenAddress: String): Either<Throwable, String> = Either.catch {
val apys = yieldSupplyMarketRepository.getCachedMarkets() ?: yieldSupplyMarketRepository.updateMarkets()
val apys = yieldSupplyRepository.getCachedMarkets() ?: yieldSupplyRepository.updateMarkets()
apys.first { it.tokenAddress == tokenAddress }.apy.toString()
}
}

View file

@ -2,15 +2,15 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData
class YieldSupplyGetChartUseCase(
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
private val yieldSupplyRepository: YieldSupplyRepository,
) {
suspend operator fun invoke(cryptoCurrency: CryptoCurrency.Token): Either<Throwable, YieldSupplyMarketChartData> =
Either.catch {
yieldSupplyMarketRepository.getTokenChart(cryptoCurrency)
yieldSupplyRepository.getTokenChart(cryptoCurrency)
}
}

View file

@ -2,14 +2,14 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldMarketTokenStatus
class YieldSupplyGetTokenStatusUseCase(
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
private val yieldSupplyRepository: YieldSupplyRepository,
) {
suspend operator fun invoke(token: CryptoCurrency.Token): Either<Throwable, YieldMarketTokenStatus> = Either.catch {
yieldSupplyMarketRepository.getTokenStatus(token)
yieldSupplyRepository.getTokenStatus(token)
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.domain.yield.supply.usecase
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.yield.supply.YieldSupplyRepository
class YieldSupplyIsAvailableUseCase(
private val yieldSupplyRepository: YieldSupplyRepository,
) {
suspend operator fun invoke(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean {
return yieldSupplyRepository.isYieldSupplySupported(userWalletId, cryptoCurrency)
}
}

View file

@ -6,7 +6,9 @@ import com.tangem.core.ui.extensions.TextReference
@Immutable
internal sealed class YieldSupplyUM {
data class Initial(
data object Initial : YieldSupplyUM()
data class Available(
val title: TextReference,
val onClick: () -> Unit,
) : YieldSupplyUM()

View file

@ -17,6 +17,7 @@ import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyIsAvailableUseCase
import com.tangem.features.yield.supply.api.YieldSupplyComponent
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
import com.tangem.features.yield.supply.impl.main.entity.LoadingStatusMode
@ -45,12 +46,13 @@ internal class YieldSupplyModel @Inject constructor(
@DelayedWork private val coroutineScope: CoroutineScope,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase,
private val yieldSupplyIsAvailableUseCase: YieldSupplyIsAvailableUseCase,
) : Model(), YieldSupplyClickIntents {
private val params = paramsContainer.require<YieldSupplyComponent.Params>()
val uiState: StateFlow<YieldSupplyUM>
field = MutableStateFlow<YieldSupplyUM>(YieldSupplyUM.Loading)
field = MutableStateFlow<YieldSupplyUM>(YieldSupplyUM.Initial)
val bottomSheetNavigation: SlotNavigation<Unit> = SlotNavigation()
@ -69,8 +71,17 @@ internal class YieldSupplyModel @Inject constructor(
field = MutableStateFlow(false)
init {
subscribeOnCurrencyStatusUpdates()
subscribeOnBalanceHidden()
checkIfYieldSupplyIsAvailable()
}
private fun checkIfYieldSupplyIsAvailable() {
modelScope.launch(dispatchers.io) {
val isAvailable = yieldSupplyIsAvailableUseCase(params.userWalletId, params.cryptoCurrency)
if (isAvailable) {
subscribeOnCurrencyStatusUpdates()
subscribeOnBalanceHidden()
}
}
}
private fun subscribeOnCurrencyStatusUpdates() {

View file

@ -20,7 +20,7 @@ internal class YieldSupplyTokenStatusSuccessTransformer(
return when (mode) {
LoadingStatusMode.Initial -> {
YieldSupplyUM.Initial(
YieldSupplyUM.Available(
title = resourceReference(
id = R.string.yield_module_token_details_earn_notification_title,
formatArgs = wrappedList(tokenStatus.apy),

View file

@ -44,7 +44,7 @@ internal fun YieldSupplyBlockContent(
modifier = modifier,
) { supplyUM ->
when (supplyUM) {
is YieldSupplyUM.Initial -> SupplyInitial(supplyUM)
is YieldSupplyUM.Available -> SupplyAvailable(supplyUM)
YieldSupplyUM.Loading -> SupplyLoading()
is YieldSupplyUM.Content -> SupplyContent(supplyUM, isBalanceHidden)
YieldSupplyUM.Processing.Enter -> SupplyProcessing(
@ -54,12 +54,13 @@ internal fun YieldSupplyBlockContent(
resourceReference(R.string.yield_module_stop_earning),
)
YieldSupplyUM.Unavailable -> SupplyUnavailable()
YieldSupplyUM.Initial -> {}
}
}
}
@Composable
private fun SupplyInitial(supplyUM: YieldSupplyUM.Initial) {
private fun SupplyAvailable(supplyUM: YieldSupplyUM.Available) {
SupplyInfo(
title = supplyUM.title,
subtitle = resourceReference(R.string.yield_module_token_details_earn_notification_description),
@ -292,7 +293,7 @@ private fun YieldSupplyBlockContent_Preview(@PreviewParameter(PreviewProvider::c
private class PreviewProvider : PreviewParameterProvider<YieldSupplyUM> {
override val values: Sequence<YieldSupplyUM>
get() = sequenceOf(
YieldSupplyUM.Initial(
YieldSupplyUM.Available(
title = TextReference.Res(
R.string.yield_module_token_details_earn_notification_title,
wrappedList("5.1"),

View file

@ -5,7 +5,7 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
tangemBlockchainSdk = "develop-1254"
tangemBlockchainSdk = "develop-1256"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-564"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^