Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-24 15:54:44 +05:00
parent fa125539b4
commit 96e30660e8
7 changed files with 116 additions and 21 deletions

View file

@ -4,10 +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.YieldSupplyTransactionRepository
import com.tangem.domain.yield.supply.usecase.YieldSupplyEstimateEnterFeeUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetContractAddressUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyStartEarningUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyStopEarningUseCase
import com.tangem.domain.yield.supply.usecase.*
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -59,7 +56,16 @@ internal object YieldSupplyDomainModule {
): YieldSupplyGetContractAddressUseCase {
return YieldSupplyGetContractAddressUseCase(
yieldSupplyTransactionRepository = yieldSupplyTransactionRepository,
)
}
@Provides
@Singleton
fun provideYieldSupplyGetProtocolBalanceUseCase(
yieldSupplyTransactionRepository: YieldSupplyTransactionRepository,
): YieldSupplyGetProtocolBalanceUseCase {
return YieldSupplyGetProtocolBalanceUseCase(
yieldSupplyTransactionRepository = yieldSupplyTransactionRepository,
)
}
}

View file

@ -89,6 +89,25 @@ internal class DefaultYieldSupplyTransactionRepository(
)
}
override suspend fun getProtocolBalance(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): BigDecimal? =
withContext(dispatchers.io) {
require(cryptoCurrency is CryptoCurrency.Token)
runCatching {
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = cryptoCurrency.network.toBlockchain(),
derivationPath = cryptoCurrency.network.derivationPath.value,
) ?: error("Wallet manager not found")
walletManager.getProtocolBalance(
token = Token(
symbol = cryptoCurrency.symbol,
contractAddress = cryptoCurrency.contractAddress,
decimals = cryptoCurrency.decimals,
),
)
}.onFailure(Timber::e).getOrNull()
}
@Suppress("LongParameterList")
private fun buildEnterTransactions(
walletManager: WalletManager,

View file

@ -22,4 +22,6 @@ interface YieldSupplyTransactionRepository {
): TransactionData.Uncompiled
suspend fun getYieldContractAddress(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): String?
suspend fun getProtocolBalance(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): BigDecimal?
}

View file

@ -0,0 +1,24 @@
package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
import java.math.BigDecimal
class YieldSupplyGetProtocolBalanceUseCase(
private val yieldSupplyTransactionRepository: YieldSupplyTransactionRepository,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
): Either<Throwable, BigDecimal?> = Either.catch {
requireNotNull(cryptoCurrency as CryptoCurrency.Token)
yieldSupplyTransactionRepository.getProtocolBalance(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
)
}
}

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.TextReference
internal data class YieldSupplyActiveContentUM(
val totalEarnings: TextReference,
val availableBalance: TextReference,
val availableBalance: TextReference?,
val providerTitle: TextReference,
val subtitle: TextReference,
val subtitleLink: TextReference,

View file

@ -10,17 +10,20 @@ 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.yield.supply.usecase.YieldSupplyGetProtocolBalanceUseCase
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveComponent
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@ModelScoped
internal class YieldSupplyActiveModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val yieldSupplyGetProtocolBalanceUseCase: YieldSupplyGetProtocolBalanceUseCase,
) : Model() {
private val params: YieldSupplyActiveComponent.Params = paramsContainer.require()
@ -32,11 +35,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
field = MutableStateFlow(
YieldSupplyActiveContentUM(
totalEarnings = stringReference("0"),
availableBalance = stringReference(
cryptoCurrencyStatusFlow.value.value.amount.format {
crypto(cryptoCurrency = cryptoCurrencyStatusFlow.value.currency)
},
),
availableBalance = null,
providerTitle = resourceReference(R.string.yield_module_provider),
subtitle = resourceReference(
id = R.string.yield_module_earn_sheet_provider_description,
@ -49,10 +48,31 @@ internal class YieldSupplyActiveModel @Inject constructor(
init {
subscribeOnCurrencyUpdates()
modelScope.launch {
val protocolBalance = yieldSupplyGetProtocolBalanceUseCase(
userWalletId = params.userWallet.walletId,
cryptoCurrency = cryptoCurrency,
).getOrNull()
stringReference(
protocolBalance.format {
crypto(
symbol = AAVEV3_PREFIX + cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)
},
)
}
}
private fun subscribeOnCurrencyUpdates() {
cryptoCurrencyStatusFlow.onEach { cryptoCurrencyStatus ->
val protocolBalance = yieldSupplyGetProtocolBalanceUseCase(
userWalletId = params.userWallet.walletId,
cryptoCurrency = cryptoCurrency,
).getOrNull()
val approvalNotification = if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isAllowedToSpend != true) {
NotificationUM.Error(
title = resourceReference(R.string.yield_module_approve_needed_notification_title),
@ -67,8 +87,24 @@ internal class YieldSupplyActiveModel @Inject constructor(
null
}
uiState.update { it.copy(notificationUM = approvalNotification) }
uiState.update {
it.copy(
notificationUM = approvalNotification,
availableBalance = stringReference(
protocolBalance.format {
crypto(
symbol = AAVEV3_PREFIX + cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)
},
),
)
}
}.flowOn(dispatchers.default)
.launchIn(modelScope)
}
private companion object {
const val AAVEV3_PREFIX = "a"
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.yield.supply.impl.subcomponents.active.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@ -22,10 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
@ -152,7 +150,7 @@ private fun DescriptionText(state: YieldSupplyActiveContentUM) {
}
@Composable
private fun InfoRow(title: TextReference, info: TextReference) {
private fun InfoRow(title: TextReference, info: TextReference?) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.padding(horizontal = 4.dp, vertical = 12.dp),
@ -163,11 +161,21 @@ private fun InfoRow(title: TextReference, info: TextReference) {
color = TangemTheme.colors.text.primary1,
)
SpacerWMax()
Text(
text = info.resolveReference(),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
)
AnimatedContent(info) { currentInfo ->
if (currentInfo != null) {
Text(
text = currentInfo.resolveReference(),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
)
} else {
TextShimmer(
text = title.resolveReference(),
style = TangemTheme.typography.body1,
)
}
}
}
}