Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-24 00:29:55 +03:00
commit 8af3e349a7
49 changed files with 837 additions and 488 deletions

View file

@ -1,6 +1,9 @@
package com.tangem.common.ui.amountScreen.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -127,6 +130,8 @@ private fun AmountInfoMain(amountUM: AmountState, modifier: Modifier = Modifier)
AnimatedContent(
targetState = amountUM,
modifier = modifier,
contentKey = { amountUM.javaClass }, // Show animation only when the state type changes [Empty -> Data]
transitionSpec = { fadeIn().togetherWith(fadeOut()) },
) { currentAmount ->
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),

View file

@ -274,6 +274,14 @@ sealed class NotificationUM(val config: NotificationConfig) {
title = resourceReference(id = R.string.yield_module_balance_info_sheet_title, wrappedList(tokenName)),
subtitle = resourceReference(id = R.string.yield_module_balance_info_sheet_subtitle),
)
data class YieldSupplyNotAllAmountSupplied(val formattedAmount: String, val symbol: String) : Warning(
title = resourceReference(
id = R.string.yield_module_amount_not_transfered_to_aave_title,
formatArgs = wrappedList(formattedAmount, symbol),
),
subtitle = TextReference.EMPTY,
)
}
open class Info(

View file

@ -20,7 +20,6 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.currency.yieldSupplyKey
import com.tangem.domain.models.currency.yieldSupplyNotAllAmountSupplied
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.utils.StringsSigns.DASH_SIGN
@ -271,8 +270,7 @@ class TokenItemStateConverter(
isFlickering = status.value.isFlickering(),
icons = buildList {
if (status.value.yieldSupplyStatus?.isActive == true &&
status.value.yieldSupplyStatus?.isAllowedToSpend == false ||
status.yieldSupplyNotAllAmountSupplied()
status.value.yieldSupplyStatus?.isAllowedToSpend == false
) {
TokenItemState.FiatAmountState.Content.IconUM(
iconRes = R.drawable.ic_alert_triangle_20,

View file

@ -1675,31 +1675,82 @@
<string name="xtz_withdrawal_message_ignore">Nein, alles senden</string>
<string name="xtz_withdrawal_message_reduce">Um %s XTZ reduziert</string>
<string name="xtz_withdrawal_message_warning">Damit Sie beim nächsten Aufladen Ihrer Brieftasche keine erhöhte Provision zahlen, soll der Betrag um %s XTZ reduziert werden</string>
<string name="yield_module_alert_description">Wenn der Yield-Modus aktiviert ist, gehen alle zukünftigen Einzahlungen an diese Adresse an Aave. Du kannst über Dein Guthaben weiterhin frei verfügen.</string>
<string name="yield_module_alert_title">Deine %s wird an Aave übermittelt</string>
<string name="yield_module_amount_not_transfered_to_aave_title">Die Bereitstellung von %1$s %2$s für Aave steht aus</string>
<string name="yield_module_approve_needed_notification_cta">Genehmigen</string>
<string name="yield_module_approve_needed_notification_description">Die Genehmigung Deines Tokens wurde widerrufen. Erteilen diese erneut, um die Servicefunktionalität fortzusetzen.</string>
<string name="yield_module_approve_needed_notification_title">Genehmigung erforderlich</string>
<string name="yield_module_approve_sheet_fee_note">Die Gebühr wird abgezogen und Dein Vermögen wird erneut verliehen.</string>
<string name="yield_module_approve_sheet_subtitle">Um weiterhin Geld verdienen zu können, ist eine Genehmigung erforderlich.</string>
<string name="yield_module_approve_sheet_title">Genehmigung bestätigen</string>
<string name="yield_module_balance_info_sheet_subtitle">Deine Gelder werden derzeit dem Aave-Protokoll bereitgestellt, Du kannst sie jedoch jederzeit verwalten.</string>
<string name="yield_module_balance_info_sheet_title">Deine%s ist in Aave hinterlegt</string>
<string name="yield_module_chart_loading_error">Chart konnte nicht geladen werden...</string>
<string name="yield_module_deposit_error_notification_title">Der erhaltene Betrag %1$s %2$s wurde nicht auf Aave eingezahlt.</string>
<string name="yield_module_earn_badge">APY %1$s%%</string>
<string name="yield_module_earn_sheet_available_title">Verfügbar</string>
<string name="yield_module_earn_sheet_current_apy_title">Aktueller effektiver Jahreszins</string>
<string name="yield_module_earn_sheet_fee_description">Beim Aufladen zum Ausleihen wird eine Netzwerkgebühr von maximal %1$s vom Guthaben abgezogen.</string>
<string name="yield_module_earn_sheet_high_fee_description">Die Netzwerkgebühr ist derzeit zu hoch, um die Kreditvergabe durchzuführen. Die Mittel werden bereitgestellt, sobald sie auf %1$s oder darunter fallen. </string>
<string name="yield_module_earn_sheet_my_funds_title">Meine Mittel</string>
<string name="yield_module_earn_sheet_provider_description">Dein %1$s ist nun bei Aave hinterlegt und bringt Zinsen ein. Du besitzt einen %2$s-Token, der Dein Guthaben repräsentiert und mit der Zeit wächst. Wenn Du mehr Geld einzahlst, wird dieses an Aave weitergeleitet, um Zinsen abzüglich einer Transaktionsgebühr zu erwirtschaften.</string>
<string name="yield_module_earn_sheet_title">Ertragsmodus</string>
<string name="yield_module_earn_sheet_total_earnings_title">Gesamtverdienst</string>
<string name="yield_module_earn_sheet_transfers_title">Übertragungen zu Aave</string>
<string name="yield_module_explore_sheet_explore_aave_button_title">Entdecke Aave</string>
<string name="yield_module_fee_policy_sheet_current_fee_note">Dies ist die aktuelle Liefergebühr auf %s. Die tatsächlichen Kosten werden auf der Registerkarte \"Aktivierung\" angezeigt.</string>
<string name="yield_module_fee_policy_sheet_current_fee_title">Aktuelle Gebühr</string>
<string name="yield_module_fee_policy_sheet_description">Alle zukünftigen %s-Einzahlungen werden automatisch an Aave geliefert, wobei die Transaktionsgebühr abgezogen wird.</string>
<string name="yield_module_fee_policy_sheet_fee_note">Bei jeder zukünftigen Aufladung wird eine Netzwerkgebühr von ungefähr %1$s (%2$s) abgezogen, die Ihr Limit von %3$s (%4$s) nicht überschreitet.</string>
<string name="yield_module_fee_policy_sheet_max_fee_note">Wenn die Netzwerkgebühren über die maximale Gebühr steigen, wird die Transaktion erst durchgeführt, wenn diese sinken. Du kannst dieses Limit später ändern.</string>
<string name="yield_module_fee_policy_sheet_max_fee_title">Maximale Gebühr</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">Dies ist die niedrigste Einzahlung, die an Aave gesendet werden kann. Um Einzahlungen rentabel zu halten, verarbeiten wir sie nicht, wenn die Netzwerkgebühren 4 % des Betrags übersteigen.</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">Der Mindestbetrag wird aus der aktuellen Netzwerkgebühr berechnet, um sicherzustellen, dass er 4%% nicht überschreitet, was den Mindestbetrag %1$s (%2$s) ergibt.</string>
<string name="yield_module_fee_policy_sheet_min_amount_title">Mindestaufladung</string>
<string name="yield_module_fee_policy_sheet_title">Gebührenpolitik</string>
<string name="yield_module_high_fee_error">Die Netzwerkgebühr ist derzeit zu hoch. Warte, bis sie unter Dein Limit fällt.</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem erhebt außerdem eine Servicegebühr von 3% auf den erzielten Ertrag.</string>
<string name="yield_module_high_fee_error">Deine Gelder werden automatisch an Aave überwiesen, sobald die Netzwerkgebühren niedriger sind oder Dein Guthaben den erforderlichen Mindestbetrag erreicht.</string>
<string name="yield_module_historical_returns">Historische Renditen</string>
<string name="yield_module_promo_screen_cash_out_title">Sofortige Auszahlung</string>
<string name="yield_module_main_view_approve_notification_description">Die Freigabe für Deine Token im Yield-Modus wurde widerrufen. Öffne den Token, um die Berechtigung erneut zu erteilen.</string>
<string name="yield_module_main_view_approve_notification_title">Token-Genehmigung erforderlich</string>
<string name="yield_module_network_fee_unreachable_notification_description">Prüfe Deine Netzwerkverbindung</string>
<string name="yield_module_network_fee_unreachable_notification_title">Informationen zu den Netzwerkgebühren nicht erreichbar</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">Jede Einzahlung, die Du tätigst, wird automatisch an Aave weitergeleitet.</string>
<string name="yield_module_promo_screen_auto_balance_title">Automatische Übertragung zu Aave</string>
<string name="yield_module_promo_screen_cash_out_subtitle">Senden, tauschen oder verkaufen Deine Gelder sofort, wann immer Du willst.</string>
<string name="yield_module_promo_screen_cash_out_title">Jederzeit Zugriff auf Dein Geld</string>
<string name="yield_module_promo_screen_how_it_works_button_title">Wie funktioniert das?</string>
<string name="yield_module_promo_screen_self_custodial_subtitle">Aave ist ein dezentrales Protokoll, das einen Gesamtwert von über 81,9 Milliarden US-Dollar verwaltet.</string>
<string name="yield_module_promo_screen_self_custodial_title">Dezentral und selbstverwahrend</string>
<string name="yield_module_promo_screen_terms_disclaimer">Durch die Nutzung dieses Dienstes erklärst Du Dich mit provider\n%1$s und %2$s einverstanden</string>
<string name="yield_module_promo_screen_title">Mit Aave verbinden</string>
<string name="yield_module_promo_screen_variable_rate_info">Aave %1$s • Variabler Zinssatz</string>
<string name="yield_module_promo_screen_variable_rate_info">Aave %1$s%% • Variabler Zinssatz</string>
<string name="yield_module_provider">Aave</string>
<string name="yield_module_rate_info_sheet_chart_average">Durchschnitt %s</string>
<string name="yield_module_rate_info_sheet_chart_title">Renditen des letzten Jahres</string>
<string name="yield_module_rate_info_sheet_description">Der aktuelle Zinssatz ist immer variabel und wird automatisch vom Aave On-Chain-Smart-Contract auf der Grundlage von Angebot und Nachfrage in Echtzeit berechnet.</string>
<string name="yield_module_rate_info_sheet_powered_by">Unterstützt durch</string>
<string name="yield_module_rate_info_sheet_title">Der Zinssatz ist variabel</string>
<string name="yield_module_receive_sheet_description">Wenn Du dein Guthaben auflädst, wird es automatisch an Aave weitergeleitet, um Zinsen zu verdienen. zur Deckung der Transaktionsgebühr wird ein Betrag von %s abgezogen.</string>
<string name="yield_module_start_earning">Beginn des Verdienstes</string>
<string name="yield_module_start_earning_sheet_description">Dein %s wird an Aave übermittelt, bleibt aber verwaltbar.</string>
<string name="yield_module_start_earning_sheet_fee_policy">Siehe Gebührenrichtlinie</string>
<string name="yield_module_start_earning_sheet_next_deposits">Deine nächste Aufladung wird automatisch an Aave weitergeleitet.</string>
<string name="yield_module_status_active">Aktiv</string>
<string name="yield_module_status_paused">Pausiert</string>
<string name="yield_module_stop_earning">Deaktiviere den Yield-Modus</string>
<string name="yield_module_stop_earning_sheet_description">Wenn Du diese Option deaktivierst, wird Dein Geld von Aave auf %s in Deiner Wallet abgehoben und es werden keine Belohnungen mehr verdient.</string>
<string name="yield_module_stop_earning_sheet_fee_note">Die Netzwerkgebühr wird von dem Betrag, den Du abhebst, abgezogen.</string>
<string name="yield_module_supply_apr">Effektiver Jahreszins für Versorgung</string>
<string name="yield_module_token_details_earn_notification_apy">Effektiver Jahreszins</string>
<string name="yield_module_token_details_earn_notification_description">Lass Dein Geld arbeiten verdiene Zinsen auf Dein Guthaben.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Verdienst auf Dein Guthaben</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">Zinsen fallen automatisch an</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Ertragsmodus</string>
<string name="yield_module_token_details_earn_notification_processing">Bearbeitung Deiner Einzahlung</string>
<string name="yield_module_token_details_earn_notification_title">Lass dein Guthaben arbeiten</string>
<string name="yield_module_transfer_mode_automatic">Automatisch</string>
<string name="yield_module_unable_to_cover_fee_description">Einzahlung von %1$s %2$s zur Deckung der Netzwerkgebühr für Transaktionen</string>
<string name="yield_module_unable_to_cover_fee_title">Die Gebühr %s kann nicht gedeckt werden</string>
<string name="yield_module_unavailable_subtitle">Der Stakingservice ist derzeit nicht verfügbar. Bitte versuche es später erneut.</string>
<string name="yield_module_unavailable_title">Einnahmen nicht verfügbar</string>
<string name="yield_supply_chart_loading_error">Chart konnte nicht geladen werden...</string>

View file

@ -1106,6 +1106,7 @@
<string name="story_web3_title">Compatible con Web 3.0</string>
<string name="sui_not_enough_coin_for_fee_description">Se requiere una transacción entrante de al menos %1$s para proceder</string>
<string name="sui_not_enough_coin_for_fee_title">Fondos insuficientes</string>
<string name="swap_approve_description">Al aprobar, permites que el contrato inteligente use tus tokens en futuras transacciones.</string>
<string name="swap_fixed_rate">Tasa Fija</string>
<string name="swap_give_permission_fee_footer">La red cobrará una tasa de aprobación del token para verificar que usted autoriza el uso de su token para el intercambio.</string>
<string name="swap_promo_text">Intercambie más tokens a mejores tasas directamente en su billetera.</string>
@ -1400,6 +1401,7 @@
<string name="wc_alert_wrong_card_title">Tenemos algún tipo de problema</string>
<string name="wc_all_dapps_disconnected">Todas las dApps desconectadas</string>
<string name="wc_allow_to_spend">Permitir gastar</string>
<string name="wc_approve_description">Al aprobar, permites que la dApp o el contrato inteligente utilicen los tokens en futuras transacciones.</string>
<string name="wc_common_address">Dirección</string>
<string name="wc_common_connect">Conectar</string>
<string name="wc_common_loading">Cargando</string>
@ -1474,4 +1476,82 @@
<string name="xtz_withdrawal_message_ignore">No, enviar todo</string>
<string name="xtz_withdrawal_message_reduce">Reducir en %s XTZ</string>
<string name="xtz_withdrawal_message_warning">Para evitar pagar una comisión mayor la próxima vez que recargue su billetera, reduzca el importe en %s XTZ</string>
<string name="yield_module_alert_description">Con el modo Rendimiento activo, todos los depósitos futuros a esta dirección irán a Aave. Puede seguir gestionando sus fondos libremente.</string>
<string name="yield_module_alert_title">Su %s se suministra a Aave</string>
<string name="yield_module_amount_not_transfered_to_aave_title">El suministro de %1$s %2$s a Aave está pendiente</string>
<string name="yield_module_approve_needed_notification_cta">Aprobar</string>
<string name="yield_module_approve_needed_notification_description">Se ha revocado la aprobación de su token. Concédalo de nuevo para reanudar el servicio.</string>
<string name="yield_module_approve_needed_notification_title">Aprobación necesaria</string>
<string name="yield_module_approve_sheet_fee_note">Se le descontará la comisión y se le volverán a prestar sus activos.</string>
<string name="yield_module_approve_sheet_subtitle">Para seguir ganando, se requiere aprobación.</string>
<string name="yield_module_approve_sheet_title">Confirmar aprobación</string>
<string name="yield_module_balance_info_sheet_subtitle">Sus fondos se suministran actualmente al protocolo Aave, pero puede gestionarlos en cualquier momento.</string>
<string name="yield_module_balance_info_sheet_title">Su %s está depositado en Aave</string>
<string name="yield_module_chart_loading_error">No se puede cargar el gráfico...</string>
<string name="yield_module_deposit_error_notification_title">La cantidad recibida, %1$s %2$s no fue suministrada a Aave.</string>
<string name="yield_module_earn_badge">APY %1$s%%</string>
<string name="yield_module_earn_sheet_available_title">Disponible</string>
<string name="yield_module_earn_sheet_current_apy_title">APY actual</string>
<string name="yield_module_earn_sheet_fee_description">En las recargas para préstamos, se deducirá del saldo una comisión de red no superior a %1$s.</string>
<string name="yield_module_earn_sheet_high_fee_description">La tasa de red es actualmente demasiado alta para ejecutar préstamos. Los fondos se suministrarán una vez que baje a %1$s o menos.</string>
<string name="yield_module_earn_sheet_my_funds_title">Mis fondos</string>
<string name="yield_module_earn_sheet_provider_description">Su %1$s está ahora depositado en Aave y devenga intereses. Tiene un token%2$s, que representa su saldo y crece con el tiempo. Cuando deposite más fondos, estos se suministrarán a Aave para ganar intereses, menos una comisión por transacción.</string>
<string name="yield_module_earn_sheet_title">Modo de rendimiento</string>
<string name="yield_module_earn_sheet_total_earnings_title">Ganancias totales</string>
<string name="yield_module_earn_sheet_transfers_title">Transferencias a Aave</string>
<string name="yield_module_explore_sheet_explore_aave_button_title">Explore Aave</string>
<string name="yield_module_fee_policy_sheet_current_fee_note">Esta es la tarifa de suministro actual en %s. El coste real se mostrará en la pestaña de activación.</string>
<string name="yield_module_fee_policy_sheet_current_fee_title">Tarifa actual</string>
<string name="yield_module_fee_policy_sheet_description">Todos los depósitos futuros %s se proporcionarán a Aave automáticamente, con la tarifa de transacción deducida.</string>
<string name="yield_module_fee_policy_sheet_fee_note">Se deducirá una comisión de red aproximada de %1$s (%2$s) de cada recarga futura, y no superará su límite de %3$s (%4$s).</string>
<string name="yield_module_fee_policy_sheet_max_fee_note">Si las tarifas de red superan la tarifa máxima, la transacción no se realizará hasta que disminuyan. Puede cambiar este límite más adelante.</string>
<string name="yield_module_fee_policy_sheet_max_fee_title">Tarifa máxima</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">El monto mínimo se calcula a partir de la tarifa de red actual para garantizar que no exceda 4%%, lo que hace que el mínimo sea %1$s (%2$s).</string>
<string name="yield_module_fee_policy_sheet_min_amount_title">Recarga mínima</string>
<string name="yield_module_fee_policy_sheet_title">Política de tarifas</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem también cobra una comisión de servicio del 3% sobre el rendimiento obtenido.</string>
<string name="yield_module_high_fee_error">Sus fondos se suministrarán automáticamente a Aave una vez que las comisiones de red sean más bajas o su saldo alcance el importe mínimo requerido.</string>
<string name="yield_module_historical_returns">Rendimientos históricos</string>
<string name="yield_module_main_view_approve_notification_description">Se ha revocado la autorización para su token en el modo Rendimiento. Abra el token para volver a conceder el permiso.</string>
<string name="yield_module_main_view_approve_notification_title">Se necesita la aprobación de Token</string>
<string name="yield_module_network_fee_unreachable_notification_description">Compruebe su conexión de red</string>
<string name="yield_module_network_fee_unreachable_notification_title">Información de tarifas de red inaccesible</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">Cada depósito que realice se suministrará a Aave automáticamente.</string>
<string name="yield_module_promo_screen_auto_balance_title">Transferencia automática a Aave</string>
<string name="yield_module_promo_screen_cash_out_subtitle">Envíe, intercambie o venda sus fondos al instante, cuando quiera.</string>
<string name="yield_module_promo_screen_cash_out_title">Acceda a sus fondos en cualquier momento</string>
<string name="yield_module_promo_screen_how_it_works_button_title">¿Cómo funciona?</string>
<string name="yield_module_promo_screen_self_custodial_subtitle">Aave es un protocolo descentralizado que administra más de 81.9 billones de dólares en valor total.</string>
<string name="yield_module_promo_screen_self_custodial_title">Descentralizado y autocustodiado</string>
<string name="yield_module_promo_screen_terms_disclaimer">Al utilizar este servicio, usted acepta que el proveedor\n%1$s y %2$s</string>
<string name="yield_module_promo_screen_title">Conectar Aave</string>
<string name="yield_module_promo_screen_variable_rate_info">Aave %1$s%% - Tipo de interés variable</string>
<string name="yield_module_provider">Aave</string>
<string name="yield_module_rate_info_sheet_chart_average">Promedio %s</string>
<string name="yield_module_rate_info_sheet_chart_title">Resultados del año pasado</string>
<string name="yield_module_rate_info_sheet_description">La tasa de interés actual siempre es variable y calculada automáticamente por el contrato inteligente onchain de Aave, en función de la oferta y la demanda en tiempo real.</string>
<string name="yield_module_rate_info_sheet_powered_by">Desarrollado por</string>
<string name="yield_module_rate_info_sheet_title">El tipo de interés es variable</string>
<string name="yield_module_receive_sheet_description">Cuando recargue, sus fondos se enviarán automáticamente a Aave para comenzar a ganar intereses. %s se deducirá para cubrir la tarifa de transacción.</string>
<string name="yield_module_start_earning">Suministro de activos</string>
<string name="yield_module_start_earning_sheet_description">Su %s se suministrará a Aave, pero seguirá siendo gestionable.</string>
<string name="yield_module_start_earning_sheet_fee_policy">Ver política de tarifas</string>
<string name="yield_module_start_earning_sheet_next_deposits">Sus próximas recargas se suministrarán automáticamente a Aave.</string>
<string name="yield_module_status_active">Activo</string>
<string name="yield_module_status_paused">En pausa</string>
<string name="yield_module_stop_earning">Desactivar el modo de rendimiento</string>
<string name="yield_module_stop_earning_sheet_description">Al desactivar esto, retirará sus fondos de Aave a %s en su billetera y dejará de ganar recompensas.</string>
<string name="yield_module_stop_earning_sheet_fee_note">La comisión de red se deducirá del importe que retire.</string>
<string name="yield_module_supply_apr">Suministro APY</string>
<string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">Deje que sus fondos hagan el trabajo mientras usted mantiene el control.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">Los intereses se devengan automáticamente</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Modo de rendimiento</string>
<string name="yield_module_token_details_earn_notification_processing">Procesando su depósito</string>
<string name="yield_module_token_details_earn_notification_title">Haga que sus activos trabajen para usted</string>
<string name="yield_module_transfer_mode_automatic">Automático</string>
<string name="yield_module_unable_to_cover_fee_description">Deposite algo de %1$s %2$s para cubrir la tarifa de red para las transacciones</string>
<string name="yield_module_unable_to_cover_fee_title">No se puede cubrir la tarifa %s</string>
<string name="yield_module_unavailable_subtitle">El servicio de intereses no está disponible en este momento. Vuelva a intentarlo más tarde.</string>
<string name="yield_module_unavailable_title">Ganancias no disponibles</string>
</resources>

View file

@ -1080,6 +1080,7 @@
<string name="story_web3_title">Compatible avec Web 3.0</string>
<string name="sui_not_enough_coin_for_fee_description">Une transaction entrante d\'au moins de %1$s est requise pour continuer</string>
<string name="sui_not_enough_coin_for_fee_title">Fonds insuffisants</string>
<string name="swap_approve_description">En approuvant, vous autorisez le contrat intelligent à utiliser vos jetons dans de futures transactions.</string>
<string name="swap_fixed_rate">Taux fixe</string>
<string name="swap_give_permission_fee_footer">Le réseau facturera des frais d\'approbation de jeton pour vérifier que vous autorisez l\'utilisation de votre jeton pour l\'échange.</string>
<string name="swap_promo_text">Échangez plus de jetons à de meilleurs taux directement dans votre portefeuille.</string>
@ -1386,6 +1387,7 @@
<string name="wc_alert_wrong_card_title">Nous avons un problème.</string>
<string name="wc_all_dapps_disconnected">Toutes les dApps sont déconnectées</string>
<string name="wc_allow_to_spend">Autoriser à dépenser</string>
<string name="wc_approve_description">En approuvant, vous autorisez la dApp ou le contrat intelligent à utiliser les jetons dans de futures transactions.</string>
<string name="wc_common_address">Adresse</string>
<string name="wc_common_connect">Connecter</string>
<string name="wc_common_loading">Chargement</string>
@ -1453,4 +1455,80 @@
<string name="xtz_withdrawal_message_ignore">Non, envoyer toute la somme</string>
<string name="xtz_withdrawal_message_reduce">Réduire de %s XTZ</string>
<string name="xtz_withdrawal_message_warning">Pour ne pas payer un fraid de commissions élevé la prochaine fois que vous rechargez votre portefeuille, veuillez réduire le montant de %s XTZ</string>
<string name="yield_module_alert_description">Vos fonds sont actuellement fournis au protocole Aave, mais vous pouvez les gérer à tout moment.</string>
<string name="yield_module_alert_title">Vos %s sont fournis à Aave.</string>
<string name="yield_module_amount_not_transfered_to_aave_title">Le transfert de %1$s %2$s vers Aave est en attente.</string>
<string name="yield_module_approve_needed_notification_cta">Donnez votre accord</string>
<string name="yield_module_approve_needed_notification_description">Un problème est survenu lors de votre précédente approbation, nous avons donc besoin d\'une nouvelle approbation. Choisissez comment vous souhaitez procéder.</string>
<string name="yield_module_approve_needed_notification_title">Autorisation nécessaire</string>
<string name="yield_module_approve_sheet_fee_note">Les frais seront prélevés et vos actifs seront à nouveau prêtés.</string>
<string name="yield_module_approve_sheet_subtitle">Pour continuer à percevoir des revenus, une autorisation est nécessaire.</string>
<string name="yield_module_approve_sheet_title">Confirmer l\'approbation</string>
<string name="yield_module_balance_info_sheet_title">Vos %s sont déposés dans Aave.</string>
<string name="yield_module_chart_loading_error">Impossible de charger le graphique...</string>
<string name="yield_module_deposit_error_notification_title">Le montant reçu, %1$s %2$s, n\'a pas été fourni à Aave.</string>
<string name="yield_module_earn_badge">APY %1$s%%</string>
<string name="yield_module_earn_sheet_available_title">Disponible</string>
<string name="yield_module_earn_sheet_current_apy_title">APY actuel</string>
<string name="yield_module_earn_sheet_fee_description">Lors du rechargement pour le dépôt, des frais de réseau ne dépassant pas %1$s seront déduits du solde.</string>
<string name="yield_module_earn_sheet_high_fee_description">Les frais de réseau sont actuellement trop élevés pour permettre l\'exécution des prêts. Les fonds seront versés dès qu\'ils auront baissé à %1$s ou moins.</string>
<string name="yield_module_earn_sheet_my_funds_title">Mes fonds</string>
<string name="yield_module_earn_sheet_provider_description">Vos %1$s sont désormais déposés chez Aave et rapportent des intérêts. Vous détenez %2$s tokens, ce qui représente votre solde et augmente au fil du temps. Lorsque vous déposez davantage de fonds, ceux-ci sont fournis à Aave afin de rapporter des intérêts, moins les frais de transaction.</string>
<string name="yield_module_earn_sheet_title">Gagner</string>
<string name="yield_module_earn_sheet_total_earnings_title">Total des gains</string>
<string name="yield_module_earn_sheet_transfers_title">Transferts vers Aave</string>
<string name="yield_module_explore_sheet_explore_aave_button_title">Découvrez Aave</string>
<string name="yield_module_fee_policy_sheet_current_fee_note">Il s\'agit des frais d\'approvisionnement actuels sur %s. Le coût réel sera indiqué dans l\'onglet Activation.</string>
<string name="yield_module_fee_policy_sheet_current_fee_title">Frais actuels</string>
<string name="yield_module_fee_policy_sheet_description">Tous les futurs dépôts %s seront automatiquement fournis à Aave, après déduction des frais de transaction.</string>
<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_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>
<string name="yield_module_high_fee_error">Vos fonds seront automatiquement transférés vers Aave dès que les frais de réseau seront moins élevés ou que votre solde atteindra le montant minimum requis.</string>
<string name="yield_module_historical_returns">Rendements historiques</string>
<string name="yield_module_main_view_approve_notification_title">Approbations de tokens nécessaires</string>
<string name="yield_module_network_fee_unreachable_notification_description">Vérifiez votre connexion réseau.</string>
<string name="yield_module_network_fee_unreachable_notification_title">Informations sur les frais de réseau inaccessibles</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">Chaque dépôt que vous effectuez sera automatiquement transféré à Aave.</string>
<string name="yield_module_promo_screen_auto_balance_title">Transfert automatique vers Aave</string>
<string name="yield_module_promo_screen_cash_out_subtitle">Envoyez, échangez ou vendez vos fonds instantanément, quand vous le souhaitez.</string>
<string name="yield_module_promo_screen_cash_out_title">Accédez à vos fonds à tout moment</string>
<string name="yield_module_promo_screen_how_it_works_button_title">Comment ça marche ?</string>
<string name="yield_module_promo_screen_self_custodial_subtitle">Aave est un protocole décentralisé qui gère plus de 81,9 milliards de dollars en valeur totale.</string>
<string name="yield_module_promo_screen_self_custodial_title">Décentralisé et auto-détenu</string>
<string name="yield_module_promo_screen_terms_disclaimer">En utilisant ce service, vous acceptez les conditions générales du fournisseur %1$s et %2$s.</string>
<string name="yield_module_promo_screen_title">Connecter Aave</string>
<string name="yield_module_promo_screen_variable_rate_info">Aave %1$s%% • Taux d\'intérêt variable</string>
<string name="yield_module_provider">Aave</string>
<string name="yield_module_rate_info_sheet_chart_average">Moyenne %s</string>
<string name="yield_module_rate_info_sheet_chart_title">Rendements de l\'année dernière</string>
<string name="yield_module_rate_info_sheet_description">Le taux d\'intérêt actuel est toujours variable et calculé automatiquement par le contrat intelligent Aave, en fonction de l\'offre et de la demande en temps réel.</string>
<string name="yield_module_rate_info_sheet_powered_by">Alimenté par</string>
<string name="yield_module_rate_info_sheet_title">Le taux d\'intérêt est variable.</string>
<string name="yield_module_receive_sheet_description">Lorsque vous effectuez un dépôt, vos fonds sont automatiquement transférés vers Aave afin de commencer à générer des intérêts. %s sera déduit pour couvrir les frais de transaction.</string>
<string name="yield_module_start_earning">Actifs d\'approvisionnement</string>
<string name="yield_module_start_earning_sheet_description">Vos %s seront fournis à Aave, mais resteront gérables.</string>
<string name="yield_module_start_earning_sheet_fee_policy">Voir la politique tarifaire</string>
<string name="yield_module_start_earning_sheet_next_deposits">Vos prochains dépôts seront automatiquement transférés vers Aave.</string>
<string name="yield_module_status_active">Actif</string>
<string name="yield_module_status_paused">En pause</string>
<string name="yield_module_stop_earning">Désactiver le mode rendement</string>
<string name="yield_module_stop_earning_sheet_description">En désactivant cela, vos fonds seront retirés d\'Aave vers %s dans votre portefeuille et vous ne gagnerez plus de récompenses.</string>
<string name="yield_module_stop_earning_sheet_fee_note">Les frais de réseau seront déduits du montant que vous retirez.</string>
<string name="yield_module_supply_apr">Rendement annuel brut (APY)</string>
<string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">Laissez vos fonds travailler pour vous tout en gardant le contrôle.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">Les intérêts sont cumulés automatiquement.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Mode de rendement</string>
<string name="yield_module_token_details_earn_notification_processing">Traitement de votre dépôt</string>
<string name="yield_module_token_details_earn_notification_title">Faites fructifier vos actifs</string>
<string name="yield_module_transfer_mode_automatic">Automatique</string>
<string name="yield_module_unable_to_cover_fee_description">Déposez %1$s %2$s pour couvrir les frais de réseau liés aux transactions.</string>
<string name="yield_module_unable_to_cover_fee_title">Impossible de couvrir les frais %s</string>
<string name="yield_module_unavailable_subtitle">Le service d\'intérêt n\'est pas disponible pour le moment. Veuillez réessayer plus tard.</string>
<string name="yield_module_unavailable_title">Gains indisponibles</string>
</resources>

View file

@ -23,6 +23,8 @@
<string name="account_archived_recover_error_title">アカウントを復元できません</string>
<string name="account_archived_title">アーカイブ済み</string>
<string name="account_could_not_archive">アカウントをアーカイブできませんでした。しばらくしてからもう一度お試しください。</string>
<string name="account_could_not_archive_referral_program_message">このアカウントは紹介プログラムに参加しています。</string>
<string name="account_could_not_archive_referral_program_title">このアカウントはアーカイブできません。</string>
<string name="account_could_not_create">アカウントを作成できませんでした。しばらくしてからもう一度お試しください。</string>
<string name="account_create_success_message">アカウントを作成しました</string>
<string name="account_details_archive">アカウントをアーカイブする</string>
@ -42,6 +44,8 @@
<string name="account_form_title_edit">アカウントを編集</string>
<string name="account_label_tokens_info">%1$s ( %2$s内)</string>
<string name="account_main_account_title">メインアカウント</string>
<string name="account_recover_limit_dialog_description">すでにアクティブアカウントの上限%1$s件を超えています。復元するには、1つをアーカイブしてください。</string>
<string name="account_recover_limit_dialog_title">アカウントを復元できません</string>
<string name="account_recover_success_message">アカウントが回復しました</string>
<string name="account_reorder_description">アカウントを長押しして並べ替える</string>
<string name="account_unsaved_dialog_action_first">編集を続ける</string>
@ -1738,14 +1742,13 @@
<string name="xtz_withdrawal_message_warning">次回ウォレットにチャージするときに手数料の増加を避けるには、金額を%s XTZ減らしてください。</string>
<string name="yield_module_alert_description">あなたの資金は現在Aaveプロトコルに供給されていますが、いつでも管理できます。</string>
<string name="yield_module_alert_title">%sはAaveに供給されています</string>
<string name="yield_module_amount_not_transfered_to_aave_title">受け取った金額%1$s %2$sはAaveに入金されませんでした。</string>
<string name="yield_module_amount_not_transfered_to_aave_title">Aaveへの%1$s %2$sの供給は保留中です</string>
<string name="yield_module_approve_needed_notification_cta">承認する</string>
<string name="yield_module_approve_needed_notification_description">前回の承認に問題があったため、新しい承認が必要です。手続き方法を選択してください。</string>
<string name="yield_module_approve_needed_notification_title">承認が必要</string>
<string name="yield_module_approve_sheet_fee_note">手数料が差し引かれ、あなたの資産は再び貸し出されます。</string>
<string name="yield_module_approve_sheet_subtitle">引き続き収益を得るには承認が必要です。</string>
<string name="yield_module_approve_sheet_title">承認を確定する</string>
<string name="yield_module_balance_info_sheet_subtitle">資産残高に関するテキスト [プレースホルダー]</string>
<string name="yield_module_balance_info_sheet_title">あなたの%sはAaveに預けられています</string>
<string name="yield_module_chart_loading_error">チャートを読み込めません・・</string>
<string name="yield_module_deposit_error_notification_title">受け取った金額%1$s %2$sはAaveに入金されませんでした。</string>
@ -1763,19 +1766,19 @@
<string name="yield_module_fee_policy_sheet_current_fee_note">これは%sの現在の供給手数料です。実際のコストはアクティベーションタブに表示されます。</string>
<string name="yield_module_fee_policy_sheet_current_fee_title">現在の手数料</string>
<string name="yield_module_fee_policy_sheet_description">今後の%sの入金はすべて、取引手数料が差し引かれて自動的にAaveに供給されます。</string>
<string name="yield_module_fee_policy_sheet_fee_note">今後のチャージごとに、おおよそ%1$s%2$sのネットワーク手数料が差し引かれますが、上限の%3$s%4$sを超えることはありません。</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">最小金額は現在のネットワーク手数料に基づいて計算されており、手数料が4を超えないように設定されています。その結果、最小金額は%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>
<string name="yield_module_high_fee_error">ネットワーク手数料が下がるか、残高が最低必要額に達すると、資金は自動的にAaveに供給されます。</string>
<string name="yield_module_historical_returns">過去のリターン</string>
<string name="yield_module_main_view_approve_notification_description">ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー]</string>
<string name="yield_module_main_view_approve_notification_title">トークンの承認が必要</string>
<string name="yield_module_network_fee_unreachable_notification_description">ネットワーク接続を確認してください</string>
<string name="yield_module_network_fee_unreachable_notification_title">ネットワーク手数料についての情報にアクセスできません</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">アカウントへの入金はすべて自動的にAaveに貸し出されます。</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">あなたが行うすべての入金は、自動的にAaveへ供給されます。</string>
<string name="yield_module_promo_screen_auto_balance_title">Aaveへの自動送金</string>
<string name="yield_module_promo_screen_cash_out_subtitle">いつでも、即座に資金を送信、交換、売却できます。</string>
<string name="yield_module_promo_screen_cash_out_title">いつでも資金にアクセス可能</string>

View file

@ -395,6 +395,7 @@
<string name="express_more_providers_soon">Больше провайдеров на подходе.\nСледите за обновлениями!</string>
<string name="express_provider">Провайдер</string>
<string name="express_provider_best_rate">Лучший курс</string>
<string name="express_provider_great_rate">Лучший выбор</string>
<string name="express_provider_max_amount">Доступно до %s</string>
<string name="express_provider_min_amount">Доступно с %s</string>
<string name="express_provider_not_available">Недоступно для этой пары</string>
@ -1236,6 +1237,7 @@
<string name="token_button_unavailability_reason_pending_transaction_send">Отправка средств станет доступной после завершения транзакции(-ий) в сети %s</string>
<string name="token_button_unavailability_reason_sell_unavailable">Продажа %s в данный момент не поддерживается ни одним провайдером. Мы работаем над добавлением новых возможностей. Следите за нашими новостями.</string>
<string name="token_button_unavailability_reason_staking_unavailable">Стейкинг %s в данный момент не поддерживается ни одним провайдером. Мы работаем над добавлением новых возможностей. Следите за нашими новостями.</string>
<string name="token_button_unavailability_reason_yield_supply_approval">Разрешение было отозвано. Ваши средства остаются в Yield сервисе. Чтобы совершать операции, перейдите в Yield сервис и снова выдайте разрешение.</string>
<string name="token_details_generate_xpub">Сгенерировать XPUB</string>
<string name="token_details_hide_alert_hide">Скрыть</string>
<string name="token_details_hide_alert_message">Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами.</string>
@ -1494,6 +1496,7 @@
<string name="wc_alert_wrong_card_title">Похоже, возникла проблема</string>
<string name="wc_all_dapps_disconnected">Все dapps отключены</string>
<string name="wc_allow_to_spend">Разрешение на использование</string>
<string name="wc_approve_description">Подтверждая, вы разрешаете смарт-контракту использовать ваши токены в будущих транзакциях.</string>
<string name="wc_common_address">Адрес</string>
<string name="wc_common_connect">Подключение</string>
<string name="wc_common_loading">Загрузка</string>
@ -1569,14 +1572,15 @@
<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_alert_description">Ваши средства сейчас размещены в протоколе AAVE, но вы можете управлять ими в любое время.</string>
<string name="yield_module_alert_description">При активном режиме доходности все будущие депозиты на этот адрес будут направляться в Aave. Вы по-прежнему можете свободно управлять своими средствами.</string>
<string name="yield_module_alert_title">Ваш %s внесён в Aave</string>
<string name="yield_module_approve_needed_notification_cta">Выдать разрешение</string>
<string name="yield_module_approve_needed_notification_description">Что-то пошло не так с вашим предыдущим разрешением, поэтому требуется новое. Выберите, как хотите продолжить.</string>
<string name="yield_module_approve_needed_notification_description">Разрешение для вашего токена было отозвано. Выдайте его снова, чтобы продолжить работу сервиса.</string>
<string name="yield_module_approve_needed_notification_title">Необходимо разрешение</string>
<string name="yield_module_approve_sheet_fee_note">Комиссия будет списана, и ваши активы снова начнут приносить доход.</string>
<string name="yield_module_approve_sheet_subtitle">Чтобы продолжить зарабатывать, нужно выдать разрешение.</string>
<string name="yield_module_approve_sheet_title">Подтвердить разрешение</string>
<string name="yield_module_balance_info_sheet_subtitle">Ваши средства в данный момент размещены в протоколе Aave, но вы можете воспользоваться ими в любое время.</string>
<string name="yield_module_balance_info_sheet_title">Ваш %s внесён в Aave</string>
<string name="yield_module_chart_loading_error">Невозможно загрузить график</string>
<string name="yield_module_deposit_error_notification_title">Полученная сумма, %1$s %2$s, не была зачислена на Aave.</string>
@ -1599,6 +1603,7 @@
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem взимает комиссию за обслуживание в размере 3% от полученного дохода.</string>
<string name="yield_module_high_fee_error">Ваши средства будут автоматически переведены в Aave, как только комиссия сети снизится или баланс достигнет минимально необходимой суммы.</string>
<string name="yield_module_historical_returns">Историческая доходность</string>
<string name="yield_module_main_view_approve_notification_description">Разрешение для вашего токена в Yield сервисе было отозвано. Откройте токен, чтобы выдать разрешение снова.</string>
<string name="yield_module_main_view_approve_notification_title">Необходимо разрешение для токена</string>
<string name="yield_module_network_fee_unreachable_notification_description">Проверьте ваше интернет соединение</string>
<string name="yield_module_network_fee_unreachable_notification_title">Информация о комиссии недоступна</string>
@ -1632,13 +1637,13 @@
<string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">Пусть ваши деньги работают — зарабатывайте проценты на свой баланс.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">Проценты начисляются автоматически.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Доходность</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Режим доходности</string>
<string name="yield_module_token_details_earn_notification_processing">Отправка ваших средств</string>
<string name="yield_module_token_details_earn_notification_title">Пусть ваш баланс работает на вас!</string>
<string name="yield_module_transfer_mode_automatic">Автоматически</string>
<string name="yield_module_unable_to_cover_fee_description">Внесите немного %1$s %2$s, чтобы покрыть комиссию сети за транзакции.</string>
<string name="yield_module_unable_to_cover_fee_title">Невозможно покрыть комиссию в %s</string>
<string name="yield_module_unavailable_subtitle">Сервис начисления процентов в данный момент недоступен. Пожалуйста, попробуйте позже.</string>
<string name="yield_module_unavailable_title">Данные о доходе недоступны</string>
<string name="yield_module_unavailable_subtitle">Сервис доходности в данный момент недоступен. Пожалуйста, попробуйте позже.</string>
<string name="yield_module_unavailable_title">Режим доходности недоступен</string>
<string name="yield_supply_chart_loading_error">Невозможно загрузить график</string>
</resources>

View file

@ -1086,6 +1086,7 @@
<string name="story_web3_title">Web 3.0 сумісність</string>
<string name="sui_not_enough_coin_for_fee_description">Для відправки потрібна вхідна транзакція на суму не менше %1$s</string>
<string name="sui_not_enough_coin_for_fee_title">Недостатньо коштів</string>
<string name="swap_approve_description">Підтверджуючи, ви дозволяєте смартконтракту використовувати ваші токени в майбутніх транзакціях.</string>
<string name="swap_give_permission_fee_footer">Мережа стягує комісію за схвалення токену за підтвердження, що саме ви дозволяєте використовувати ваш токен для обміну.</string>
<string name="swap_promo_text">Обмінюйте більше токенів за вигіднішим курсом прямо у своєму гаманці.</string>
<string name="swap_promo_title">З\'явився новий провайдер обмінів!</string>
@ -1366,6 +1367,7 @@
<string name="wc_alert_wrong_card_description">Обрана не вірна картка або кільце</string>
<string name="wc_alert_wrong_card_title">Схоже, виникла проблема</string>
<string name="wc_all_dapps_disconnected">Усі dApps відключені</string>
<string name="wc_approve_description">Підтверджуючи, ви дозволяєте dApp або смартконтракту використовувати токени в майбутніх транзакціях.</string>
<string name="wc_common_address">Адреса</string>
<string name="wc_common_connect">Підключення</string>
<string name="wc_common_network">Мережа</string>
@ -1431,4 +1433,5 @@
<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_main_view_approve_notification_description">Дозвіл для вашого токена в режимі Yield було відкликано. Відкрийте токен, щоб надати дозвіл знову.</string>
</resources>

View file

@ -249,6 +249,7 @@
<string name="common_enable">Enable</string>
<string name="common_enabled">Enabled</string>
<string name="common_error">Error</string>
<string name="common_estimated_fee">Estimated fee</string>
<string name="common_exchange">Swap</string>
<string name="common_explore">Explore</string>
<string name="common_explore_transaction_history">Explore transaction history</string>
@ -484,7 +485,7 @@
<string name="express_provider">Provider</string>
<string name="express_provider_best_rate">Best rate</string>
<string name="express_provider_fca_warning_list">FCA Warning List</string>
<string name="express_provider_great_rate">Great rate</string>
<string name="express_provider_great_rate">Best choice</string>
<string name="express_provider_in_fca_warning_list">Provider in FCA warning list</string>
<string name="express_provider_max_amount">Available up to %s</string>
<string name="express_provider_min_amount">Available from %s</string>
@ -1369,7 +1370,7 @@
<string name="token_button_unavailability_reason_pending_transaction_send">Sending funds will be available once the pending transaction(s) in network %s is complete</string>
<string name="token_button_unavailability_reason_sell_unavailable">Selling %s is not supported by current providers, but we are working to add more options.</string>
<string name="token_button_unavailability_reason_staking_unavailable">Staking %s is not supported by current providers, but we are working to add more options.</string>
<string name="token_button_unavailability_reason_yield_supply_approval">Text here</string>
<string name="token_button_unavailability_reason_yield_supply_approval">Approval has been revoked. Your funds remain in Yield mode. To perform actions, please go to Yield mode and grant permission again.</string>
<string name="token_details_generate_xpub">Generate XPUB</string>
<string name="token_details_hide_alert_hide">Hide</string>
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
@ -1812,16 +1813,16 @@
<string name="xtz_withdrawal_message_ignore">No, send all</string>
<string name="xtz_withdrawal_message_reduce">Reduce by %s XTZ</string>
<string name="xtz_withdrawal_message_warning">To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ</string>
<string name="yield_module_alert_description">Your funds are currently supplied to the Aave protocol, but you can manage them at any time.</string>
<string name="yield_module_alert_description">With Yield mode active, all future deposits to this address will go to Aave. You can still manage your funds freely.</string>
<string name="yield_module_alert_title">Your %s is supplied to Aave</string>
<string name="yield_module_amount_not_transfered_to_aave_title">Supplying %1$s %2$s to Aave is pending</string>
<string name="yield_module_approve_needed_notification_cta">Give Approve</string>
<string name="yield_module_approve_needed_notification_description">Something went wrong with your previous approval, so we need a new one. Choose how youd like to proceed.</string>
<string name="yield_module_approve_needed_notification_description">Your tokens approval has been revoked. Grant it again to resume service functionality.</string>
<string name="yield_module_approve_needed_notification_title">Approve needed</string>
<string name="yield_module_approve_sheet_fee_note">The fee will be taken out, and your assets will be lent again.</string>
<string name="yield_module_approve_sheet_subtitle">To continue earning, approval is required.</string>
<string name="yield_module_approve_sheet_title">Confirm approval</string>
<string name="yield_module_balance_info_sheet_subtitle">Text about your money in balance [PLACEHOLDER]</string>
<string name="yield_module_balance_info_sheet_subtitle">Your funds are currently supplied to the Aave protocol, but you can manage them at any time.</string>
<string name="yield_module_balance_info_sheet_title">Your %s is deposited in Aave</string>
<string name="yield_module_chart_loading_error">Unable to load chart...</string>
<string name="yield_module_deposit_error_notification_title">The amount received, %1$s %2$s was not supplied to Aave.</string>
@ -1832,7 +1833,7 @@
<string name="yield_module_earn_sheet_high_fee_description">The network fee is currently too high to execute lending. Funds will be supplied once it drops to %1$s or below. </string>
<string name="yield_module_earn_sheet_my_funds_title">My funds</string>
<string name="yield_module_earn_sheet_provider_description">Your %1$s is now deposited in Aave and earning interest. You hold a%2$s token, which represents your balance and grows over time. When you deposit more funds, they\'ll be supplied to Aave to earn interest, minus a transaction fee.</string>
<string name="yield_module_earn_sheet_title">Earn</string>
<string name="yield_module_earn_sheet_title">Yield mode</string>
<string name="yield_module_earn_sheet_total_earnings_title">Total earnings</string>
<string name="yield_module_earn_sheet_transfers_title">Transfers to Aave</string>
<string name="yield_module_explore_sheet_explore_aave_button_title">Explore Aave</string>
@ -1842,14 +1843,14 @@
<string name="yield_module_fee_policy_sheet_fee_note">An approximate network fee of %1$s (%2$s) will be deducted from each future top-up, and it wont exceed your %3$s (%4$s) limit.</string>
<string name="yield_module_fee_policy_sheet_max_fee_note">If network fees rise above maximum fee, the transaction wont go through until they decrease. You can change this limit later.</string>
<string name="yield_module_fee_policy_sheet_max_fee_title">Maximum fee</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">The minimum amount is calculated from the current network fee to ensure it does not exceed 4%, which makes the minimum %1$s (%2$s).</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">The minimum amount is calculated from the current network fee to ensure it does not exceed 4%%, which makes the minimum %1$s (%2$s).</string>
<string name="yield_module_fee_policy_sheet_min_amount_title">Minimum top-up</string>
<string name="yield_module_fee_policy_sheet_title">Fee policy</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem also takes a 3% service fee on yield earned.</string>
<string name="yield_module_high_fee_error">Your funds will be automatically supplied to Aave once network fees are lower or your balance meets the minimum required amount.</string>
<string name="yield_module_historical_returns">Historical returns</string>
<string name="yield_module_main_view_approve_notification_description">Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]</string>
<string name="yield_module_main_view_approve_notification_title">Token approvals needed</string>
<string name="yield_module_main_view_approve_notification_description">Approval for your token in the Yield mode has been revoked. Open the token to grant permission again.</string>
<string name="yield_module_main_view_approve_notification_title">Token approval needed</string>
<string name="yield_module_network_fee_unreachable_notification_description">Check your network connection</string>
<string name="yield_module_network_fee_unreachable_notification_title">Network fee info unreachable</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">Every deposit you make will be supplied to Aave automatically.</string>
@ -1872,7 +1873,7 @@
<string name="yield_module_start_earning">Supply assets</string>
<string name="yield_module_start_earning_sheet_description">Your %s will be supplied to Aave, but will remain manageable.</string>
<string name="yield_module_start_earning_sheet_fee_policy">See fee policy</string>
<string name="yield_module_start_earning_sheet_next_deposits">Your next deposits will be automatically supplied to Aave.</string>
<string name="yield_module_start_earning_sheet_next_deposits">Your next top-ups will be automatically supplied to Aave.</string>
<string name="yield_module_status_active">Active</string>
<string name="yield_module_status_paused">Paused</string>
<string name="yield_module_stop_earning">Disable yield mode</string>
@ -1888,7 +1889,7 @@
<string name="yield_module_transfer_mode_automatic">Automatic</string>
<string name="yield_module_unable_to_cover_fee_description">Deposit some %1$s %2$s to cover the network fee for transactions</string>
<string name="yield_module_unable_to_cover_fee_title">Unable to cover %s fee</string>
<string name="yield_module_unavailable_subtitle">The interest service isnt available at the moment. Please try again later.</string>
<string name="yield_module_unavailable_title">Earnings unavailable</string>
<string name="yield_module_unavailable_subtitle">The Yield mode service isnt available at the moment. Please try again later.</string>
<string name="yield_module_unavailable_title">Yield mode unavailable</string>
<string name="yield_supply_chart_loading_error">Unable to load chart...</string>
</resources>

View file

@ -21,7 +21,7 @@ object TangemBlogUrlBuilder {
const val RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP = "https://tangem.com/en/blog/post/give-revoke-permission/"
const val YIELD_SUPPLY_HOW_IT_WORKS_URL = "https://tangem.com/en/blog/post/savings-account"
const val YIELD_SUPPLY_HOW_IT_WORKS_URL = "https://tangem.com/en/blog/post/yield-mode"
const val YIELD_SUPPLY_TOS_URL = "https://aave.com/terms-of-service"
const val YIELD_SUPPLY_PRIVACY_URL = "https://aave.com/privacy-policy"
}

View file

@ -2,7 +2,6 @@ package com.tangem.data.tokens.repository
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.common.*
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.data.tokens.converters.UtxoConverter
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@ -19,7 +18,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.isZero
import com.tangem.utils.extensions.orZero
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.math.BigDecimal
internal class DefaultCurrencyChecksRepository(
@ -168,27 +166,4 @@ internal class DefaultCurrencyChecksRepository(
else -> null
}
}
override suspend fun getProtocolBalance(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): BigDecimal? {
val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return null
val isActive = cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive ?: false
if (!isActive) return null
return runCatching {
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = token.network.toBlockchain(),
derivationPath = token.network.derivationPath.value,
) ?: error("Wallet manager not found")
walletManager.getEffectiveProtocolBalance(
token = Token(
symbol = token.symbol,
contractAddress = token.contractAddress,
decimals = token.decimals,
),
)
}.onFailure(Timber::e).getOrThrow()
}
}

View file

@ -16,11 +16,13 @@ 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.YieldSupplyEnterStatus
import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap
import kotlin.collections.map
internal class DefaultYieldSupplyRepository(
@ -30,6 +32,8 @@ internal class DefaultYieldSupplyRepository(
private val dispatchers: CoroutineDispatcherProvider,
) : YieldSupplyRepository {
private val statusMap: MutableMap<String, YieldSupplyEnterStatus> = ConcurrentHashMap()
override suspend fun getCachedMarkets(): List<YieldMarketToken>? = withContext(dispatchers.io) {
val cache = store.getSyncOrNull().orEmpty()
val domain = cache.map(YieldMarketTokenConverter::convert)
@ -97,6 +101,17 @@ internal class DefaultYieldSupplyRepository(
).getOrThrow().isActive
}
override suspend fun saveTokenProtocolStatus(
cryptoCurrency: CryptoCurrency,
yieldSupplyEnterStatus: YieldSupplyEnterStatus,
) {
statusMap[cryptoCurrency.id.value] = yieldSupplyEnterStatus
}
override fun getTokenProtocolStatus(cryptoCurrencyToken: CryptoCurrency): YieldSupplyEnterStatus? {
return statusMap[cryptoCurrencyToken.id.value]
}
private fun List<YieldMarketToken>.enrichNetworkIds(): List<YieldMarketToken> {
val chainIdMap = Blockchain.entries.associate { it.getChainId() to it.toNetworkId() }
return this.map { token ->

View file

@ -73,10 +73,4 @@ sealed class CryptoCurrencyWarning {
val requiredAmount: BigDecimal,
val currencyDecimals: Int,
) : CryptoCurrencyWarning()
data class YieldSupplyNotDepositedAmount(
val currency: CryptoCurrency,
val currencySymbol: String,
val amount: BigDecimal,
) : CryptoCurrencyWarning()
}

View file

@ -51,8 +51,7 @@ class GetCurrencyWarningsUseCase(
flowOf(currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus)),
flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)),
flowOf(currencyChecksRepository.getFeeResourceAmount(userWalletId, currency.network)),
flowOf(currencyChecksRepository.getProtocolBalance(userWalletId, currencyStatus)),
) { coinRelatedWarnings, maybeRentWarning, maybeEdWarning, maybeFeeResource, yieldSupplyProtocolBalance ->
) { coinRelatedWarnings, maybeRentWarning, maybeEdWarning, maybeFeeResource ->
setOfNotNull(
maybeRentWarning,
maybeEdWarning?.let { getExistentialDepositWarning(currency, it) },
@ -63,7 +62,6 @@ class GetCurrencyWarningsUseCase(
getBeaconChainShutdownWarning(rawId = currency.network.id.rawId),
getAssetRequirementsWarning(userWalletId = userWalletId, currency = currency),
getMigrationFromMaticToPolWarning(currency),
getYieldSupplyWarning(cryptoCurrencyStatus = currencyStatus, yieldSupplyProtocolBalance),
)
}.flowOn(dispatchers.io)
}
@ -263,28 +261,6 @@ class GetCurrencyWarningsUseCase(
}
}
private fun getYieldSupplyWarning(
cryptoCurrencyStatus: CryptoCurrencyStatus,
protocolBalance: BigDecimal?,
): CryptoCurrencyWarning? {
val value = cryptoCurrencyStatus.value
val isActive = value.yieldSupplyStatus?.isActive == true
val amount = value.amount
if (!isActive || protocolBalance == null || amount == null) return null
val notDepositedAmount = amount.minus(protocolBalance)
return if (notDepositedAmount > BigDecimal.ZERO) {
CryptoCurrencyWarning.YieldSupplyNotDepositedAmount(
currency = cryptoCurrencyStatus.currency,
amount = notDepositedAmount,
currencySymbol = cryptoCurrencyStatus.currency.symbol,
)
} else {
null
}
}
private fun BigDecimal?.isZero(): Boolean {
return this?.signum() == 0
}

View file

@ -61,11 +61,4 @@ interface CurrencyChecksRepository {
currencyStatus: CryptoCurrencyStatus,
balanceAfterTransaction: BigDecimal,
): CryptoCurrencyWarning.Rent?
/**
* Returns the YieldSupplied protocol balance in Aave for the given `cryptoCurrency`.
* This represents the amount supplied to the protocol for the specified `userWalletId`
* (e.g., aTokens balance). Returns null if not applicable or unknown.
*/
suspend fun getProtocolBalance(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus): BigDecimal?
}

View file

@ -0,0 +1,6 @@
package com.tangem.domain.yield.supply.models
enum class YieldSupplyEnterStatus {
Enter,
Exit,
}

View file

@ -3,6 +3,7 @@ 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.YieldSupplyEnterStatus
import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData
import kotlinx.coroutines.flow.Flow
@ -56,4 +57,30 @@ interface YieldSupplyRepository {
*/
@Throws
suspend fun deactivateProtocol(cryptoCurrencyToken: CryptoCurrency.Token): Boolean
/**
* Save the last user-initiated yield protocol action for the given currency.
*
* The saved value helps the UI render an intermediate "processing" state while waiting
* for the definitive protocol status to be fetched from the network. This information
* is transient and not intended to be persisted across app restarts.
*
* @param cryptoCurrencyToken the currency or token the action relates to
* @param yieldSupplyEnterStatus the last action intent: [YieldSupplyEnterStatus.Enter] or [YieldSupplyEnterStatus.Exit]
*/
suspend fun saveTokenProtocolStatus(
cryptoCurrencyToken: CryptoCurrency,
yieldSupplyEnterStatus: YieldSupplyEnterStatus,
)
/**
* Get the last saved user-initiated yield protocol action for the given currency, if any.
*
* Used to determine whether the UI should display an intermediate "processing" state
* until the protocol status retrieved from backend reflects the change.
*
* @param cryptoCurrencyToken the currency or token to query
* @return the last action intent or null if nothing has been recorded
*/
fun getTokenProtocolStatus(cryptoCurrencyToken: CryptoCurrency): YieldSupplyEnterStatus?
}

View file

@ -4,6 +4,7 @@ import arrow.core.Either
import arrow.core.Either.Companion.catch
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.currency.yieldSupplyKey
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.quotes.QuotesRepository
@ -51,7 +52,9 @@ class YieldSupplyGetMaxFeeUseCase(
?: error("Native fiat rate is missing")
require(nativeFiatRate > BigDecimal.ZERO) { "Native fiat rate must be > 0" }
val marketToken = yieldSupplyRepository.getTokenStatus(token)
val cachedMarketToken = yieldSupplyRepository.getCachedMarkets().orEmpty()
.firstOrNull { it.yieldSupplyKey == token.yieldSupplyKey() }
val marketToken = cachedMarketToken ?: yieldSupplyRepository.getTokenStatus(token)
val maxFeeNative = marketToken.maxFeeNative.toBigDecimal()
val rateRatio = nativeFiatRate.divide(

View file

@ -8,6 +8,7 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
@ -37,6 +38,7 @@ internal class SwapSelectTokensModel @Inject constructor(
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
) : Model() {
val state: StateFlow<SwapSelectTokensUM> = controller.state
@ -77,10 +79,14 @@ internal class SwapSelectTokensModel @Inject constructor(
selectedTokenItemState = selectedTokenItemState,
onRemoveClick = ::onRemoveFromTokenClick,
isAccountsMode = isAccountsMode,
account = getAccountCurrencyStatusUseCase.invokeSync(
userWalletId = params.userWalletId,
currency = status.currency,
).getOrNull()?.account,
account = if (accountsFeatureToggles.isFeatureEnabled) {
getAccountCurrencyStatusUseCase.invokeSync(
userWalletId = params.userWalletId,
currency = status.currency,
).getOrNull()?.account
} else {
null
},
),
)
}
@ -104,10 +110,14 @@ internal class SwapSelectTokensModel @Inject constructor(
transformer = SelectToTokenTransformer(
selectedTokenItemState = selectedTokenItemState,
isAccountsMode = isAccountsMode,
account = getAccountCurrencyStatusUseCase.invokeSync(
userWalletId = params.userWalletId,
currency = status.currency,
).getOrNull()?.account,
account = if (accountsFeatureToggles.isFeatureEnabled) {
getAccountCurrencyStatusUseCase.invokeSync(
userWalletId = params.userWalletId,
currency = status.currency,
).getOrNull()?.account
} else {
null
},
),
)

View file

@ -142,8 +142,8 @@ internal class SendAmountModel @Inject constructor(
) { newCryptoCurrencyStatus, account, isAccountsMode ->
maxAmountBoundary = MaxEnterAmountConverter().convert(newCryptoCurrencyStatus)
cryptoCurrencyStatus = newCryptoCurrencyStatus
initMinBoundary(cryptoCurrencyStatus, account, isAccountsMode)
}.flowOn(dispatchers.default)
initMinBoundary(newCryptoCurrencyStatus, account, isAccountsMode)
}.flowOn(dispatchers.main)
.launchIn(modelScope)
}

View file

@ -171,7 +171,7 @@ internal class StakingModel @Inject constructor(
}
private var isAccountInitialized: Boolean = true
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
private lateinit var cryptoCurrencyStatus: CryptoCurrencyStatus
private var stakingActions: List<StakingAction> = emptyList()
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
private var minimumTransactionAmount: EnterAmountBoundary? = null
@ -1299,6 +1299,7 @@ internal class StakingModel @Inject constructor(
}
private fun isCaseWithUnitializedTonAccount(): Boolean {
if (!::cryptoCurrencyStatus.isInitialized) return false
return value.currentStep == StakingStep.Confirmation &&
isTon(cryptoCurrencyStatus.currency.network.rawId) &&
!isAccountInitialized

View file

@ -19,7 +19,6 @@ import com.tangem.features.tokenreceive.entity.ReceiveAddress.Type.Ens
import com.tangem.features.tokenreceive.entity.ReceiveAddress.Type.Primary
import com.tangem.features.tokenreceive.ui.state.TokenReceiveUM
import com.tangem.utils.Provider
import com.tangem.utils.extensions.addIf
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
@ -138,12 +137,7 @@ internal class TokenReceiveStateFactory(
tokenName: String,
tokenReceiveNotification: List<TokenReceiveNotification>,
): List<NotificationUM> {
val yieldSupplyNotification: TokenReceiveNotification? =
tokenReceiveNotification.firstOrNull { it.isYieldSupplyNotification }
return buildList {
addIf(yieldSupplyNotification != null) {
NotificationUM.Warning.YieldSupplyIsActive(tokenName)
}
add(
NotificationUM.Info(
title = resourceReference(

View file

@ -156,10 +156,6 @@ internal class TokenDetailsNotificationConverter(
)
is CryptoCurrencyWarning.MigrationMaticToPol -> MigrationMaticToPol
is CryptoCurrencyWarning.UsedOutdatedDataWarning -> UsedOutdatedData
is CryptoCurrencyWarning.YieldSupplyNotDepositedAmount -> YieldSupplyNotTransferedToAave(
tokenName = warning.currencySymbol,
amount = warning.amount.format { crypto(symbol = "", decimals = warning.currency.decimals) },
)
}
}

View file

@ -123,6 +123,14 @@ private fun TokenList(
end = TangemTheme.dimens.spacing16,
)
// workaround to force recomposition of list items when the list is updated
// because sometimes items disappear after reordering in 1.7.4+ compose-runtime version
// check removing after update to compose-runtime 1.8.0+
val forceRecompose = remember { mutableIntStateOf(0) }
LaunchedEffect(state.items) {
forceRecompose.intValue++
}
LazyColumn(
modifier = Modifier
.align(Alignment.TopCenter)
@ -143,13 +151,15 @@ private fun TokenList(
}
}
DraggableItem(
index = index,
item = item,
reorderableState = reorderableListState,
onDragStart = onDragStart,
isBalanceHidden = isBalanceHidden,
)
key(forceRecompose.intValue) {
DraggableItem(
index = index,
item = item,
reorderableState = reorderableListState,
onDragStart = onDragStart,
isBalanceHidden = isBalanceHidden,
)
}
}
}

View file

@ -46,7 +46,7 @@ class DefaultUserWalletImageFetcher @Inject constructor(
override fun walletImage(walletId: UserWalletId, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> =
getUserWalletUseCase.invokeFlow(walletId)
.transformLatest {
.transform {
it.fold(
ifLeft = { emit(UserWalletItemUM.ImageState.Loading) },
ifRight = { wallet -> emitAll(walletImage(wallet, size)) },

View file

@ -1,39 +0,0 @@
package com.tangem.features.yield.supply.impl.common
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import javax.inject.Inject
import javax.inject.Singleton
/**
* Trigger for entering/exiting protocol from other components
*/
interface YieldSupplyProtocolTrigger {
suspend fun onEnterProtocol()
suspend fun onExitProtocol()
}
/**
* Listener to observe entering/exiting protocol events
*/
interface YieldSupplyProtocolListener {
val enterProtocolTriggerFlow: Flow<Unit>
val exitProtocolTriggerFlow: Flow<Unit>
}
@Singleton
internal class DefaultYieldSupplyProtocolTrigger @Inject constructor() :
YieldSupplyProtocolTrigger,
YieldSupplyProtocolListener {
override val enterProtocolTriggerFlow = MutableSharedFlow<Unit>()
override val exitProtocolTriggerFlow = MutableSharedFlow<Unit>()
override suspend fun onEnterProtocol() {
enterProtocolTriggerFlow.emit(Unit)
}
override suspend fun onExitProtocol() {
exitProtocolTriggerFlow.emit(Unit)
}
}

View file

@ -13,10 +13,12 @@ internal sealed class YieldSupplyFeeUM {
data object Error : YieldSupplyFeeUM()
data class Content(
val transactionDataList: ImmutableList<TransactionData.Uncompiled>,
val feeValue: TextReference,
val currentNetworkFeeValue: TextReference,
val maxNetworkFeeValue: TextReference,
val minAmountFeeValue: TextReference,
val feeFiatValue: TextReference,
val tokenFeeFiatValue: TextReference,
val maxNetworkFeeFiatValue: TextReference,
val minTopUpFiatValue: TextReference,
val feeNoteValue: TextReference = TextReference.EMPTY,
val minFeeNoteValue: TextReference = TextReference.EMPTY,
) : YieldSupplyFeeUM()
}

View file

@ -3,11 +3,13 @@ package com.tangem.features.yield.supply.impl.common.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withLink
@ -85,13 +87,17 @@ internal fun YieldSupplyActionContent(
),
) {
val fee = when (val yieldSupplyFeeUM = yieldSupplyActionUM.yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.feeValue
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.feeFiatValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
}
YieldSupplyFeeRow(
title = resourceReference(R.string.common_network_fee_title),
value = fee,
modifier = Modifier.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(horizontal = 16.dp, vertical = 12.dp),
)
}
}
@ -138,10 +144,11 @@ private class YieldSupplyActionContentPreviewProvider : PreviewParameterProvider
currencyIconState = CurrencyIconState.Loading,
yieldSupplyFeeUM = YieldSupplyFeeUM.Content(
transactionDataList = persistentListOf(),
feeValue = stringReference("0.00020 ETH • \$0.99"),
currentNetworkFeeValue = stringReference("1.45 USDT • \$1.45"),
maxNetworkFeeValue = stringReference("8.50 USDT • \$8.50"),
minAmountFeeValue = stringReference("50 USDT • \$50"),
feeFiatValue = stringReference("$0.99"),
tokenFeeFiatValue = stringReference("$1.45"),
maxNetworkFeeFiatValue = stringReference("$8.50"),
minTopUpFiatValue = stringReference("$50"),
feeNoteValue = TextReference.EMPTY,
),
isPrimaryButtonEnabled = false,
isTransactionSending = false,

View file

@ -1,17 +1,12 @@
package com.tangem.features.yield.supply.impl.common.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerWMax
@ -23,15 +18,11 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.yield.supply.impl.R
@Composable
internal fun YieldSupplyFeeRow(title: TextReference, value: TextReference?) {
internal fun YieldSupplyFeeRow(title: TextReference, value: TextReference?, modifier: Modifier = Modifier) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(horizontal = 16.dp, vertical = 12.dp),
modifier = modifier,
) {
Text(
text = title.resolveReference(),

View file

@ -3,10 +3,6 @@ package com.tangem.features.yield.supply.impl.di
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.yield.supply.impl.DefaultYieldSupplyFeatureToggles
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
import com.tangem.features.yield.supply.impl.common.DefaultYieldSupplyProtocolTrigger
import com.tangem.features.yield.supply.impl.common.YieldSupplyProtocolListener
import com.tangem.features.yield.supply.impl.common.YieldSupplyProtocolTrigger
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -22,17 +18,4 @@ internal object YieldSupplyFeatureModule {
fun provideYieldFeatureToggles(featureTogglesManager: FeatureTogglesManager): YieldSupplyFeatureToggles {
return DefaultYieldSupplyFeatureToggles(featureTogglesManager)
}
}
@InstallIn(SingletonComponent::class)
@Module
internal interface YieldSupplyProtocolModuleBinds {
@Singleton
@Binds
fun bindYieldSupplyProtocolTrigger(impl: DefaultYieldSupplyProtocolTrigger): YieldSupplyProtocolTrigger
@Singleton
@Binds
fun bindYieldSupplyProtocolListener(impl: DefaultYieldSupplyProtocolTrigger): YieldSupplyProtocolListener
}

View file

@ -24,7 +24,7 @@ internal sealed class YieldSupplyUM {
val subtitle: TextReference,
val rewardsApy: TextReference,
val onClick: () -> Unit,
val isAllowedToSpend: Boolean,
val showWarningIcon: Boolean,
) : YieldSupplyUM()
@Immutable

View file

@ -15,12 +15,14 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.models.currency.yieldSupplyNotAllAmountSupplied
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.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldSupplyEnterStatus
import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
@ -28,7 +30,6 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyIsAvailableUseCase
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.api.YieldSupplyComponent
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
import com.tangem.features.yield.supply.impl.common.YieldSupplyProtocolListener
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
import com.tangem.features.yield.supply.impl.main.model.transformers.YieldSupplyTokenStatusSuccessTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -58,7 +59,7 @@ internal class YieldSupplyModel @Inject constructor(
private val yieldSupplyIsAvailableUseCase: YieldSupplyIsAvailableUseCase,
private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase,
private val yieldSupplyDeactivateUseCase: YieldSupplyDeactivateUseCase,
private val yieldSupplyProtocolListener: YieldSupplyProtocolListener,
private val yieldSupplyRepository: YieldSupplyRepository,
) : Model(), YieldSupplyClickIntents {
private val params = paramsContainer.require<YieldSupplyComponent.Params>()
@ -86,38 +87,6 @@ internal class YieldSupplyModel @Inject constructor(
init {
checkIfYieldSupplyIsAvailable()
observeProtocolEvents()
}
private fun observeProtocolEvents() {
yieldSupplyProtocolListener.exitProtocolTriggerFlow.onEach {
uiState.update {
YieldSupplyUM.Processing.Exit
}
coroutineScope.launch(dispatchers.io) {
delay(PROCESSING_UPDATE_DELAY)
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
),
)
}
}.launchIn(modelScope)
yieldSupplyProtocolListener.enterProtocolTriggerFlow.onEach {
uiState.update {
YieldSupplyUM.Processing.Enter
}
coroutineScope.launch(dispatchers.io) {
delay(PROCESSING_UPDATE_DELAY)
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
),
)
}
}.launchIn(modelScope)
}
private fun checkIfYieldSupplyIsAvailable() {
@ -141,7 +110,6 @@ internal class YieldSupplyModel @Inject constructor(
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = false,
).onEach { maybeCryptoCurrency ->
Timber.tag("getSingleCryptoCurrencyStatusUseCase").d("update $maybeCryptoCurrency")
maybeCryptoCurrency.fold(
ifRight = { cryptoCurrencyStatus ->
cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus }
@ -174,7 +142,7 @@ internal class YieldSupplyModel @Inject constructor(
)
}.onLeft {
Timber.e(it)
uiState.update { YieldSupplyUM.Unavailable }
uiState.update { YieldSupplyUM.Initial }
}
}
}
@ -213,53 +181,56 @@ internal class YieldSupplyModel @Inject constructor(
@Suppress("MaximumLineLength")
private fun onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus: CryptoCurrencyStatus) {
val yieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
val hasActiveTransaction = cryptoCurrencyStatus.value.hasCurrentNetworkTransactions
val yieldTransaction = cryptoCurrencyStatus.value.pendingTransactions.firstOrNull {
it.type is TxInfo.TransactionType.YieldSupply
}?.type as? TxInfo.TransactionType.YieldSupply
sendInfoAboutProtocolStatus(cryptoCurrencyStatus)
val tokenProtocolStatus = yieldSupplyRepository.getTokenProtocolStatus(cryptoCurrency)
val isActive = yieldSupplyStatus?.isActive == true
when {
hasActiveTransaction && yieldTransaction != null -> {
coroutineScope.launch(dispatchers.io) {
delay(PROCESSING_UPDATE_DELAY)
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
),
)
}
uiState.update {
when (yieldTransaction) {
TxInfo.TransactionType.YieldSupply.Enter -> YieldSupplyUM.Processing.Enter
TxInfo.TransactionType.YieldSupply.Exit -> YieldSupplyUM.Processing.Exit
}
}
!isActive && tokenProtocolStatus == YieldSupplyEnterStatus.Enter -> {
uiState.update { YieldSupplyUM.Processing.Enter }
fetchCurrencyWithDelay()
}
yieldSupplyStatus?.isActive == true -> {
val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return
if (!yieldSupplyStatus.isAllowedToSpend) {
analyticsEventsHandler.send(
YieldSupplyAnalytics.NoticeApproveNeeded(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
),
)
}
loadActiveState(
cryptoCurrencyToken = cryptoCurrencyToken,
yieldSupplyStatus = yieldSupplyStatus,
)
isActive && tokenProtocolStatus == YieldSupplyEnterStatus.Exit -> {
uiState.update { YieldSupplyUM.Processing.Exit }
fetchCurrencyWithDelay()
}
else -> {
loadTokenStatus()
sendInfoAboutProtocolStatus(cryptoCurrencyStatus)
if (isActive) {
loadActiveState(
cryptoCurrencyStatus = cryptoCurrencyStatus,
yieldSupplyStatus = requireNotNull(yieldSupplyStatus),
)
} else {
loadTokenStatus()
}
}
}
}
private fun loadActiveState(cryptoCurrencyToken: CryptoCurrency.Token, yieldSupplyStatus: YieldSupplyStatus) {
private fun fetchCurrencyWithDelay() {
coroutineScope.launch(dispatchers.io) {
delay(PROCESSING_UPDATE_DELAY)
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
),
)
}
}
private fun loadActiveState(cryptoCurrencyStatus: CryptoCurrencyStatus, yieldSupplyStatus: YieldSupplyStatus) {
val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return
val showWarningIcon = !yieldSupplyStatus.isAllowedToSpend ||
cryptoCurrencyStatus.yieldSupplyNotAllAmountSupplied()
if (!yieldSupplyStatus.isAllowedToSpend) {
analyticsEventsHandler.send(
YieldSupplyAnalytics.NoticeApproveNeeded(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
),
)
}
modelScope.launch(dispatchers.default) {
yieldSupplyGetTokenStatusUseCase(cryptoCurrencyToken)
.onRight { tokenStatus ->
@ -278,7 +249,7 @@ internal class YieldSupplyModel @Inject constructor(
stringReference(" ${tokenStatus.apy}%"),
),
onClick = ::onActiveClick,
isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend,
showWarningIcon = showWarningIcon,
apy = tokenStatus.apy.toString(),
)
}
@ -294,7 +265,7 @@ internal class YieldSupplyModel @Inject constructor(
),
rewardsApy = TextReference.EMPTY,
onClick = ::onActiveClick,
isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend,
showWarningIcon = showWarningIcon,
apy = "",
)
}

View file

@ -132,7 +132,7 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content) {
)
}
SpacerW8()
AnimatedVisibility(supplyUM.isAllowedToSpend.not()) {
AnimatedVisibility(supplyUM.showWarningIcon) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_alert_triangle_20),
contentDescription = null,
@ -317,7 +317,7 @@ private class PreviewProvider : PreviewParameterProvider<YieldSupplyUM> {
rewardsApy = stringReference("5.1 % APY"),
onClick = {},
apy = "5.1",
isAllowedToSpend = false,
showWarningIcon = false,
),
YieldSupplyUM.Content(
title = stringReference("Aave lending is active"),
@ -325,7 +325,7 @@ private class PreviewProvider : PreviewParameterProvider<YieldSupplyUM> {
rewardsApy = stringReference("5.1 % APY"),
onClick = {},
apy = "5.1",
isAllowedToSpend = true,
showWarningIcon = true,
),
YieldSupplyUM.Loading,
YieldSupplyUM.Processing.Enter,

View file

@ -50,6 +50,7 @@ internal class YieldSupplyActiveComponent(
state = state,
isBalanceHidden = isBalanceHidden,
chartComponent = chartComponent,
onReadMoreClick = params.callback::onReadMoreClick,
modifier = Modifier,
)
}
@ -76,5 +77,6 @@ internal class YieldSupplyActiveComponent(
fun onBackClick()
fun onStopEarning()
fun onApprove()
fun onReadMoreClick()
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.features.yield.supply.impl.subcomponents.active.entity
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
internal data class YieldSupplyActiveContentUM(
val totalEarnings: TextReference,
@ -9,10 +10,11 @@ internal data class YieldSupplyActiveContentUM(
val providerTitle: TextReference,
val subtitle: TextReference,
val subtitleLink: TextReference,
val notificationUM: NotificationUM?,
val notifications: ImmutableList<NotificationUM>,
val minAmount: TextReference?,
val currentFee: TextReference?,
val feeDescription: TextReference?,
val minFeeDescription: TextReference?,
val apy: TextReference? = null,
val isHighFee: Boolean = false,
)

View file

@ -4,10 +4,12 @@ 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.core.navigation.url.UrlOpener
import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveComponent
import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveEntryComponent
import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyApproveComponent
import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent
import com.tangem.utils.TangemBlogUrlBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
@ -16,6 +18,7 @@ internal class YieldSupplyActiveEntryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
paramsContainer: ParamsContainer,
private val router: Router,
private val urlOpener: UrlOpener,
) : Model(), YieldSupplyActiveComponent.ModelCallback,
YieldSupplyStopEarningComponent.ModelCallback,
YieldSupplyApproveComponent.ModelCallback {
@ -37,4 +40,8 @@ internal class YieldSupplyActiveEntryModel @Inject constructor(
override fun onApprove() {
router.push(YieldSupplyActiveRoute.Approve)
}
override fun onReadMoreClick() {
urlOpener.openUrl(TangemBlogUrlBuilder.YIELD_SUPPLY_HOW_IT_WORKS_URL)
}
}

View file

@ -12,11 +12,11 @@ import com.tangem.core.ui.extensions.resourceReference
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.fiat
import com.tangem.core.ui.format.bigdecimal.format
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.yield.supply.usecase.YieldSupplyGetProtocolBalanceUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase
@ -24,14 +24,20 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyGetCurrentFeeUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetMaxFeeUseCase
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.common.formatter.YieldSupplyAmountFormatter
import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveComponent
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
import com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers.YieldSupplyActiveMinAmountTransformer
import com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers.YieldSupplyActiveFeeContentTransformer
import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
@Suppress("LongParameterList")
@ -65,11 +71,12 @@ internal class YieldSupplyActiveModel @Inject constructor(
formatArgs = wrappedList(cryptoCurrency.symbol, cryptoCurrency.symbol),
),
subtitleLink = resourceReference(R.string.common_read_more),
notificationUM = null,
notifications = persistentListOf(),
minAmount = null,
currentFee = null,
feeDescription = null,
isHighFee = false,
minFeeDescription = null,
),
)
@ -108,27 +115,13 @@ internal class YieldSupplyActiveModel @Inject constructor(
cryptoCurrency = cryptoCurrency,
).getOrNull()
val approvalNotification = if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isAllowedToSpend != true) {
NotificationUM.Error(
title = resourceReference(R.string.yield_module_approve_needed_notification_title),
subtitle = resourceReference(R.string.yield_module_approve_needed_notification_description),
iconResId = R.drawable.ic_alert_triangle_20,
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
text = resourceReference(R.string.yield_module_approve_needed_notification_cta),
onClick = params.callback::onApprove,
),
)
} else {
null
}
loadApy()
loadMinAmount()
loadFees()
uiState.update {
it.copy(
notificationUM = approvalNotification,
notifications = getNotifications(cryptoCurrencyStatus),
availableBalance = stringReference(
protocolBalance.format {
crypto(
@ -153,31 +146,77 @@ internal class YieldSupplyActiveModel @Inject constructor(
)
}
}.onLeft {
uiState.update {
it.copy(
apy = TextReference.Str(DASH_SIGN),
)
}
Timber.e("Error loading token status")
}
}
}
private fun getNotifications(cryptoCurrencyStatus: CryptoCurrencyStatus): ImmutableList<NotificationUM> {
val approvalNotification = if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isAllowedToSpend != true) {
NotificationUM.Error(
title = resourceReference(R.string.yield_module_approve_needed_notification_title),
subtitle = resourceReference(R.string.yield_module_approve_needed_notification_description),
iconResId = R.drawable.ic_alert_triangle_20,
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
text = resourceReference(R.string.yield_module_approve_needed_notification_cta),
onClick = params.callback::onApprove,
),
)
} else {
null
}
val notSuppliedNotification = getNotSuppliedNotification(cryptoCurrencyStatus)
return listOfNotNull(
approvalNotification,
notSuppliedNotification,
).toPersistentList()
}
private fun getNotSuppliedNotification(cryptoCurrencyStatus: CryptoCurrencyStatus): NotificationUM? {
val value = cryptoCurrencyStatus.value
val isActive = value.yieldSupplyStatus?.isActive == true
val effectiveProtocolBalance = value.yieldSupplyStatus?.effectiveProtocolBalance ?: null
val amount = value.amount
if (!isActive || effectiveProtocolBalance == null || amount == null) return null
val notDepositedAmount = amount.minus(effectiveProtocolBalance)
return if (notDepositedAmount > BigDecimal.ZERO) {
val formattedAmount =
notDepositedAmount.format { crypto(symbol = "", decimals = cryptoCurrencyStatus.currency.decimals) }
NotificationUM.Warning.YieldSupplyNotAllAmountSupplied(
formattedAmount = formattedAmount,
symbol = cryptoCurrency.symbol,
)
} else {
null
}
}
private fun loadMinAmount() {
modelScope.launch(dispatchers.default) {
yieldSupplyMinAmountUseCase(
params.userWallet,
cryptoCurrencyStatusFlow.value,
).onRight { minAmount ->
val minAmountTextReference = YieldSupplyAmountFormatter(
cryptoCurrencyStatusFlow.value.currency,
appCurrency,
).invoke(
feeValue = minAmount,
fiatRate = cryptoCurrencyStatusFlow.value.value.fiatRate,
showCrypto = false,
uiState.update(
YieldSupplyActiveMinAmountTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value,
appCurrency = appCurrency,
minAmount = minAmount,
),
)
uiState.update {
it.copy(minAmount = minAmountTextReference)
}
}.onLeft {
uiState.update {
it.copy(minAmount = TextReference.Str(DASH_SIGN))
it.copy(
minAmount = TextReference.Str(DASH_SIGN),
feeDescription = null,
)
}
}
}
@ -197,33 +236,23 @@ internal class YieldSupplyActiveModel @Inject constructor(
cryptoCurrencyStatus = cryptoStatus,
).getOrNull()
val currentFeeText = currentFee?.let {
YieldSupplyAmountFormatter(
cryptoStatus.currency,
appCurrency,
).invoke(
feeValue = it,
fiatRate = cryptoStatus.value.fiatRate,
showCrypto = false,
if (currentFee != null && maxFee != null) {
uiState.update(
YieldSupplyActiveFeeContentTransformer(
cryptoCurrencyStatus = cryptoStatus,
appCurrency = appCurrency,
feeValue = currentFee,
maxNetworkFee = maxFee,
),
)
}
val isHighFee = if (currentFee != null && maxFee != null) currentFee > maxFee else false
val maxFiatFee = maxFee?.multiply(cryptoStatus.value.fiatRate)
.format { fiat(appCurrency.code, appCurrency.symbol) }
val feeDescription = if (isHighFee) {
resourceReference(R.string.yield_module_earn_sheet_high_fee_description, wrappedList(maxFiatFee))
} else {
resourceReference(R.string.yield_module_earn_sheet_fee_description, wrappedList(maxFiatFee))
}
uiState.update {
it.copy(
currentFee = currentFeeText ?: stringReference(DASH_SIGN),
isHighFee = isHighFee,
feeDescription = feeDescription,
)
uiState.update {
it.copy(
currentFee = stringReference(DASH_SIGN),
feeDescription = null,
isHighFee = false,
)
}
}
}
}

View file

@ -0,0 +1,57 @@
package com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers
import com.tangem.core.ui.extensions.resourceReference
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.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
import com.tangem.utils.transformer.Transformer
import java.math.BigDecimal
/**
* Computes fee description and current fee values for Active screen based on token rates.
*/
internal class YieldSupplyActiveFeeContentTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val appCurrency: AppCurrency,
private val feeValue: BigDecimal,
private val maxNetworkFee: BigDecimal,
) : Transformer<YieldSupplyActiveContentUM> {
override fun transform(prevState: YieldSupplyActiveContentUM): YieldSupplyActiveContentUM {
val cryptoCurrency = cryptoCurrencyStatus.currency
val tokenFiatRate = cryptoCurrencyStatus.value.fiatRate
val tokenCryptoFeeValueText = feeValue.format { crypto(cryptoCurrency) }
val tokenFiatFee = tokenFiatRate?.let(feeValue::multiply)
val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
val maxFeeCryptoValueText = maxNetworkFee.format { crypto(cryptoCurrency) }
val maxFiatFee = tokenFiatRate?.let { rate -> maxNetworkFee.multiply(rate) }
val maxFiatFeeValueText = maxFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
val feeNoteValue: TextReference = resourceReference(
id = R.string.yield_module_fee_policy_sheet_fee_note,
formatArgs = wrappedList(
stringReference(tokenFiatFeeValueText),
stringReference(tokenCryptoFeeValueText),
stringReference(maxFiatFeeValueText),
stringReference(maxFeeCryptoValueText),
),
)
val isHighFee = feeValue > maxNetworkFee
return prevState.copy(
currentFee = stringReference(tokenFiatFeeValueText),
feeDescription = feeNoteValue,
isHighFee = isHighFee,
)
}
}

View file

@ -0,0 +1,46 @@
package com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers
import com.tangem.core.ui.extensions.resourceReference
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.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
import com.tangem.utils.transformer.Transformer
import java.math.BigDecimal
/**
* Computes and sets minimum amount (fiat) and fee description note
*/
internal class YieldSupplyActiveMinAmountTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val appCurrency: AppCurrency,
private val minAmount: BigDecimal,
) : Transformer<YieldSupplyActiveContentUM> {
override fun transform(prevState: YieldSupplyActiveContentUM): YieldSupplyActiveContentUM {
val cryptoCurrency = cryptoCurrencyStatus.currency
val tokenFiatRate = cryptoCurrencyStatus.value.fiatRate
val minAmountCryptoText = minAmount.format { crypto(cryptoCurrency) }
val minAmountFiat = tokenFiatRate?.let(minAmount::multiply)
val minAmountFiatText = minAmountFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
val minFeeNoteValue = resourceReference(
id = R.string.yield_module_fee_policy_sheet_min_amount_note,
formatArgs = wrappedList(
minAmountFiatText,
minAmountCryptoText,
),
)
return prevState.copy(
minAmount = stringReference(minAmountFiatText),
minFeeDescription = minFeeNoteValue,
)
}
}

View file

@ -28,17 +28,20 @@ import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun YieldSupplyActiveContent(
state: YieldSupplyActiveContentUM,
isBalanceHidden: Boolean,
onReadMoreClick: () -> Unit,
chartComponent: ComposableContentComponent,
modifier: Modifier = Modifier,
) {
@ -61,16 +64,23 @@ internal fun YieldSupplyActiveContent(
chartComponent.Content(Modifier.padding(bottom = 12.dp))
}
AnimatedVisibility(state.notificationUM != null) {
val wrappedNotification = remember(this) { requireNotNull(state.notificationUM) }
Notification(
config = wrappedNotification.config,
iconTint = null,
containerColor = TangemTheme.colors.background.action,
)
AnimatedVisibility(state.notifications.isNotEmpty()) {
val notifications = remember(state.notifications) { state.notifications }
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
notifications.forEach { notificationUM ->
Notification(
config = notificationUM.config,
containerColor = TangemTheme.colors.background.action,
)
}
}
}
YieldSupplyActiveMyFunds(state = state, isBalanceHidden = isBalanceHidden)
YieldSupplyActiveMyFunds(
state = state,
isBalanceHidden = isBalanceHidden,
onReadMoreClick = onReadMoreClick,
)
AnimatedVisibility(state.feeDescription != null) {
Text(
@ -81,12 +91,14 @@ internal fun YieldSupplyActiveContent(
)
}
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = stringResourceSafe(R.string.yield_module_fee_policy_sheet_min_amount_note),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
AnimatedVisibility(state.minFeeDescription != null) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = state.minFeeDescription?.resolveReference().orEmpty(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}
@ -108,7 +120,7 @@ private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) {
TextShimmer(
modifier = modifier.width(94.dp),
text = "",
style = TangemTheme.typography.head,
style = TangemTheme.typography.h2,
)
} else {
Row(verticalAlignment = Alignment.CenterVertically) {
@ -123,7 +135,7 @@ private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) {
Text(
modifier = modifier,
text = apyText,
style = TangemTheme.typography.body1,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.accent,
)
}
@ -134,7 +146,11 @@ private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) {
@Suppress("LongMethod")
@Composable
private fun YieldSupplyActiveMyFunds(state: YieldSupplyActiveContentUM, isBalanceHidden: Boolean) {
private fun YieldSupplyActiveMyFunds(
state: YieldSupplyActiveContentUM,
onReadMoreClick: () -> Unit,
isBalanceHidden: Boolean,
) {
Column(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
@ -164,7 +180,7 @@ private fun YieldSupplyActiveMyFunds(state: YieldSupplyActiveContentUM, isBalanc
)
}
SpacerH8()
DescriptionText(state = state)
DescriptionText(state = state, onReadMoreClick = onReadMoreClick)
SpacerH8()
HorizontalDivider(
thickness = 0.5.dp,
@ -206,14 +222,14 @@ private fun YieldSupplyActiveMyFunds(state: YieldSupplyActiveContentUM, isBalanc
}
@Composable
private fun DescriptionText(state: YieldSupplyActiveContentUM) {
private fun DescriptionText(state: YieldSupplyActiveContentUM, onReadMoreClick: () -> Unit) {
val subtitle = annotatedReference {
append(state.subtitle.resolveReference())
appendSpace()
withLink(
LinkAnnotation.Clickable(
tag = "LINK_TAG",
linkInteractionListener = {},
linkInteractionListener = { onReadMoreClick() },
),
{
appendColored(
@ -324,6 +340,7 @@ private fun YieldSupplyActiveBottomSheet_Preview(
state = params,
isBalanceHidden = true,
chartComponent = ComposableContentComponent.EMPTY,
onReadMoreClick = {},
)
}
}
@ -340,15 +357,31 @@ private class YieldSupplyActiveBottomSheetPreviewProvider : PreviewParameterProv
wrappedList("USDT", "USDT"),
),
subtitleLink = resourceReference(R.string.common_read_more),
notificationUM = NotificationUM.Error.InvalidAmount,
apy = stringReference("5,14%"),
notifications = persistentListOf(
NotificationUM.Error.InvalidAmount,
NotificationUM.Info(
title = resourceReference(
R.string.yield_module_amount_not_transfered_to_aave_title,
wrappedList(
"0,03",
),
),
subtitle = TextReference.EMPTY,
iconTint = NotificationConfig.IconTint.Accent,
),
),
minAmount = stringReference("50 USDT"),
isHighFee = true,
currentFee = stringReference("30 USDT"),
feeDescription = stringReference(
"The network fee is currently too high to execute lending." +
"Funds will be supplied once it drops to \$12 or below. ",
),
currentFee = stringReference("30 USDT"),
apy = stringReference("5,14%"),
isHighFee = true,
minFeeDescription = stringReference(
"The network fee is currently too high to execute lending." +
"Funds will be supplied once it drops to \$12 or below. ",
),
),
)
}

View file

@ -10,7 +10,6 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -33,7 +32,7 @@ import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyAp
import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSupplyNotificationsComponent
import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSupplyNotificationsUpdateTrigger
import com.tangem.features.yield.supply.impl.subcomponents.notifications.entity.YieldSupplyNotificationData
import com.tangem.utils.StringsSigns.DOT
import com.tangem.core.ui.extensions.TextReference
import com.tangem.utils.TangemBlogUrlBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
@ -220,7 +219,6 @@ internal class YieldSupplyApproveModel @Inject constructor(
val feeFiatValue = feeCryptoCurrencyStatus.value.fiatRate?.let { rate ->
feeCryptoValue?.multiply(rate)
}
val cryptoFee = feeCryptoValue.format { crypto(feeCryptoCurrencyStatus.currency) }
val fiatFee = feeFiatValue.format { fiat(appCurrency.code, appCurrency.symbol) }
uiState.update {
@ -233,14 +231,11 @@ internal class YieldSupplyApproveModel @Inject constructor(
transactionDataList = persistentListOf(
approvalTransitionData.copy(fee = transactionFee.normal),
),
feeValue = combinedReference(
stringReference(cryptoFee),
stringReference(" $DOT "),
stringReference(fiatFee),
),
currentNetworkFeeValue = TextReference.EMPTY,
maxNetworkFeeValue = TextReference.EMPTY,
minAmountFeeValue = TextReference.EMPTY,
feeFiatValue = stringReference(fiatFee),
tokenFeeFiatValue = TextReference.EMPTY,
maxNetworkFeeFiatValue = TextReference.EMPTY,
minTopUpFiatValue = TextReference.EMPTY,
feeNoteValue = TextReference.EMPTY,
),
)
}

View file

@ -38,7 +38,6 @@ internal class YieldSupplyFeePolicyComponent(
YieldSupplyFeePolicyContent(
yieldSupplyFeeUM = uiState.yieldSupplyFeeUM,
tokenSymbol = params.cryptoCurrency.symbol,
networkName = params.cryptoCurrency.network.name,
modifier = modifier,
)
}

View file

@ -6,10 +6,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -34,7 +36,6 @@ import kotlinx.collections.immutable.persistentListOf
internal fun YieldSupplyFeePolicyContent(
yieldSupplyFeeUM: YieldSupplyFeeUM,
tokenSymbol: String,
networkName: String,
modifier: Modifier = Modifier,
) {
Column(
@ -62,68 +63,51 @@ internal fun YieldSupplyFeePolicyContent(
modifier = Modifier.padding(horizontal = 16.dp),
)
SpacerH24()
FooterContainer(
footer = resourceReference(
id = R.string.yield_module_fee_policy_sheet_min_amount_note,
formatArgs = wrappedList(networkName),
),
paddingValues = PaddingValues(
top = 8.dp,
start = 12.dp,
end = 12.dp,
),
) {
val minAmount = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.minAmountFeeValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
}
YieldSupplyFeeRow(
title = resourceReference(R.string.yield_module_fee_policy_sheet_min_amount_title),
value = minAmount,
)
}
MinFeeNote(yieldSupplyFeeUM)
SpacerH16()
val feeNote = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.feeNoteValue
YieldSupplyFeeUM.Error -> null
YieldSupplyFeeUM.Loading -> null
}
FooterContainer(
footer = resourceReference(
id = R.string.yield_module_fee_policy_sheet_current_fee_note,
formatArgs = wrappedList(networkName),
),
footer = feeNote,
paddingValues = PaddingValues(
top = 8.dp,
start = 12.dp,
end = 12.dp,
),
) {
val currentFee = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.currentNetworkFeeValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(horizontal = 16.dp),
) {
val currentFee = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.tokenFeeFiatValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
}
YieldSupplyFeeRow(
title = resourceReference(R.string.yield_module_fee_policy_sheet_current_fee_title),
value = currentFee,
modifier = Modifier.padding(vertical = 12.dp),
)
val maxFee = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.maxNetworkFeeFiatValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
}
YieldSupplyFeeRow(
title = resourceReference(R.string.yield_module_fee_policy_sheet_max_fee_title),
value = maxFee,
modifier = Modifier.padding(vertical = 12.dp),
)
}
YieldSupplyFeeRow(
title = resourceReference(R.string.yield_module_fee_policy_sheet_current_fee_title),
value = currentFee,
)
}
SpacerH16()
FooterContainer(
footer = resourceReference(R.string.yield_module_fee_policy_sheet_max_fee_note),
paddingValues = PaddingValues(
top = 8.dp,
start = 12.dp,
end = 12.dp,
),
) {
val maxFee = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.maxNetworkFeeValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
}
YieldSupplyFeeRow(
title = resourceReference(R.string.yield_module_fee_policy_sheet_max_fee_title),
value = maxFee,
)
}
SpacerH8()
Text(
text = stringResourceSafe(R.string.yield_module_fee_policy_tangem_service_fee_title),
style = TangemTheme.typography.caption2,
@ -139,6 +123,38 @@ internal fun YieldSupplyFeePolicyContent(
}
}
@Composable
private fun MinFeeNote(yieldSupplyFeeUM: YieldSupplyFeeUM) {
val minFeeNote = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.minFeeNoteValue
YieldSupplyFeeUM.Error -> null
YieldSupplyFeeUM.Loading -> null
}
FooterContainer(
footer = minFeeNote,
paddingValues = PaddingValues(
top = 8.dp,
start = 12.dp,
end = 12.dp,
),
) {
val minAmount = when (yieldSupplyFeeUM) {
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.minTopUpFiatValue
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
YieldSupplyFeeUM.Loading -> null
}
YieldSupplyFeeRow(
title = resourceReference(R.string.yield_module_fee_policy_sheet_min_amount_title),
value = minAmount,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(horizontal = 16.dp, vertical = 12.dp),
)
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@ -148,13 +164,28 @@ private fun YieldSupplyFeePolicyContent_Preview() {
YieldSupplyFeePolicyContent(
yieldSupplyFeeUM = YieldSupplyFeeUM.Content(
transactionDataList = persistentListOf(),
feeValue = stringReference("0.0001 ETH • \$1.45"),
maxNetworkFeeValue = stringReference("8.50 USDT • \$8.50"),
currentNetworkFeeValue = stringReference("1.45 USDT • \$1.45"),
minAmountFeeValue = stringReference("50 USDT • \$50"),
feeFiatValue = stringReference("$1.45"),
maxNetworkFeeFiatValue = stringReference("$8.50"),
tokenFeeFiatValue = stringReference("$1.45"),
minTopUpFiatValue = stringReference("$50"),
feeNoteValue = resourceReference(
id = R.string.yield_module_fee_policy_sheet_fee_note,
formatArgs = wrappedList(
stringReference("$1.45"),
stringReference("1.46 USDT"),
stringReference("$8.50"),
stringReference("8.50 USDT"),
),
),
minFeeNoteValue = resourceReference(
id = R.string.yield_module_fee_policy_sheet_min_amount_note,
formatArgs = wrappedList(
stringReference("$2.45"),
stringReference("2.46 USDT"),
),
),
),
tokenSymbol = "USDT",
networkName = "Ethereum",
modifier = Modifier.background(TangemTheme.colors.background.primary),
)
}

View file

@ -20,6 +20,8 @@ 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
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldSupplyEnterStatus
import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyEstimateEnterFeeUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
@ -28,7 +30,6 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyStartEarningUseCase
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
import com.tangem.features.yield.supply.impl.common.YieldSupplyAlertFactory
import com.tangem.features.yield.supply.impl.common.YieldSupplyProtocolTrigger
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
import com.tangem.features.yield.supply.impl.common.entity.transformer.YieldSupplyTransactionInProgressTransformer
@ -66,7 +67,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase,
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase,
private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase,
private val yieldSupplyProtocolTrigger: YieldSupplyProtocolTrigger,
private val yieldSupplyRepository: YieldSupplyRepository,
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
private val params: YieldSupplyStartEarningComponent.Params = paramsContainer.require()
@ -252,7 +253,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
)
},
ifRight = {
yieldSupplyProtocolTrigger.onEnterProtocol()
yieldSupplyRepository.saveTokenProtocolStatus(cryptoCurrency, YieldSupplyEnterStatus.Enter)
analytics.send(YieldSupplyAnalytics.FundsEarned)
yieldSupplyActivateUseCase(cryptoCurrency)
modelScope.launch {

View file

@ -1,17 +1,17 @@
package com.tangem.features.yield.supply.impl.subcomponents.startearning.model.transformers
import com.tangem.blockchain.common.TransactionData
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference
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.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
import com.tangem.features.yield.supply.impl.common.formatter.YieldSupplyAmountFormatter
import com.tangem.utils.StringsSigns.DOT
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toPersistentList
import java.math.BigDecimal
@ -29,30 +29,46 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
) : Transformer<YieldSupplyActionUM> {
override fun transform(prevState: YieldSupplyActionUM): YieldSupplyActionUM {
val cryptoCurrency = cryptoCurrencyStatus.currency
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val tokenFiatRate = cryptoCurrencyStatus.value.fiatRate
val feeFiatRate = feeCryptoCurrencyStatus.value.fiatRate
val crypto = feeValue.format { crypto(feeCryptoCurrencyStatus.currency) }
val fiatFeeValue = feeFiatRate?.let(feeValue::multiply)
val feeFiat = feeFiatRate?.let(feeValue::multiply)
val feeFiatValueText = feeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
val fiat = fiatFeeValue.format { fiat(appCurrency.code, appCurrency.symbol) }
val tokenCryptoFeeValue = fiatRate?.let { rate ->
fiatFeeValue?.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP)
val tokenCryptoFee = tokenFiatRate?.let { rate ->
feeFiat?.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP)
}
val tokenCryptoFeeValueText = tokenCryptoFee.format { crypto(cryptoCurrency) }
val tokenFiatFee = feeFiat // same fiat amount
val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
val tokenCryptoFee = tokenCryptoFeeValue.format { crypto(cryptoCurrency) }
val maxCryptoFee = maxNetworkFee.format { crypto(cryptoCurrency) }
val maxFiatFeeValue = fiatRate?.let { rate ->
maxNetworkFee.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP)
val maxFeeCryptoValueText = maxNetworkFee.format { crypto(cryptoCurrency) }
val maxFiatFee = tokenFiatRate?.let { rate ->
maxNetworkFee.multiply(rate)
}
val maxFiatFee = maxFiatFeeValue.format { fiat(appCurrency.code, appCurrency.symbol) }
val maxFiatFeeValueText = maxFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
val minAmountTextReference = YieldSupplyAmountFormatter(
cryptoCurrency,
appCurrency,
).invoke(minAmount, cryptoCurrencyStatus.value.fiatRate)
val minAmountCryptoText = minAmount.format { crypto(cryptoCurrency) }
val minAmountFiat = tokenFiatRate?.let(minAmount::multiply)
val minAmountFiatText = minAmountFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
val feeNoteValue = resourceReference(
id = R.string.yield_module_fee_policy_sheet_fee_note,
formatArgs = wrappedList(
tokenFiatFeeValueText,
tokenCryptoFeeValueText,
maxFiatFeeValueText,
maxFeeCryptoValueText,
),
)
val minFeeNoteValue = resourceReference(
id = R.string.yield_module_fee_policy_sheet_min_amount_note,
formatArgs = wrappedList(
minAmountFiatText,
minAmountCryptoText,
),
)
return if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading) {
prevState.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading)
@ -60,22 +76,12 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
prevState.copy(
yieldSupplyFeeUM = YieldSupplyFeeUM.Content(
transactionDataList = updatedTransactionList.toPersistentList(),
feeValue = combinedReference(
stringReference(crypto),
stringReference(" $DOT "),
stringReference(fiat),
),
currentNetworkFeeValue = combinedReference(
stringReference(tokenCryptoFee),
stringReference(" $DOT "),
stringReference(fiat),
),
maxNetworkFeeValue = combinedReference(
stringReference(maxCryptoFee),
stringReference(" $DOT "),
stringReference(maxFiatFee),
),
minAmountFeeValue = minAmountTextReference,
feeFiatValue = stringReference(feeFiatValueText),
tokenFeeFiatValue = stringReference(tokenFiatFeeValueText),
maxNetworkFeeFiatValue = stringReference(maxFiatFeeValueText),
minTopUpFiatValue = stringReference(minAmountFiatText),
feeNoteValue = feeNoteValue,
minFeeNoteValue = minFeeNoteValue,
),
)
}

View file

@ -16,12 +16,13 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldSupplyEnterStatus
import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyStopEarningUseCase
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
import com.tangem.features.yield.supply.impl.common.YieldSupplyAlertFactory
import com.tangem.features.yield.supply.impl.common.YieldSupplyProtocolTrigger
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
import com.tangem.features.yield.supply.impl.common.entity.transformer.YieldSupplyTransactionInProgressTransformer
@ -55,7 +56,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
private val yieldSupplyNotificationsUpdateTrigger: YieldSupplyNotificationsUpdateTrigger,
private val yieldSupplyAlertFactory: YieldSupplyAlertFactory,
private val yieldSupplyDeactivateUseCase: YieldSupplyDeactivateUseCase,
private val yieldSupplyProtocolTrigger: YieldSupplyProtocolTrigger,
private val yieldSupplyRepository: YieldSupplyRepository,
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
private val params: YieldSupplyStopEarningComponent.Params = paramsContainer.require()
@ -158,7 +159,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
)
},
ifRight = {
yieldSupplyProtocolTrigger.onExitProtocol()
yieldSupplyRepository.saveTokenProtocolStatus(cryptoCurrency, YieldSupplyEnterStatus.Exit)
analytics.send(
YieldSupplyAnalytics.FundsWithdrawn(
token = cryptoCurrency.symbol,

View file

@ -2,16 +2,13 @@ package com.tangem.features.yield.supply.impl.subcomponents.stopearning.model.tr
import com.tangem.blockchain.common.TransactionData
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
import com.tangem.utils.StringsSigns.DOT
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toPersistentList
import java.math.BigDecimal
@ -24,11 +21,10 @@ internal class YieldSupplyStopEarningFeeContentTransformer(
private val feeValue: BigDecimal,
) : Transformer<YieldSupplyActionUM> {
override fun transform(prevState: YieldSupplyActionUM): YieldSupplyActionUM {
val feeFiatValue = feeCryptoCurrencyStatus.value.fiatRate?.let { rate ->
val feeFiatValueRaw = feeCryptoCurrencyStatus.value.fiatRate?.let { rate ->
feeValue.multiply(rate)
}
val cryptoFee = feeValue.format { crypto(feeCryptoCurrencyStatus.currency) }
val fiatFee = feeFiatValue.format { fiat(appCurrency.code, appCurrency.symbol) }
val fiatFeeText = feeFiatValueRaw.format { fiat(appCurrency.code, appCurrency.symbol) }
return if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading) {
prevState.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading)
@ -37,14 +33,11 @@ internal class YieldSupplyStopEarningFeeContentTransformer(
isPrimaryButtonEnabled = true,
yieldSupplyFeeUM = YieldSupplyFeeUM.Content(
transactionDataList = transactions.toPersistentList(),
feeValue = combinedReference(
stringReference(cryptoFee),
stringReference(" $DOT "),
stringReference(fiatFee),
),
currentNetworkFeeValue = TextReference.EMPTY,
maxNetworkFeeValue = TextReference.EMPTY,
minAmountFeeValue = TextReference.EMPTY,
feeFiatValue = stringReference(fiatFeeText),
tokenFeeFiatValue = TextReference.EMPTY,
maxNetworkFeeFiatValue = TextReference.EMPTY,
minTopUpFiatValue = TextReference.EMPTY,
feeNoteValue = TextReference.EMPTY,
),
)
}

View file

@ -33,8 +33,8 @@ androidxWorkManager = "2.9.0"
# endregion AndroidX
# region Compose
compose-runtime = "1.7.4"
compose-foundation = "1.7.4"
compose-runtime = "1.7.8"
compose-foundation = "1.7.8"
compose-material3 = "1.3.1"
compose-constraint = "1.0.1"
compose-navigation = "2.7.7"