Updated on 2026-08-14
This commit is contained in:
parent
a3b65cd194
commit
d22bfd0c87
27 changed files with 342 additions and 139 deletions
|
|
@ -330,6 +330,7 @@ private val previewData = GiveTxPermissionBottomSheetConfig(
|
|||
approveType = ApproveType.LIMITED,
|
||||
approveButton = ApprovePermissionButton(true) {},
|
||||
cancelButton = CancelPermissionButton(true),
|
||||
isResetApproval = false,
|
||||
onChangeApproveType = { ApproveType.LIMITED },
|
||||
subtitle = resourceReference(R.string.give_permission_staking_subtitle, wrappedList("1")),
|
||||
dialogText = resourceReference(R.string.give_permission_staking_footer),
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ sealed class GiveTxPermissionState {
|
|||
val approveItems: ImmutableList<ApproveType> = ApproveType.entries.toImmutableList(),
|
||||
val approveButton: ApprovePermissionButton,
|
||||
val cancelButton: CancelPermissionButton,
|
||||
val isResetApproval: Boolean,
|
||||
val onChangeApproveType: ((ApproveType) -> Unit)? = null,
|
||||
val onOpenLearnMoreAboutApproveClick: () -> Unit,
|
||||
) : GiveTxPermissionState()
|
||||
|
|
|
|||
|
|
@ -1620,7 +1620,7 @@
|
|||
<string name="tangempay_service_unavailable_title">Service vorübergehend nicht verfügbar</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Daten können derzeit nicht angezeigt werden, Kartenzahlungen funktionieren jedoch weiterhin.</string>
|
||||
<string name="tangempay_set_pin_code">Satz \nPIN-Code</string>
|
||||
<string name="tangempay_sync_needed">Nicht synchronisiert</string>
|
||||
<string name="tangempay_sync_needed">Sitzung abgelaufen</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Zugang wiederherstellen</string>
|
||||
<string name="tangempay_tangem_visa_card">Nutzen Sie USDC für alltägliche Zahlungen</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay ist vorübergehend nicht erreichbar.</string>
|
||||
|
|
|
|||
|
|
@ -1593,7 +1593,7 @@
|
|||
<string name="tangempay_service_unavailable_title">Servicio temporalmente no disponible</string>
|
||||
<string name="tangempay_service_unreachable_try_later">No es posible mostrar los datos en este momento, pero los pagos con tarjeta siguen funcionando.</string>
|
||||
<string name="tangempay_set_pin_code">Establecer \nCódigo PIN</string>
|
||||
<string name="tangempay_sync_needed">Sincronización necesaria</string>
|
||||
<string name="tangempay_sync_needed">Sesión expirada</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Restablecer acceso</string>
|
||||
<string name="tangempay_tangem_visa_card">Usa USDC para pagos cotidianos</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay temporalmente no disponible</string>
|
||||
|
|
|
|||
|
|
@ -1590,7 +1590,7 @@
|
|||
<string name="tangempay_service_unavailable_title">Service temporairement indisponible</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Les données ne peuvent pas être affichées pour le moment, mais les paiements par carte fonctionnent toujours.</string>
|
||||
<string name="tangempay_set_pin_code">Définir le \ncode PIN</string>
|
||||
<string name="tangempay_sync_needed">Synchronisation requise</string>
|
||||
<string name="tangempay_sync_needed">Session expirée</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Restaurer l\'accès</string>
|
||||
<string name="tangempay_tangem_visa_card">Utilisez USDC pour les paiements quotidiens</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay est temporairement indisponible</string>
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@
|
|||
<string name="tangempay_service_unavailable_description">Stiamo risolvendo un problema tecnico. Riprova più tardi.</string>
|
||||
<string name="tangempay_service_unavailable_title">Servizio temporaneamente non disponibile</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Al momento non è possibile visualizzare i dati, ma i pagamenti con carta continuano a funzionare.</string>
|
||||
<string name="tangempay_sync_needed">Sincronizzazione necessaria</string>
|
||||
<string name="tangempay_sync_needed">Sessione scaduta</string>
|
||||
<string name="tangempay_tangem_visa_card">Usa USDC per i pagamenti quotidiani</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay è temporaneamente non disponibile</string>
|
||||
<string name="tangempay_title">Tangem Pay</string>
|
||||
|
|
|
|||
|
|
@ -467,6 +467,19 @@
|
|||
<string name="domain_receive_assets_onboarding_description">他のネットワークで資産を送金すると、永久に失われます。</string>
|
||||
<string name="domain_receive_assets_onboarding_network_name">%sネットワーク</string>
|
||||
<string name="domain_receive_assets_onboarding_title">下記のみを使用して資金を送金する</string>
|
||||
<string name="dynamic_addresses">動的アドレス</string>
|
||||
<string name="dynamic_addresses_custom_token_error_on_addition">動的アドレスが有効になっています。カスタムの「change」と「index」は利用できません。</string>
|
||||
<string name="dynamic_addresses_enabled_toast_title">動的アドレスが有効です。</string>
|
||||
<string name="dynamic_addresses_enter_features_privacy_description">取引ごとに新しいアドレスを使うことで、追跡されにくくなり、オンチェーン上のプライバシーが向上します。</string>
|
||||
<string name="dynamic_addresses_enter_features_privacy_title">プライバシー強化</string>
|
||||
<string name="dynamic_addresses_enter_features_receving_description">UTXOベースのネットワークでは、アドレスが自動生成されるため、手動で管理しなくても簡単に資金を受け取れます。</string>
|
||||
<string name="dynamic_addresses_enter_features_receving_title">スムーズな受け取り</string>
|
||||
<string name="dynamic_addresses_enter_main_button_title">動的アドレスを有効にする</string>
|
||||
<string name="dynamic_addresses_enter_subtitle">動的アドレスでは、プライバシー強化のため毎回新しいアドレスが生成されますが、合計残高は変わりません。</string>
|
||||
<string name="dynamic_addresses_error_has_custom_token_description">一部のカスタムアドレスまたはトークンで通常と異なる導出パスが使われているため、動的アドレスを有効にできません。</string>
|
||||
<string name="dynamic_addresses_error_has_custom_token_title">動的アドレスは利用できません</string>
|
||||
<string name="dynamic_addresses_error_service_unavailable_description">現在、プロバイダーに接続できません。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="dynamic_addresses_error_service_unavailable_title">サービスを利用できません。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="earn_best_opportunities">おすすめ</string>
|
||||
<string name="earn_clear_filter">絞り込みを解除</string>
|
||||
<string name="earn_empty">リストは現在更新中のため、一時的に空になっています。しばらくしてからご確認ください。</string>
|
||||
|
|
@ -745,7 +758,10 @@
|
|||
<string name="markets_loading_no_data_title">データなし</string>
|
||||
<string name="markets_pulse_common_title">マーケット動向</string>
|
||||
<string name="markets_quick_actions">クイックアクション</string>
|
||||
<string name="markets_search_clear_all_hints">すべてクリア</string>
|
||||
<string name="markets_search_header_title">トークンを探す</string>
|
||||
<string name="markets_search_hint_header">最近</string>
|
||||
<string name="markets_search_portfolio_header">ポートフォリオ内</string>
|
||||
<string name="markets_search_result_title">結果</string>
|
||||
<string name="markets_search_see_tokens_under_100k">時価総額10万ドル以下のトークンを見る</string>
|
||||
<string name="markets_search_show_tokens">トークンを表示</string>
|
||||
|
|
@ -820,9 +836,11 @@
|
|||
<string name="markets_token_details_market_rating_description">時価総額に基づくすべてのコイン間の暗号資産評価における位置</string>
|
||||
<string name="markets_token_details_market_rating_full">市場格付け</string>
|
||||
<string name="markets_token_details_max_supply">最大供給量</string>
|
||||
<string name="markets_token_details_max_supply_and_circulation_full">流通量と最大供給量</string>
|
||||
<string name="markets_token_details_max_supply_description">特定の暗号資産に存在しうるコインまたはトークンの最大数</string>
|
||||
<string name="markets_token_details_max_supply_full">最大供給量</string>
|
||||
<string name="markets_token_details_metrics">指標</string>
|
||||
<string name="markets_token_details_metrics_no_limited">制限なし</string>
|
||||
<string name="markets_token_details_official_links">公式リンク</string>
|
||||
<string name="markets_token_details_price_performance">値動き</string>
|
||||
<string name="markets_token_details_repository">リポジトリ</string>
|
||||
|
|
@ -1095,6 +1113,8 @@
|
|||
<string name="qr_scanner_error_unrecognized_title">認識できないQRコード</string>
|
||||
<string name="qr_scanner_error_unsupported_network_message">このネットワークは、追加されているトークンのいずれにも対応していません。対応しているトークンを追加してから暗号資産を送金してください。</string>
|
||||
<string name="qr_scanner_error_unsupported_network_title">対応しているトークンが見つかりません</string>
|
||||
<string name="qr_scanner_warning_unknown_parameters_message">このQRコードには認識できないパラメータが含まれています:%s。続行すると、一部の支払い情報が失われる可能性があります。</string>
|
||||
<string name="qr_scanner_warning_unknown_parameters_title">不明なパラメータ</string>
|
||||
<string name="receive_bottom_sheet_no_memo_required_message">メモ不要</string>
|
||||
<string name="receive_bottom_sheet_warning_message">%3$sネットワーク上の%1$s ( %2$s )</string>
|
||||
<string name="receive_bottom_sheet_warning_message_compact">%2$sネットワーク上の%1$s</string>
|
||||
|
|
@ -1477,6 +1497,7 @@
|
|||
<string name="swapping_fee_estimation_error_text">手数料見積りエラーです。サポートにフィードバックをお送りください。</string>
|
||||
<string name="swapping_from_title">スワップする</string>
|
||||
<string name="swapping_high_price_impact_description">選択したトークンをこの量を交換すると、価格に大きな影響が生じ、結果が減少します。</string>
|
||||
<string name="swapping_high_price_impact_text">流動性が低いため、受取額が大幅に少なくなる可能性があります。金額を減らすか、別のプロバイダーをお試しください。</string>
|
||||
<string name="swapping_high_price_impact_title">価格への影響が甚大です</string>
|
||||
<string name="swapping_insufficient_funds">残高不足</string>
|
||||
<string name="swapping_insufficient_funds_description">この取引を完了するには残高が不足しています。受け取り額を減らすか、資金を追加してください。</string>
|
||||
|
|
@ -1486,6 +1507,8 @@
|
|||
<string name="swapping_to_title">受け取る</string>
|
||||
<string name="swapping_token_list_title">トークンを選択</string>
|
||||
<string name="swapping_token_not_available">利用不可</string>
|
||||
<string name="swapping_trade_too_large_text">この取引に十分な流動性がありません。\n金額を減らすか、別のプロバイダーを選択してください。</string>
|
||||
<string name="swapping_trade_too_large_title">取引額が大きすぎます</string>
|
||||
<string name="tangem_pay_beta_notification_subtitle">皆様からのフィードバックをお待ちしております</string>
|
||||
<string name="tangem_pay_beta_notification_title">Tangem Payのベータ版を公開しました</string>
|
||||
<string name="tangem_pay_card_frozen">カードが凍結されています</string>
|
||||
|
|
@ -1609,7 +1632,7 @@
|
|||
<string name="tangempay_service_unavailable_title">サービスは一時的に利用できません</string>
|
||||
<string name="tangempay_service_unreachable_try_later">現在、データを表示できませんが、カードでのお支払いは引き続きご利用いただけます。</string>
|
||||
<string name="tangempay_set_pin_code">\nPINコードの設定</string>
|
||||
<string name="tangempay_sync_needed">未同期</string>
|
||||
<string name="tangempay_sync_needed">セッションの有効期限が切れました</string>
|
||||
<string name="tangempay_sync_needed_restore_access">アクセスを復元</string>
|
||||
<string name="tangempay_tangem_visa_card">日常の支払いにUSDCを利用</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Payは現在一時的に利用できません。</string>
|
||||
|
|
@ -1646,6 +1669,7 @@
|
|||
<string name="token_details_token_type_subtitle_no_standard">%%image%% %1$sネットワーク上のトークン</string>
|
||||
<string name="token_details_unable_hide_alert_message">%1$s ( %2$s ) トークンは%3$sネットワークの主要通貨であり、このネットワーク上の他のトークンがリストにある限り、非表示にすることはできません。</string>
|
||||
<string name="token_details_unable_hide_alert_title">%sを非表示にできません</string>
|
||||
<string name="token_market_metrics_no_data">該当なし</string>
|
||||
<string name="token_receive_show_qr_code_title">QRコードを表示</string>
|
||||
<string name="token_swap_changelly_promotion_message">このトークンは、2月%2$s-%3$s の間、%1$s のサービス手数料で別のトークンと交換できます。</string>
|
||||
<string name="token_swap_changelly_promotion_title">Changellyでスワップ、手数料%s</string>
|
||||
|
|
|
|||
|
|
@ -1631,7 +1631,7 @@
|
|||
<string name="tangempay_service_unavailable_title">Serviço temporariamente indisponível</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Não foi possível exibir os detalhes. No entanto, os pagamentos com cartão ainda estão funcionando.</string>
|
||||
<string name="tangempay_set_pin_code">Defina o código PIN.</string>
|
||||
<string name="tangempay_sync_needed">Não sincronizado</string>
|
||||
<string name="tangempay_sync_needed">Sessão expirada</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Restaurar acesso</string>
|
||||
<string name="tangempay_tangem_visa_card">Use USDC para pagamentos do dia a dia.</string>
|
||||
<string name="tangempay_temporarily_unavailable">O serviço Tangem Pay está temporariamente inacessível.</string>
|
||||
|
|
|
|||
|
|
@ -1624,7 +1624,7 @@
|
|||
<string name="tangempay_service_unavailable_description">Мы устраняем техническую проблему. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="tangempay_service_unavailable_title">Сервис временно недоступен</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Не можем показать данные карты, но оплаты продолжают работать.</string>
|
||||
<string name="tangempay_sync_needed">Не синхронизирован</string>
|
||||
<string name="tangempay_sync_needed">Сессия истекла</string>
|
||||
<string name="tangempay_tangem_visa_card">Оплачивайте ежедневные покупки в USDC</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay временно недоступен</string>
|
||||
<string name="tangempay_title">Tangem Pay</string>
|
||||
|
|
|
|||
|
|
@ -1633,7 +1633,7 @@
|
|||
<string name="tangempay_service_unavailable_title">Сервіс тимчасово недоступний</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Не можемо показати дані картки, але оплати продовжують працювати.</string>
|
||||
<string name="tangempay_set_pin_code">Встановіть \nPIN-код</string>
|
||||
<string name="tangempay_sync_needed">Не синхронізовано</string>
|
||||
<string name="tangempay_sync_needed">Сесія закінчилася</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Відновити доступ</string>
|
||||
<string name="tangempay_tangem_visa_card">Використовуйте USDC для щоденних платежів</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay тимчасово недоступний</string>
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@
|
|||
<string name="tangempay_service_unavailable_description">我们正在修复技术问题。请稍后再试。</string>
|
||||
<string name="tangempay_service_unavailable_title">服務暫時無法使用</string>
|
||||
<string name="tangempay_service_unreachable_try_later">目前無法顯示資料,但卡片支付仍可正常使用。</string>
|
||||
<string name="tangempay_sync_needed">需要同步</string>
|
||||
<string name="tangempay_sync_needed">工作階段已過期</string>
|
||||
<string name="tangempay_tangem_visa_card">使用 USDC 進行日常支付</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay暂时不可用</string>
|
||||
<string name="tangempay_title">Tangem Pay</string>
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@
|
|||
<string name="custom_token_creation_error_token_already_exist_title">Token already exists</string>
|
||||
<string name="custom_token_creation_error_wrong_decimals">Decimal must be a valid integer, up to %d</string>
|
||||
<string name="custom_token_custom_derivation">Custom derivation</string>
|
||||
<string name="custom_token_custom_derivation_dynamic_addresses_enabled_error">Dynamic Addresses is enabled. Custom \\"change\\" and \\"index\\" are unavailable.</string>
|
||||
<string name="custom_token_custom_derivation_placeholder">E. g. m/00\'/0000\'/0\'/0/0</string>
|
||||
<string name="custom_token_custom_derivation_title">Enter custom derivation</string>
|
||||
<string name="custom_token_decimals_input_title">Decimals</string>
|
||||
|
|
@ -475,6 +476,7 @@
|
|||
<string name="domain_receive_assets_onboarding_network_name">%s network</string>
|
||||
<string name="domain_receive_assets_onboarding_title">Send funds using only</string>
|
||||
<string name="dynamic_addresses">Dynamic addresses</string>
|
||||
<string name="dynamic_addresses_custom_token_error_on_addition">Dynamic Addresses is enabled. Custom \"change\" and \"index\" are unavailable.</string>
|
||||
<string name="dynamic_addresses_enabled_toast_title">Dynamic addresses enabled</string>
|
||||
<string name="dynamic_addresses_enter_features_privacy_description">Use a new address for each transaction to reduce traceability and improve on-chain privacy.</string>
|
||||
<string name="dynamic_addresses_enter_features_privacy_title">Enhanced Privacy</string>
|
||||
|
|
@ -1652,7 +1654,7 @@
|
|||
<string name="tangempay_service_unavailable_title">Service temporarily unavailable</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Unable to display details. However, card payments are still working.</string>
|
||||
<string name="tangempay_set_pin_code">Set \nPIN code</string>
|
||||
<string name="tangempay_sync_needed">Not synced</string>
|
||||
<string name="tangempay_sync_needed">Session expired</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Restore access</string>
|
||||
<string name="tangempay_tangem_visa_card">Use USDC for everyday payments</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay is temporarily unreachable</string>
|
||||
|
|
@ -1732,6 +1734,9 @@
|
|||
<string name="universal_error">We\'ve encountered an error. Error code: %s. Please contact our support.</string>
|
||||
<string name="unlock_wallet_description_full">Use %s or scan a card/ring to have access to your wallet</string>
|
||||
<string name="unsupported_wc_version">Connection failed: This dApp uses Wallet Connect version 1.0, which is not supported. Please ensure the dApp supports Wallet Connect version 2.0 to connect successfully.</string>
|
||||
<string name="update_approval_permission_fee_note">Previous permission will be revoked and a new one will be issued. Network will charge token approval fee for each of theses actions. You will see a zero-amount transaction in the history as a revoke evidence.</string>
|
||||
<string name="update_approval_permission_subtitle">Transaction exceeds previously granted permission amount.\nUpdate permission to proceed</string>
|
||||
<string name="update_approval_permission_title">Update permission</string>
|
||||
<string name="upgrade_to_hardware_wallet_button_title">Upgrade to hardware wallet</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Stay up to date with the latest features and news</string>
|
||||
<string name="user_push_notification_agreement_argument_one_subtitle">Real-time alerts for transactions, exchanges, and critical updates.</string>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ interface GiveApprovalComponent : ComposableBottomSheetComponent {
|
|||
val spenderAddress: String,
|
||||
val subtitle: TextReference,
|
||||
val isHoldToConfirm: Boolean = false,
|
||||
val isResetApproval: Boolean = false,
|
||||
val callback: Callback,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
|
|
@ -23,7 +24,6 @@ import com.tangem.features.send.v2.api.params.FeeSelectorParams
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import com.tangem.common.ui.R as CommonUiR
|
||||
|
||||
internal class DefaultGiveApprovalComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
|
|
@ -38,6 +38,7 @@ internal class DefaultGiveApprovalComponent @AssistedInject constructor(
|
|||
params = FeeSelectorParams.FeeSelectorBlockParams(
|
||||
state = FeeSelectorUM.Loading,
|
||||
onLoadFee = { model.loadFee() },
|
||||
onDisableCustomFee = { model.shouldDisableCustomFee() },
|
||||
onLoadFeeExtended = { selectedFeeToken -> model.loadFeeExtended(selectedFeeToken) },
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
|
|
@ -71,22 +72,23 @@ internal class DefaultGiveApprovalComponent @AssistedInject constructor(
|
|||
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
titleText = resourceReference(CommonUiR.string.give_permission_title),
|
||||
titleText = resourceReference(
|
||||
if (uiState.isResetApproval) {
|
||||
R.string.update_approval_permission_title
|
||||
} else {
|
||||
R.string.give_permission_title
|
||||
},
|
||||
),
|
||||
titleAction = TopAppBarButtonUM.Icon(
|
||||
iconRes = CommonUiR.drawable.ic_information_24,
|
||||
iconRes = R.drawable.ic_information_24,
|
||||
onClicked = model::showPermissionInfoDialog,
|
||||
),
|
||||
) {
|
||||
GiveApprovalContent(
|
||||
currency = currency,
|
||||
subtitle = params.subtitle,
|
||||
approveType = uiState.approveType,
|
||||
approveItems = uiState.approveItems,
|
||||
uiState = uiState,
|
||||
onChangeApproveType = model::onChangeApproveType,
|
||||
walletInteractionIcon = uiState.walletInteractionIcon,
|
||||
isApproveEnabled = uiState.isApproveButtonEnabled,
|
||||
isApproveLoading = uiState.isApproveLoading,
|
||||
isHoldToConfirm = uiState.isHoldToConfirm,
|
||||
onApproveClick = model::onApproveClick,
|
||||
onCancelClick = model::onCancelClick,
|
||||
onOpenLearnMoreAboutApproveClick = model::onOpenLearnMoreAboutApproveClick,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import arrow.core.Either
|
|||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionSender.MultipleTransactionSendMode
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
|
|
@ -22,8 +23,10 @@ import com.tangem.core.ui.message.DialogMessage
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.transaction.models.AllowanceInfo
|
||||
import com.tangem.domain.transaction.models.TransactionFeeExtended
|
||||
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.GetAllowanceInfoUseCase
|
||||
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.gasless.CreateAndSendGaslessTransactionUseCase
|
||||
|
|
@ -32,6 +35,7 @@ import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase
|
|||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.approval.api.GiveApprovalComponent
|
||||
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
|
||||
import com.tangem.features.send.v2.api.entity.FeeItem
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -41,15 +45,17 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class GiveApprovalModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
paramsContainer: ParamsContainer,
|
||||
private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase,
|
||||
private val getAllowanceInfoUseCase: GetAllowanceInfoUseCase,
|
||||
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||
private val getFeeUseCase: GetFeeUseCase,
|
||||
private val getFeeForGaslessUseCase: GetFeeForGaslessUseCase,
|
||||
|
|
@ -77,11 +83,14 @@ internal class GiveApprovalModel @Inject constructor(
|
|||
isApproveButtonEnabled = false,
|
||||
isApproveLoading = false,
|
||||
isHoldToConfirm = params.isHoldToConfirm,
|
||||
isResetApproval = params.isResetApproval,
|
||||
),
|
||||
)
|
||||
|
||||
private var feeSelectorUM: FeeSelectorUM = FeeSelectorUM.Loading
|
||||
|
||||
private var approvalTxList: Map<TransactionData.Uncompiled, TransactionFee> = emptyMap()
|
||||
|
||||
override fun onFeeResult(feeSelectorUM: FeeSelectorUM) {
|
||||
this.feeSelectorUM = feeSelectorUM
|
||||
uiState.update { it.copy(isApproveButtonEnabled = feeSelectorUM.isPrimaryButtonEnabled) }
|
||||
|
|
@ -122,83 +131,127 @@ internal class GiveApprovalModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun prepareApprovalTransaction(): Either<Throwable, TransactionData> {
|
||||
val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
val tokenCurrency = cryptoCurrencyStatus.currency as? CryptoCurrency.Token
|
||||
?: return Either.Left(IllegalStateException("Currency is not a token"))
|
||||
|
||||
return createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
userWalletId = params.userWalletId,
|
||||
amount = getApprovalAmount(),
|
||||
contractAddress = tokenCurrency.contractAddress,
|
||||
spenderAddress = params.spenderAddress,
|
||||
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
|
||||
return onApprovalTx(
|
||||
onApprove = { approve ->
|
||||
getFeeUseCase(
|
||||
transactionData = approve,
|
||||
userWallet = userWallet,
|
||||
network = params.cryptoCurrencyStatus.currency.network,
|
||||
).onRight { fee ->
|
||||
approvalTxList = mapOf(approve to fee)
|
||||
}
|
||||
},
|
||||
onResetApprove = { (revokeApproval, approve) ->
|
||||
getFeeUseCase(
|
||||
transactionData = revokeApproval,
|
||||
userWallet = userWallet,
|
||||
network = params.cryptoCurrencyStatus.currency.network,
|
||||
).map { revokeFee ->
|
||||
estimateFeeForResetApproval(
|
||||
revokeTransactionFee = revokeFee,
|
||||
revokeApprovalTransaction = revokeApproval,
|
||||
approvalTransaction = approve,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
|
||||
val approvalTransaction = prepareApprovalTransaction()
|
||||
.getOrElse { return GetFeeError.DataError(it).left() }
|
||||
|
||||
return getFeeUseCase(
|
||||
transactionData = approvalTransaction,
|
||||
userWallet = userWallet,
|
||||
network = params.cryptoCurrencyStatus.currency.network,
|
||||
)
|
||||
fun shouldDisableCustomFee(): Boolean {
|
||||
return approvalTxList.size > 1
|
||||
}
|
||||
|
||||
suspend fun loadFeeExtended(maybeToken: CryptoCurrencyStatus?): Either<GetFeeError, TransactionFeeExtended> {
|
||||
val approvalTransaction = prepareApprovalTransaction()
|
||||
.getOrElse { return GetFeeError.DataError(it).left() }
|
||||
val approve = createApprovalTransactionUseCase(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
amount = getApprovalAmount(),
|
||||
contractAddress = (params.cryptoCurrencyStatus.currency as CryptoCurrency.Token).contractAddress,
|
||||
spenderAddress = params.spenderAddress,
|
||||
).getOrElse { error ->
|
||||
TangemLogger.e("Failed to create approveTransaction", error)
|
||||
return GetFeeError.DataError(error).left()
|
||||
}
|
||||
|
||||
return if (maybeToken == null) {
|
||||
getFeeForGaslessUseCase(
|
||||
transactionData = approvalTransaction,
|
||||
transactionData = approve,
|
||||
userWallet = userWallet,
|
||||
network = params.cryptoCurrencyStatus.currency.network,
|
||||
)
|
||||
} else {
|
||||
getFeeForTokenUseCase(
|
||||
transactionData = approvalTransaction,
|
||||
transactionData = approve,
|
||||
userWallet = userWallet,
|
||||
token = maybeToken.currency,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun estimateFeeForResetApproval(
|
||||
revokeTransactionFee: TransactionFee,
|
||||
revokeApprovalTransaction: TransactionData.Uncompiled,
|
||||
approvalTransaction: TransactionData.Uncompiled,
|
||||
) = when (revokeTransactionFee) {
|
||||
is TransactionFee.Choosable -> {
|
||||
val approveFee = revokeTransactionFee.copy(
|
||||
minimum = revokeTransactionFee.minimum.increaseEthereumGasLimitBy(2.toBigDecimal()),
|
||||
normal = revokeTransactionFee.normal.increaseEthereumGasLimitBy(2.toBigDecimal()),
|
||||
priority = revokeTransactionFee.priority.increaseEthereumGasLimitBy(2.toBigDecimal()),
|
||||
)
|
||||
approvalTxList = mapOf(
|
||||
revokeApprovalTransaction to revokeTransactionFee,
|
||||
approvalTransaction to approveFee,
|
||||
)
|
||||
revokeTransactionFee.copy(
|
||||
minimum = approveFee.minimum + revokeTransactionFee.minimum,
|
||||
normal = approveFee.normal + revokeTransactionFee.normal,
|
||||
priority = approveFee.priority + revokeTransactionFee.priority,
|
||||
)
|
||||
}
|
||||
is TransactionFee.Single -> {
|
||||
val approveFee = revokeTransactionFee.copy(normal = revokeTransactionFee.normal)
|
||||
approvalTxList = mapOf(
|
||||
revokeApprovalTransaction to revokeTransactionFee,
|
||||
approvalTransaction to approveFee,
|
||||
)
|
||||
revokeTransactionFee.copy(normal = approveFee.normal + revokeTransactionFee.normal)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendApprovalTransaction(): Boolean {
|
||||
val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
val tokenCurrency = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return false
|
||||
|
||||
val feeContent = feeSelectorUM as? FeeSelectorUM.Content ?: return false
|
||||
val selectedFee = feeContent.selectedFeeItem.fee
|
||||
val feeExtended = feeContent.feeExtraInfo.transactionFeeExtended
|
||||
|
||||
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
|
||||
|
||||
val transactionData = createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
userWalletId = params.userWalletId,
|
||||
amount = getApprovalAmount(),
|
||||
fee = selectedFee,
|
||||
contractAddress = tokenCurrency.contractAddress,
|
||||
spenderAddress = params.spenderAddress,
|
||||
).getOrElse { error ->
|
||||
TangemLogger.e("Failed to create approval transaction", error)
|
||||
return false
|
||||
val transactions = approvalTxList.map { (tx, fee) ->
|
||||
tx.copy(
|
||||
fee = when (feeContent.selectedFeeItem) {
|
||||
is FeeItem.Fast -> (fee as? TransactionFee.Choosable)?.priority ?: fee.normal
|
||||
is FeeItem.Market -> fee.normal
|
||||
is FeeItem.Slow -> (fee as? TransactionFee.Choosable)?.minimum ?: fee.normal
|
||||
else -> feeContent.selectedFeeItem.fee
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return if (isFeeInTokenCurrency) {
|
||||
createAndSendGaslessTransactionUseCase(
|
||||
userWallet = userWallet,
|
||||
transactionData = transactionData,
|
||||
transactionData = transactions.first(),
|
||||
fee = feeExtended,
|
||||
)
|
||||
} else {
|
||||
sendTransactionUseCase(
|
||||
txData = transactionData,
|
||||
txsData = transactions,
|
||||
userWallet = userWallet,
|
||||
network = tokenCurrency.network,
|
||||
sendMode = MultipleTransactionSendMode.DEFAULT,
|
||||
)
|
||||
}.fold(
|
||||
ifLeft = { error ->
|
||||
|
|
@ -241,4 +294,88 @@ internal class GiveApprovalModel @Inject constructor(
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> onApprovalTx(
|
||||
onApprove: suspend (TransactionData.Uncompiled) -> Either<GetFeeError, T>,
|
||||
onResetApprove:
|
||||
suspend (Pair<TransactionData.Uncompiled, TransactionData.Uncompiled>) -> Either<GetFeeError, T>,
|
||||
): Either<GetFeeError, T> {
|
||||
val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
val tokenCurrency = cryptoCurrencyStatus.currency as? CryptoCurrency.Token
|
||||
?: return GetFeeError.DataError(IllegalStateException("Currency is not a token")).left()
|
||||
|
||||
val amount = params.amount.toBigDecimalOrNull()
|
||||
?: return GetFeeError.DataError(IllegalArgumentException("Invalid amount format")).left()
|
||||
|
||||
val allowance = getAllowanceInfoUseCase(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
spenderAddress = params.spenderAddress,
|
||||
requiredAmount = amount,
|
||||
).getOrElse { error ->
|
||||
TangemLogger.e("Failed to get allowance info", error)
|
||||
return GetFeeError.DataError(error).left()
|
||||
}
|
||||
|
||||
val approve = createApprovalTransactionUseCase(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amount = getApprovalAmount(),
|
||||
contractAddress = tokenCurrency.contractAddress,
|
||||
spenderAddress = params.spenderAddress,
|
||||
).getOrElse { error ->
|
||||
TangemLogger.e("Failed to create approveTransaction", error)
|
||||
return GetFeeError.DataError(error).left()
|
||||
}
|
||||
|
||||
return if (allowance is AllowanceInfo.ResetNeeded) {
|
||||
val revokeApproval = createApprovalTransactionUseCase(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amount = BigDecimal.ZERO,
|
||||
contractAddress = tokenCurrency.contractAddress,
|
||||
spenderAddress = params.spenderAddress,
|
||||
).getOrElse { error ->
|
||||
TangemLogger.e("Failed to create revoke approveTransaction", error)
|
||||
return GetFeeError.DataError(error).left()
|
||||
}
|
||||
onResetApprove(revokeApproval to approve)
|
||||
} else {
|
||||
onApprove(approve)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Fee.increaseEthereumGasLimitBy(multiplier: BigDecimal): Fee {
|
||||
if (this !is Fee.Ethereum) return this
|
||||
val increasedGasPrice = amount.value?.movePointRight(amount.decimals)
|
||||
?.divide(gasLimit.toBigDecimal(), RoundingMode.HALF_UP)
|
||||
val increasedGasLimit = gasLimit
|
||||
.multiply(multiplier.toBigInteger())
|
||||
val increasedAmount = amount.copy(
|
||||
value = increasedGasPrice?.multiply(
|
||||
increasedGasLimit.toBigDecimal().movePointLeft(amount.decimals),
|
||||
),
|
||||
)
|
||||
return when (this) {
|
||||
is Fee.Ethereum.EIP1559 -> copy(amount = increasedAmount, gasLimit = increasedGasLimit)
|
||||
is Fee.Ethereum.Legacy -> copy(amount = increasedAmount, gasLimit = increasedGasLimit)
|
||||
is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]")
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun Fee.plus(otherFee: Fee): Fee {
|
||||
if (this !is Fee.Ethereum || otherFee !is Fee.Ethereum) return this
|
||||
val gasLimit = this.gasLimit
|
||||
val increasedGasPrice = this.amount.value?.movePointRight(this.amount.decimals)
|
||||
?.divide(gasLimit.toBigDecimal(), RoundingMode.HALF_UP)
|
||||
val increasedGasLimit = gasLimit + otherFee.gasLimit
|
||||
val increasedAmount = this.amount.copy(
|
||||
value = increasedGasLimit.toBigDecimal().multiply(increasedGasPrice).movePointLeft(this.amount.decimals),
|
||||
)
|
||||
return when (this) {
|
||||
is Fee.Ethereum.EIP1559 -> copy(amount = increasedAmount, gasLimit = increasedGasLimit)
|
||||
is Fee.Ethereum.Legacy -> copy(amount = increasedAmount, gasLimit = increasedGasLimit)
|
||||
is Fee.Ethereum.TokenCurrency -> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,5 @@ internal data class GiveApprovalUM(
|
|||
val isApproveButtonEnabled: Boolean,
|
||||
val isApproveLoading: Boolean,
|
||||
val isHoldToConfirm: Boolean = false,
|
||||
val isResetApproval: Boolean = false,
|
||||
)
|
||||
|
|
@ -27,28 +27,24 @@ import androidx.compose.ui.unit.DpOffset
|
|||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.approval.impl.model.GiveApprovalUM
|
||||
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import com.tangem.common.ui.R as CommonUiR
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
internal fun GiveApprovalContent(
|
||||
currency: String,
|
||||
uiState: GiveApprovalUM,
|
||||
subtitle: TextReference,
|
||||
approveType: ApproveType,
|
||||
approveItems: ImmutableList<ApproveType>,
|
||||
onChangeApproveType: (ApproveType) -> Unit,
|
||||
walletInteractionIcon: Int?,
|
||||
isApproveEnabled: Boolean,
|
||||
isApproveLoading: Boolean,
|
||||
isHoldToConfirm: Boolean,
|
||||
onApproveClick: () -> Unit,
|
||||
onCancelClick: () -> Unit,
|
||||
onOpenLearnMoreAboutApproveClick: () -> Unit,
|
||||
|
|
@ -73,20 +69,21 @@ internal fun GiveApprovalContent(
|
|||
|
||||
ApprovalInfo(
|
||||
currency = currency,
|
||||
approveType = approveType,
|
||||
approveItems = approveItems,
|
||||
approveType = uiState.approveType,
|
||||
approveItems = uiState.approveItems,
|
||||
onChangeApproveType = onChangeApproveType,
|
||||
onOpenLearnMoreAboutApproveClick = onOpenLearnMoreAboutApproveClick,
|
||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||
isResetApproval = uiState.isResetApproval,
|
||||
)
|
||||
|
||||
SpacerH(height = TangemTheme.dimens.spacing20)
|
||||
|
||||
if (isHoldToConfirm) {
|
||||
if (uiState.isHoldToConfirm) {
|
||||
HoldToConfirmButton(
|
||||
text = stringResourceSafe(id = CommonUiR.string.common_approve),
|
||||
enabled = isApproveEnabled,
|
||||
isLoading = isApproveLoading,
|
||||
text = stringResourceSafe(id = R.string.common_approve),
|
||||
enabled = uiState.isApproveButtonEnabled,
|
||||
isLoading = uiState.isApproveLoading,
|
||||
onConfirm = onApproveClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -94,21 +91,21 @@ internal fun GiveApprovalContent(
|
|||
)
|
||||
} else {
|
||||
PrimaryButtonIconEnd(
|
||||
text = stringResourceSafe(id = CommonUiR.string.common_approve),
|
||||
iconResId = walletInteractionIcon,
|
||||
showProgress = isApproveLoading,
|
||||
text = stringResourceSafe(id = R.string.common_approve),
|
||||
iconResId = uiState.walletInteractionIcon,
|
||||
showProgress = uiState.isApproveLoading,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
onClick = onApproveClick,
|
||||
enabled = isApproveEnabled,
|
||||
enabled = uiState.isApproveButtonEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH12()
|
||||
|
||||
SecondaryButton(
|
||||
text = stringResourceSafe(id = CommonUiR.string.common_cancel),
|
||||
text = stringResourceSafe(id = R.string.common_cancel),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
|
|
@ -124,6 +121,7 @@ internal fun GiveApprovalContent(
|
|||
private fun ApprovalInfo(
|
||||
currency: String,
|
||||
approveType: ApproveType,
|
||||
isResetApproval: Boolean,
|
||||
approveItems: ImmutableList<ApproveType>,
|
||||
onChangeApproveType: (ApproveType) -> Unit,
|
||||
onOpenLearnMoreAboutApproveClick: () -> Unit,
|
||||
|
|
@ -131,7 +129,7 @@ private fun ApprovalInfo(
|
|||
) {
|
||||
FooterContainer(
|
||||
footer = annotatedReference {
|
||||
append(stringResourceSafe(CommonUiR.string.swap_approve_description))
|
||||
append(stringResourceSafe(R.string.swap_approve_description))
|
||||
append(" ")
|
||||
withLink(
|
||||
link = LinkAnnotation.Clickable(
|
||||
|
|
@ -140,7 +138,7 @@ private fun ApprovalInfo(
|
|||
),
|
||||
block = {
|
||||
appendColored(
|
||||
text = stringResourceSafe(CommonUiR.string.common_learn_more),
|
||||
text = stringResourceSafe(R.string.common_learn_more),
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
|
|
@ -157,7 +155,13 @@ private fun ApprovalInfo(
|
|||
}
|
||||
SpacerH16()
|
||||
FooterContainer(
|
||||
footer = resourceReference(CommonUiR.string.give_permission_policy_type_footer),
|
||||
footer = resourceReference(
|
||||
if (isResetApproval) {
|
||||
R.string.update_approval_permission_fee_note
|
||||
} else {
|
||||
R.string.give_permission_policy_type_footer
|
||||
},
|
||||
),
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
feeSelectorBlockComponent.Content(
|
||||
|
|
@ -202,7 +206,7 @@ private fun AmountItem(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = CommonUiR.string.give_permission_rows_amount, currency),
|
||||
text = stringResourceSafe(id = R.string.give_permission_rows_amount, currency),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
maxLines = 1,
|
||||
|
|
@ -215,7 +219,7 @@ private fun AmountItem(
|
|||
maxLines = 1,
|
||||
)
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(id = CommonUiR.drawable.ic_chevron_24)),
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(id = R.drawable.ic_chevron_24)),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing2),
|
||||
|
|
@ -275,10 +279,10 @@ private fun DropdownSelector(
|
|||
Text(
|
||||
text = when (item) {
|
||||
ApproveType.LIMITED -> stringResourceSafe(
|
||||
id = CommonUiR.string.give_permission_current_transaction,
|
||||
id = R.string.give_permission_current_transaction,
|
||||
)
|
||||
ApproveType.UNLIMITED -> stringResourceSafe(
|
||||
id = CommonUiR.string.give_permission_unlimited,
|
||||
id = R.string.give_permission_unlimited,
|
||||
)
|
||||
},
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
|
|
@ -288,7 +292,7 @@ private fun DropdownSelector(
|
|||
SpacerWMax()
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
image = ImageVector.vectorResource(id = CommonUiR.drawable.ic_check_24),
|
||||
image = ImageVector.vectorResource(id = R.drawable.ic_check_24),
|
||||
),
|
||||
tint = color,
|
||||
contentDescription = null,
|
||||
|
|
@ -316,13 +320,8 @@ private fun GiveApprovalContentPreview(
|
|||
GiveApprovalContent(
|
||||
currency = params.currency,
|
||||
subtitle = params.subtitle,
|
||||
approveType = params.approveType,
|
||||
approveItems = params.approveItems,
|
||||
uiState = params.uiState,
|
||||
onChangeApproveType = {},
|
||||
walletInteractionIcon = params.walletInteractionIcon,
|
||||
isApproveEnabled = params.isApproveEnabled,
|
||||
isApproveLoading = params.isApproveLoading,
|
||||
isHoldToConfirm = false,
|
||||
onApproveClick = {},
|
||||
onCancelClick = {},
|
||||
onOpenLearnMoreAboutApproveClick = {},
|
||||
|
|
@ -334,11 +333,7 @@ private fun GiveApprovalContentPreview(
|
|||
private data class GiveApprovalPreviewParams(
|
||||
val currency: String,
|
||||
val subtitle: TextReference,
|
||||
val approveType: ApproveType,
|
||||
val approveItems: ImmutableList<ApproveType>,
|
||||
val walletInteractionIcon: Int?,
|
||||
val isApproveEnabled: Boolean,
|
||||
val isApproveLoading: Boolean,
|
||||
val uiState: GiveApprovalUM,
|
||||
)
|
||||
|
||||
private class GiveApprovalContentPreviewProvider : PreviewParameterProvider<GiveApprovalPreviewParams> {
|
||||
|
|
@ -347,20 +342,36 @@ private class GiveApprovalContentPreviewProvider : PreviewParameterProvider<Give
|
|||
GiveApprovalPreviewParams(
|
||||
currency = "USDT",
|
||||
subtitle = stringReference("Allow this app to access your USDT"),
|
||||
approveType = ApproveType.LIMITED,
|
||||
approveItems = persistentListOf(ApproveType.LIMITED, ApproveType.UNLIMITED),
|
||||
walletInteractionIcon = CommonUiR.drawable.ic_tangem_24,
|
||||
isApproveEnabled = true,
|
||||
isApproveLoading = false,
|
||||
uiState = GiveApprovalUM(
|
||||
approveType = ApproveType.LIMITED,
|
||||
approveItems = persistentListOf(ApproveType.LIMITED, ApproveType.UNLIMITED),
|
||||
walletInteractionIcon = R.drawable.ic_tangem_24,
|
||||
isApproveButtonEnabled = true,
|
||||
isApproveLoading = false,
|
||||
),
|
||||
),
|
||||
GiveApprovalPreviewParams(
|
||||
currency = "USDC",
|
||||
subtitle = stringReference("Allow this app to access your USDC"),
|
||||
approveType = ApproveType.UNLIMITED,
|
||||
approveItems = persistentListOf(ApproveType.LIMITED, ApproveType.UNLIMITED),
|
||||
walletInteractionIcon = CommonUiR.drawable.ic_tangem_24,
|
||||
isApproveEnabled = false,
|
||||
isApproveLoading = true,
|
||||
uiState = GiveApprovalUM(
|
||||
approveType = ApproveType.UNLIMITED,
|
||||
approveItems = persistentListOf(ApproveType.LIMITED, ApproveType.UNLIMITED),
|
||||
walletInteractionIcon = R.drawable.ic_tangem_24,
|
||||
isApproveButtonEnabled = false,
|
||||
isApproveLoading = true,
|
||||
),
|
||||
),
|
||||
GiveApprovalPreviewParams(
|
||||
currency = "USDC",
|
||||
subtitle = stringReference("Allow this app to access your USDC"),
|
||||
uiState = GiveApprovalUM(
|
||||
approveType = ApproveType.UNLIMITED,
|
||||
approveItems = persistentListOf(ApproveType.LIMITED, ApproveType.UNLIMITED),
|
||||
walletInteractionIcon = R.drawable.ic_tangem_24,
|
||||
isApproveButtonEnabled = true,
|
||||
isApproveLoading = false,
|
||||
isResetApproval = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ sealed class FeeSelectorParams {
|
|||
abstract val userWalletId: UserWalletId
|
||||
abstract val onLoadFeeExtended: (suspend (CryptoCurrencyStatus?) -> Either<GetFeeError, TransactionFeeExtended>)?
|
||||
abstract val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>
|
||||
abstract val onDisableCustomFee: () -> Boolean
|
||||
abstract val cryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
abstract val feeCryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
abstract val feeStateConfiguration: FeeStateConfiguration
|
||||
|
|
@ -39,6 +40,7 @@ sealed class FeeSelectorParams {
|
|||
override val analyticsCategoryName: String,
|
||||
override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource,
|
||||
override val shouldShowOnlySpeedOption: Boolean = false,
|
||||
override val onDisableCustomFee: () -> Boolean = { false },
|
||||
val bottomSheetShown: (Boolean) -> Unit = {},
|
||||
) : FeeSelectorParams()
|
||||
|
||||
|
|
@ -56,6 +58,7 @@ sealed class FeeSelectorParams {
|
|||
override val analyticsCategoryName: String,
|
||||
override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource,
|
||||
override val shouldShowOnlySpeedOption: Boolean = false,
|
||||
override val onDisableCustomFee: () -> Boolean = { false },
|
||||
val callback: FeeSelectorModelCallback,
|
||||
) : FeeSelectorParams()
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
|
|||
params = FeeSelectorParams.FeeSelectorDetailsParams(
|
||||
state = model.uiState.value,
|
||||
onLoadFee = params.onLoadFee,
|
||||
onDisableCustomFee = params.onDisableCustomFee,
|
||||
onLoadFeeExtended = params.onLoadFeeExtended,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
|||
feeStateConfiguration = params.feeStateConfiguration,
|
||||
isFeeApproximate = isFeeApproximate(fee.transactionFee.normal.amount.type),
|
||||
feeSelectorIntents = this@FeeSelectorLogic,
|
||||
shouldDisableCustomFee = params.onDisableCustomFee(),
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ internal class FeeItemConverter(
|
|||
private val feeSelectorIntents: FeeSelectorIntents,
|
||||
private val appCurrency: AppCurrency,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val shouldDisableCustomFee: Boolean,
|
||||
) : Converter<FeeItemConverter.Input, ImmutableList<FeeItem>> {
|
||||
|
||||
private val customFeeFieldConverter = FeeSelectorCustomFieldConverter(
|
||||
|
|
@ -54,8 +55,10 @@ internal class FeeItemConverter(
|
|||
add(FeeItem.Market(fee = value.transactionFee.normal))
|
||||
}
|
||||
}
|
||||
val customFee = value.customFee ?: constructCustomFee()
|
||||
customFee?.let(::add)
|
||||
if (!shouldDisableCustomFee) {
|
||||
val customFee = value.customFee ?: constructCustomFee()
|
||||
customFee?.let(::add)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<FeeItem>.addFeeItemsLimited(value: Input) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ internal class FeeSelectorLoadedTransformer(
|
|||
private val feeStateConfiguration: FeeSelectorParams.FeeStateConfiguration,
|
||||
private val isFeeApproximate: Boolean,
|
||||
private val feeSelectorIntents: FeeSelectorIntents,
|
||||
private val shouldDisableCustomFee: Boolean,
|
||||
) : Transformer<FeeSelectorUM> {
|
||||
|
||||
private val feeItemsConverter = FeeItemConverter(
|
||||
|
|
@ -30,6 +31,7 @@ internal class FeeSelectorLoadedTransformer(
|
|||
feeSelectorIntents = feeSelectorIntents,
|
||||
appCurrency = appCurrency,
|
||||
cryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
shouldDisableCustomFee = shouldDisableCustomFee,
|
||||
)
|
||||
|
||||
override fun transform(prevState: FeeSelectorUM): FeeSelectorUM {
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ internal class ShowApprovalBottomSheetTransformer(
|
|||
footerText = resourceReference(R.string.staking_give_permission_fee_footer),
|
||||
onChangeApproveType = prevState.clickIntents::onApproveTypeChange,
|
||||
onOpenLearnMoreAboutApproveClick = {},
|
||||
isResetApproval = false,
|
||||
),
|
||||
walletInteractionIcon = walletInterationIcon(userWallet),
|
||||
onCancel = onDismiss,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.blockchain.common.Amount
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionExtras
|
||||
import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderFactory
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplySendCallData
|
||||
|
|
@ -2025,35 +2024,31 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
// setting up amount for approve with given amount for swap [SwapApproveType.Limited]
|
||||
val callData = SmartContractCallDataProviderFactory.getApprovalCallData(
|
||||
spenderAddress = requireNotNull(spenderAddress) { "spenderAddress cant be null" },
|
||||
amount = swapAmount.value.convertToSdkAmount(fromTokenStatus),
|
||||
blockchain = fromToken.network.toBlockchain(),
|
||||
)
|
||||
val feeData = try {
|
||||
val extras = createTransactionExtrasUseCase(
|
||||
callData = callData,
|
||||
network = fromToken.network,
|
||||
).getOrNull() ?: error("unable to create extras")
|
||||
val fromAddress = requireNotNull(
|
||||
fromTokenStatus.value.networkAddress?.defaultAddress?.value,
|
||||
) { "networkAddress cant be null" }
|
||||
|
||||
val fromAddress = requireNotNull(
|
||||
fromTokenStatus.value.networkAddress?.defaultAddress?.value,
|
||||
) { "networkAddress cant be null" }
|
||||
val transactionData = TransactionData.Uncompiled(
|
||||
amount = createNativeAmountForDex("0", fromToken.network),
|
||||
destinationAddress = fromToken.getContractAddress(),
|
||||
fee = null,
|
||||
sourceAddress = fromAddress,
|
||||
extras = extras,
|
||||
)
|
||||
getFeeUseCase(
|
||||
transactionData = transactionData,
|
||||
network = fromToken.network,
|
||||
userWallet = userWallet,
|
||||
).getOrNull() ?: error("unable to calculate fee")
|
||||
} catch (e: Exception) {
|
||||
TangemLogger.e("Failed to get fee", e)
|
||||
// it's impossible next steps without fee
|
||||
val allowanceInfo = getAllowanceInfoUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = fromToken,
|
||||
spenderAddress = requireNotNull(spenderAddress) { "spenderAddress cant be null" },
|
||||
requiredAmount = swapAmount.value,
|
||||
).getOrNull()
|
||||
|
||||
val amount = if (allowanceInfo is AllowanceInfo.ResetNeeded) {
|
||||
BigDecimal.ZERO
|
||||
} else {
|
||||
swapAmount.value
|
||||
}
|
||||
|
||||
val approveTransaction = createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = fromTokenStatus,
|
||||
userWalletId = userWalletId,
|
||||
amount = amount,
|
||||
contractAddress = fromToken.getContractAddress(),
|
||||
spenderAddress = spenderAddress,
|
||||
).getOrElse { error ->
|
||||
TangemLogger.e("Failed to create approveTransaction", error)
|
||||
return createSwapErrorWith(
|
||||
fromToken = fromTokenStatus,
|
||||
fromAccount = fromAccount,
|
||||
|
|
@ -2063,6 +2058,12 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
val feeData = getFeeUseCase(
|
||||
transactionData = approveTransaction,
|
||||
network = fromToken.network,
|
||||
userWallet = userWallet,
|
||||
).getOrNull() ?: error("unable to calculate fee")
|
||||
|
||||
val feeState = feeData
|
||||
.patchTransactionFeeForSwap(INCREASE_GAS_LIMIT_FOR_DEX)
|
||||
.toTxFeeState(fromToken, null)
|
||||
|
|
@ -2084,6 +2085,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
amount = INFINITY_SYMBOL,
|
||||
walletAddress = getWalletAddress(fromToken.network),
|
||||
spenderAddress = getTokenAddress(fromToken),
|
||||
isResetApproval = allowanceInfo is AllowanceInfo.ResetNeeded,
|
||||
requestApproveData = RequestApproveStateData(
|
||||
fee = feeState,
|
||||
fromTokenAmount = swapAmount,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ sealed class PermissionDataState {
|
|||
val walletAddress: String,
|
||||
val spenderAddress: String,
|
||||
val requestApproveData: RequestApproveStateData,
|
||||
val isResetApproval: Boolean,
|
||||
) : PermissionDataState()
|
||||
|
||||
object PermissionFailed : PermissionDataState()
|
||||
|
|
|
|||
|
|
@ -206,10 +206,15 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
feeCryptoCurrencyStatus = feeCryptoCurrency,
|
||||
amount = model.dataState.amount.orEmpty(),
|
||||
spenderAddress = requireNotNull(model.dataState.approveDataModel).spenderAddress,
|
||||
subtitle = resourceReference(
|
||||
id = R.string.give_permission_swap_subtitle,
|
||||
formatArgs = wrappedList(providerName, permissionState.currency),
|
||||
),
|
||||
subtitle = if (permissionState.isResetApproval) {
|
||||
resourceReference(R.string.update_approval_permission_subtitle)
|
||||
} else {
|
||||
resourceReference(
|
||||
id = R.string.give_permission_swap_subtitle,
|
||||
formatArgs = wrappedList(providerName, permissionState.currency),
|
||||
)
|
||||
},
|
||||
isResetApproval = permissionState.isResetApproval,
|
||||
isHoldToConfirm = model.isHoldToConfirmEnabled,
|
||||
callback = model.approvalCallback,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -975,6 +975,7 @@ internal class StateBuilder(
|
|||
dialogText = resourceReference(R.string.swapping_approve_information_text),
|
||||
footerText = resourceReference(R.string.swap_give_permission_fee_footer),
|
||||
onOpenLearnMoreAboutApproveClick = onOpenLearnMoreAboutApproveClick,
|
||||
isResetApproval = permissionDataState.isResetApproval,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue