Updated on 2026-08-14
This commit is contained in:
commit
0cc3e8e4e4
13 changed files with 74 additions and 48 deletions
|
|
@ -9,6 +9,7 @@ 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.core.ui.format.bigdecimal.shorted
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -399,11 +400,16 @@ sealed class NotificationUM(val config: NotificationConfig) {
|
|||
|
||||
data class RentExemptionDestination(
|
||||
private val rentExemptionAmount: BigDecimal,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
) : Error(
|
||||
title = TextReference.Res(R.string.send_notification_invalid_amount_title),
|
||||
subtitle = TextReference.Res(
|
||||
id = R.string.send_notification_invalid_amount_rent_destination,
|
||||
formatArgs = wrappedList(rentExemptionAmount),
|
||||
formatArgs = wrappedList(
|
||||
rentExemptionAmount.format {
|
||||
crypto(cryptoCurrency)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ object NotificationsFactory {
|
|||
is BlockchainSdkError.DestinationTagRequired -> addRequireDestinationTagErrorNotification()
|
||||
is BlockchainSdkError.Solana.DestinationRentExemption -> addRentExemptionDestinationNotification(
|
||||
rentExemptionAmount = validationError.rentAmount,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
)
|
||||
is BlockchainSdkError.TransactionDustChangeError -> add(
|
||||
NotificationUM.Error.MinimumAmountError(
|
||||
|
|
@ -462,10 +463,14 @@ object NotificationsFactory {
|
|||
add(NotificationUM.Solana.RentInfo(rentWarning))
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addRentExemptionDestinationNotification(rentExemptionAmount: BigDecimal) {
|
||||
private fun MutableList<NotificationUM>.addRentExemptionDestinationNotification(
|
||||
rentExemptionAmount: BigDecimal,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
add(
|
||||
NotificationUM.Solana.RentExemptionDestination(
|
||||
rentExemptionAmount = rentExemptionAmount,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.currency.yieldSupplyKey
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
|
@ -185,6 +186,7 @@ class TokenItemStateConverter(
|
|||
}
|
||||
}
|
||||
|
||||
// polygon-pos_0xc2132d05d31c914a87c6611c10748aeb04b58e8f
|
||||
private fun resolveEarnApy(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
yieldModuleApyMap: Map<String, String>,
|
||||
|
|
@ -192,7 +194,12 @@ class TokenItemStateConverter(
|
|||
): Pair<TextReference?, Boolean> {
|
||||
val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token
|
||||
if (token != null && yieldModuleApyMap.isNotEmpty()) {
|
||||
val yieldSupplyApy = yieldModuleApyMap[token.yieldSupplyKey()]
|
||||
val yieldSupplyApy = yieldModuleApyMap.entries.firstOrNull {
|
||||
it.key.equals(
|
||||
other = token.yieldSupplyKey(),
|
||||
ignoreCase = BlockchainUtils.isCaseInsensitiveContractAddress(token.network.rawId),
|
||||
)
|
||||
}?.value
|
||||
if (yieldSupplyApy != null) {
|
||||
val isActive = cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive ?: false
|
||||
return resourceReference(
|
||||
|
|
|
|||
|
|
@ -1484,7 +1484,7 @@
|
|||
<string name="yield_module_fee_policy_sheet_fee_note">Des frais de réseau d\'environ %1$s (%2$s) seront déduits de chaque recharge future, sans dépasser votre limite de %3$s (%4$s).</string>
|
||||
<string name="yield_module_fee_policy_sheet_max_fee_note">Si les frais de réseau dépassent le montant maximal, la transaction ne sera pas effectuée tant qu\'ils n\'auront pas diminué. Vous pouvez modifier cette limite ultérieurement.</string>
|
||||
<string name="yield_module_fee_policy_sheet_max_fee_title">Frais maximaux</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_note">Le montant minimum est calculé à partir des frais de réseau actuels afin de garantir qu\'il ne dépasse pas 4 %, ce qui donne un minimum de %1$s (%2$s).</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_note">Le montant minimum est calculé à partir des frais de réseau actuels afin de garantir qu\'il ne dépasse pas 4 %%, ce qui donne un minimum de %1$s (%2$s).</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_title">Recharge minimale</string>
|
||||
<string name="yield_module_fee_policy_sheet_title">Politique tarifaire</string>
|
||||
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem prélève également des frais de service de 3% sur les revenus générés.</string>
|
||||
|
|
|
|||
|
|
@ -1597,7 +1597,7 @@
|
|||
<string name="yield_module_fee_policy_sheet_description">Все следующие пополнения %s автоматически поступят в Aave с удержанием комиссии за транзакцию.</string>
|
||||
<string name="yield_module_fee_policy_sheet_max_fee_note">Если комиссия сети превысит максимальный лимит, транзакция не будет выполнена до тех пор, пока комиссия не снизится. Вы сможете изменить этот лимит позже.</string>
|
||||
<string name="yield_module_fee_policy_sheet_max_fee_title">Максимальная комиссия</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_note">Это минимальный депозит, который можно отправить в Aave. Чтобы депозиты оставались прибыльными, мы не обрабатываем их, когда комиссия сети превышает 4% от суммы.</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_note">Это минимальный депозит, который можно отправить в Aave. Чтобы депозиты оставались прибыльными, мы не обрабатываем их, когда комиссия сети превышает %1$s%2$sот суммы.</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_title">Минимальный депозит</string>
|
||||
<string name="yield_module_fee_policy_sheet_title">Политика комиссий</string>
|
||||
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem взимает комиссию за обслуживание в размере 3% от полученного дохода.</string>
|
||||
|
|
|
|||
|
|
@ -1432,6 +1432,6 @@
|
|||
<string name="xtz_withdrawal_message_ignore">Ні, відправити все</string>
|
||||
<string name="xtz_withdrawal_message_reduce">Зменшити на %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_warning">Щоб не платити підвищену комісію при наступному поповненні гаманця, зменште суму на %s XTZ</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_note">Це найменший депозит, який можна надіслати в Aave. Щоб депозити залишалися прибутковими, ми не обробляємо їх, коли комісія мережі перевищує 4% від суми.</string>
|
||||
<string name="yield_module_fee_policy_sheet_min_amount_note">Це найменший депозит, який можна надіслати в Aave. Щоб депозити залишалися прибутковими, ми не обробляємо їх, коли комісія мережі перевищує %1$s%2$s від суми.</string>
|
||||
<string name="yield_module_main_view_approve_notification_description">Дозвіл для вашого токена в режимі Yield було відкликано. Відкрийте токен, щоб надати дозвіл знову.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class StakingApyFlowUseCase(private val stakingRepository: StakingRepository) {
|
|||
return stakingRepository.getEnabledYields()
|
||||
.map { yields ->
|
||||
yields.associate { yield ->
|
||||
val key = "${yield.token.network.name.lowercase()}_${yield.token.symbol}"
|
||||
val key = "${yield.token.coinGeckoId}_${yield.token.symbol}"
|
||||
val apy = calculateApy(yield)
|
||||
key to apy
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.SwapCryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapCurrencies
|
||||
import com.tangem.domain.swap.models.SwapCurrenciesGroup
|
||||
import com.tangem.domain.swap.models.SwapPairModel
|
||||
import com.tangem.domain.swap.models.SwapTxType
|
||||
import com.tangem.domain.swap.models.*
|
||||
|
||||
/**
|
||||
* Get list of swap pairs
|
||||
|
|
@ -67,13 +63,14 @@ class GetSwapPairsUseCase(
|
|||
groupingCurrency: (SwapPairModel) -> CryptoCurrencyStatus,
|
||||
cryptoCurrencyStatusList: List<CryptoCurrencyStatus>,
|
||||
): SwapCurrenciesGroup {
|
||||
val availableCryptoCurrencies = filter { pair -> filteringCurrency(pair).currency.id == initialCurrency.id }
|
||||
val availableCryptoCurrencies = filter { pair ->
|
||||
filteringCurrency(pair).currency.id.rawCurrencyId == initialCurrency.id.rawCurrencyId
|
||||
}.filter { pair ->
|
||||
// Search available to swap currency
|
||||
.filter { pair ->
|
||||
cryptoCurrencyStatusList.any { currencyStatus ->
|
||||
currencyStatus.currency.id == pair.to.currency.id
|
||||
}
|
||||
}.map { pair -> SwapCryptoCurrency(groupingCurrency(pair), pair.providers) }
|
||||
cryptoCurrencyStatusList.any { currencyStatus ->
|
||||
currencyStatus.currency.id == pair.to.currency.id
|
||||
}
|
||||
}.map { pair -> SwapCryptoCurrency(groupingCurrency(pair), pair.providers) }
|
||||
|
||||
val unavailableCryptoCurrencies =
|
||||
cryptoCurrencyStatusList - availableCryptoCurrencies.map { it.currencyStatus }.toSet()
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.SwapCryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapCurrencies
|
||||
import com.tangem.domain.swap.models.SwapCurrenciesGroup
|
||||
import com.tangem.domain.swap.models.SwapPairModel
|
||||
import com.tangem.domain.swap.models.SwapTxType
|
||||
import com.tangem.domain.swap.models.*
|
||||
|
||||
/**
|
||||
* Returns pais
|
||||
|
|
@ -36,7 +32,7 @@ class GetSwapSupportedPairsUseCase(
|
|||
swapTxType = swapTxType,
|
||||
)
|
||||
|
||||
val filteredOutInitial = cryptoCurrencyList.filterNot { it.id == initialCurrency.id }
|
||||
val filteredOutInitial = cryptoCurrencyList.filterNot { it.id.rawNetworkId == initialCurrency.id.rawNetworkId }
|
||||
|
||||
val fromGroup = pairs.groupPairs(
|
||||
initialCurrency = initialCurrency,
|
||||
|
|
@ -63,15 +59,16 @@ class GetSwapSupportedPairsUseCase(
|
|||
groupingCurrency: (SwapPairModel) -> CryptoCurrencyStatus,
|
||||
cryptoCurrencyList: List<CryptoCurrency>,
|
||||
): SwapCurrenciesGroup {
|
||||
val availableCryptoCurrencies = filter { pair -> filteringCurrency(pair).currency.id == initialCurrency.id }
|
||||
val availableCryptoCurrencies = filter { pair ->
|
||||
filteringCurrency(pair).currency.id.rawCurrencyId == initialCurrency.id.rawCurrencyId
|
||||
}.filter { pair ->
|
||||
// Search available to swap currency
|
||||
.filter { pair ->
|
||||
cryptoCurrencyList.any { currencyStatus ->
|
||||
// Allowed only on networks without tx extras (e.i. memo and destination tag)
|
||||
val isExtrasSupported = currencyStatus.network.transactionExtrasType.isTxExtrasSupported()
|
||||
currencyStatus.id == pair.to.currency.id && !isExtrasSupported
|
||||
}
|
||||
}.map { pair -> SwapCryptoCurrency(groupingCurrency(pair), pair.providers) }
|
||||
cryptoCurrencyList.any { currencyStatus ->
|
||||
// Allowed only on networks without tx extras (e.i. memo and destination tag)
|
||||
val isExtrasSupported = currencyStatus.network.transactionExtrasType.isTxExtrasSupported()
|
||||
currencyStatus.id == pair.to.currency.id && !isExtrasSupported
|
||||
}
|
||||
}.map { pair -> SwapCryptoCurrency(groupingCurrency(pair), pair.providers) }
|
||||
|
||||
val unavailableCryptoCurrencies =
|
||||
cryptoCurrencyList - availableCryptoCurrencies.map { it.currencyStatus.currency }.toSet()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.getPendingActio
|
|||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlin.text.isNullOrEmpty
|
||||
|
||||
internal class SetButtonsStateTransformer(
|
||||
private val urlOpener: UrlOpener,
|
||||
|
|
@ -25,7 +26,7 @@ internal class SetButtonsStateTransformer(
|
|||
val buttonsState = if (prevState.isButtonsVisible()) {
|
||||
NavigationButtonsState.Data(
|
||||
primaryButton = getPrimaryButton(prevState),
|
||||
extraButtons = getExtraButtons(prevState).takeIf { txUrl != null },
|
||||
extraButtons = getExtraButtons(prevState).takeUnless { txUrl.isNullOrEmpty() },
|
||||
txUrl = txUrl,
|
||||
onTextClick = urlOpener::openUrl,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,30 +40,32 @@ import com.tangem.utils.StringsSigns
|
|||
internal fun YieldSupplyBlockContent(yieldSupplyUM: YieldSupplyUM, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(
|
||||
targetState = yieldSupplyUM,
|
||||
modifier = modifier,
|
||||
) { supplyUM ->
|
||||
when (supplyUM) {
|
||||
is YieldSupplyUM.Available -> SupplyAvailable(supplyUM)
|
||||
YieldSupplyUM.Loading -> SupplyLoading()
|
||||
is YieldSupplyUM.Content -> SupplyContent(supplyUM)
|
||||
is YieldSupplyUM.Available -> SupplyAvailable(supplyUM, modifier)
|
||||
YieldSupplyUM.Loading -> SupplyLoading(modifier)
|
||||
is YieldSupplyUM.Content -> SupplyContent(supplyUM, modifier)
|
||||
YieldSupplyUM.Processing.Enter -> SupplyProcessing(
|
||||
resourceReference(R.string.yield_module_token_details_earn_notification_processing),
|
||||
modifier,
|
||||
)
|
||||
YieldSupplyUM.Processing.Exit -> SupplyProcessing(
|
||||
resourceReference(R.string.yield_module_stop_earning),
|
||||
modifier,
|
||||
)
|
||||
YieldSupplyUM.Unavailable -> SupplyUnavailable()
|
||||
YieldSupplyUM.Initial -> {}
|
||||
YieldSupplyUM.Unavailable -> SupplyUnavailable(modifier)
|
||||
YieldSupplyUM.Initial -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SupplyAvailable(supplyUM: YieldSupplyUM.Available) {
|
||||
private fun SupplyAvailable(supplyUM: YieldSupplyUM.Available, modifier: Modifier = Modifier) {
|
||||
SupplyInfo(
|
||||
title = supplyUM.title,
|
||||
subtitle = resourceReference(R.string.yield_module_token_details_earn_notification_description),
|
||||
iconTint = TangemTheme.colors.icon.accent,
|
||||
modifier = modifier,
|
||||
button = {
|
||||
SecondaryButton(
|
||||
text = stringResourceSafe(R.string.common_learn_more),
|
||||
|
|
@ -76,20 +78,21 @@ private fun SupplyAvailable(supplyUM: YieldSupplyUM.Available) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SupplyUnavailable() {
|
||||
private fun SupplyUnavailable(modifier: Modifier = Modifier) {
|
||||
SupplyInfo(
|
||||
title = resourceReference(R.string.yield_module_unavailable_title),
|
||||
subtitle = resourceReference(R.string.yield_module_unavailable_subtitle),
|
||||
iconTint = TangemTheme.colors.icon.inactive,
|
||||
button = null,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SupplyContent(supplyUM: YieldSupplyUM.Content) {
|
||||
private fun SupplyContent(supplyUM: YieldSupplyUM.Content, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.clickable(onClick = supplyUM.onClick)
|
||||
|
|
@ -149,10 +152,10 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SupplyProcessing(text: TextReference) {
|
||||
private fun SupplyProcessing(text: TextReference, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(12.dp),
|
||||
|
|
@ -190,11 +193,11 @@ private fun SupplyProcessing(text: TextReference) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SupplyLoading() {
|
||||
private fun SupplyLoading(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(12.dp),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ import com.tangem.features.yield.supply.impl.R
|
|||
@Composable
|
||||
internal fun YieldSupplyActiveTitle(onCloseClick: () -> Unit) {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.align(Alignment.Center)) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.yield_module_earn_sheet_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
|
|
|
|||
|
|
@ -159,6 +159,13 @@ object BlockchainUtils {
|
|||
return isSolana(blockchainId) || isBSC(blockchainId) || isTon(blockchainId)
|
||||
}
|
||||
|
||||
/** Checks if the blockchain uses case-insensitive contract addresses */
|
||||
fun isCaseInsensitiveContractAddress(blockchainId: String): Boolean {
|
||||
val blockchain = Blockchain.fromId(blockchainId)
|
||||
|
||||
return blockchain.isEvm()
|
||||
}
|
||||
|
||||
private fun getNetworkStandardName(blockchain: Blockchain): String {
|
||||
return when (blockchain) {
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ERC20"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue