Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-10 16:33:07 +04:00
parent 2f2d6755f9
commit 5f4dfefc2e
20 changed files with 190 additions and 311 deletions

View file

@ -43,6 +43,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.appCurrency.models)
implementation(projects.domain.appCurrency)
implementation(projects.domain.account.status)
implementation(projects.domain.wallets.models)
implementation(projects.domain.wallets)
implementation(projects.domain.tokens.models)

View file

@ -16,12 +16,13 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.usecase.*
import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent
@ -55,7 +56,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val urlOpener: UrlOpener,
private val appRouter: AppRouter,
private val yieldSupplyGetDustMinAmountUseCase: YieldSupplyGetDustMinAmountUseCase,
@ -168,44 +169,44 @@ internal class YieldSupplyActiveModel @Inject constructor(
ifRight = { wallet ->
userWallet = wallet
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = params.userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = false,
).onEach { maybeCryptoCurrency ->
maybeCryptoCurrency.fold(
ifRight = { cryptoCurrencyStatus ->
cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus }
singleAccountStatusListSupplier(params.userWalletId)
.map { it.getCryptoCurrencyStatus(currency = cryptoCurrency) }
.distinctUntilChanged()
.onEach { maybeCryptoCurrency ->
maybeCryptoCurrency.fold(
ifSome = { cryptoCurrencyStatus ->
cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus }
val protocolBalance =
cryptoCurrencyStatus.value.yieldSupplyStatus?.effectiveProtocolBalance
?: yieldSupplyGetProtocolBalanceUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
).getOrNull()
val protocolBalance =
cryptoCurrencyStatus.value.yieldSupplyStatus?.effectiveProtocolBalance
?: yieldSupplyGetProtocolBalanceUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
).getOrNull()
loadApy()
loadMinAmount()
loadFees()
loadApy()
loadMinAmount()
loadFees()
uiState.update {
it.copy(
availableBalance = stringReference(
protocolBalance.format {
crypto(
symbol = AAVEV3_PREFIX + cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)
},
),
)
}
},
ifLeft = {
Timber.w(it.toString())
},
)
}.flowOn(dispatchers.default)
uiState.update {
it.copy(
availableBalance = stringReference(
protocolBalance.format {
crypto(
symbol = AAVEV3_PREFIX + cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)
},
),
)
}
},
ifEmpty = {
Timber.w("No currency status found: ${cryptoCurrency.id}")
},
)
}
.flowOn(dispatchers.default)
.launchIn(modelScope)
},
ifLeft = { error ->

View file

@ -5,9 +5,10 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusUseCase
import com.tangem.features.yield.supply.api.YieldSupplyEntryComponent
@ -24,7 +25,7 @@ internal class YieldSupplyEntryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val yieldSupplyEnterStatusUseCase: YieldSupplyEnterStatusUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
) : Model() {
private val params = paramsContainer.require<YieldSupplyEntryComponent.Params>()
@ -39,22 +40,22 @@ internal class YieldSupplyEntryModel @Inject constructor(
val userWalletId = params.userWalletId
val cryptoCurrency = params.cryptoCurrency
modelScope.launch(dispatchers.default) {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
).onLeft { error ->
Timber.e("Failed to get CryptoCurrencyStatus: $error")
withContext(dispatchers.mainImmediate) {
router.pop()
}
}.onRight { cryptoCurrencyStatus ->
withContext(dispatchers.mainImmediate) {
val route = getInitialRoute(cryptoCurrencyStatus)
if (route != null) {
router.replaceCurrent(route)
singleAccountStatusListSupplier.getSyncOrNull(userWalletId)
.getCryptoCurrencyStatus(currency = cryptoCurrency)
.onNone {
Timber.e("Failed to get CryptoCurrencyStatus: ${cryptoCurrency.id}")
withContext(dispatchers.mainImmediate) {
router.pop()
}
}
.onSome { cryptoCurrencyStatus ->
withContext(dispatchers.mainImmediate) {
val route = getInitialRoute(cryptoCurrencyStatus)
if (route != null) {
router.replaceCurrent(route)
}
}
}
}
}
}

View file

@ -11,6 +11,8 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource
@ -21,7 +23,6 @@ import com.tangem.domain.models.currency.shouldShowNotSuppliedNotification
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.models.YieldSupplyPendingStatus
import com.tangem.domain.yield.supply.usecase.*
@ -35,9 +36,9 @@ import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlin.properties.Delegates
import java.util.concurrent.atomic.AtomicBoolean
@Suppress("LongParameterList", "LargeClass")
@ModelScoped
@ -48,7 +49,7 @@ internal class YieldSupplyModel @Inject constructor(
private val appRouter: AppRouter,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase,
private val yieldSupplyIsAvailableUseCase: YieldSupplyIsAvailableUseCase,
@ -109,32 +110,33 @@ internal class YieldSupplyModel @Inject constructor(
private fun subscribeOnCurrencyStatusUpdates() {
combine(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = params.userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = false,
),
yieldSupplyEnterStatusFlowUseCase(
flow = singleAccountStatusListSupplier(params.userWalletId).map {
it.getCryptoCurrencyStatus(currency = cryptoCurrency)
},
flow2 = yieldSupplyEnterStatusFlowUseCase(
userWalletId = params.userWalletId,
cryptoCurrency = cryptoCurrency,
),
) { maybeCryptoCurrency, _ ->
maybeCryptoCurrency
}.flowOn(dispatchers.io)
}
.flowOn(dispatchers.io)
.distinctUntilChanged()
.onEach { maybeCryptoCurrency ->
maybeCryptoCurrency.fold(
ifRight = { cryptoCurrencyStatus ->
ifSome = { cryptoCurrencyStatus ->
latestCryptoCurrencyStatus = cryptoCurrencyStatus
if (isFirstCryptoCurrencyStatusEmission.compareAndSet(true, false)) {
sendInfoAboutProtocolStatus(cryptoCurrencyStatus)
}
onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus)
},
ifLeft = {
Timber.w(it.toString())
ifEmpty = {
Timber.w("Unable to get crypto currency status: ${cryptoCurrency.id}")
},
)
}.launchIn(modelScope)
}
.launchIn(modelScope)
}
private suspend fun loadTokenStatus() {

View file

@ -13,13 +13,14 @@ import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIco
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -55,7 +56,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
paramsContainer: ParamsContainer,
private val analytics: AnalyticsEventHandler,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val yieldSupplyStartEarningUseCase: YieldSupplyStartEarningUseCase,
@ -346,27 +347,27 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
}
private fun getCurrenciesStatusUpdates() {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = false,
).onEach { maybeCryptoCurrency ->
maybeCryptoCurrency.fold(
ifRight = { cryptoCurrencyStatus ->
onDataLoaded(
currencyStatus = cryptoCurrencyStatus,
feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
).getOrNull() ?: cryptoCurrencyStatus,
)
},
ifLeft = {
Timber.w(it.toString())
showAlertError()
},
)
}.launchIn(modelScope)
singleAccountStatusListSupplier(params.userWalletId)
.map { it.getCryptoCurrencyStatus(currency = cryptoCurrency) }
.distinctUntilChanged()
.onEach { maybeCryptoCurrency ->
maybeCryptoCurrency.fold(
ifSome = { cryptoCurrencyStatus ->
onDataLoaded(
currencyStatus = cryptoCurrencyStatus,
feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
).getOrNull() ?: cryptoCurrencyStatus,
)
},
ifEmpty = {
Timber.w("Unable to get crypto currency status: ${cryptoCurrency.id}")
showAlertError()
},
)
}
.launchIn(modelScope)
}
private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus) {