Updated on 2026-08-14
This commit is contained in:
commit
35420a4a7a
29 changed files with 875 additions and 88 deletions
|
|
@ -401,6 +401,9 @@ class TokenItemStateConverter(
|
|||
onYieldPromoCloseClick: (() -> Unit)?,
|
||||
): TokenItemState.PromoBannerState {
|
||||
val token = status.currency as? CryptoCurrency.Token ?: return TokenItemState.PromoBannerState.Empty
|
||||
if (status.value !is CryptoCurrencyStatus.Loaded) {
|
||||
return TokenItemState.PromoBannerState.Empty
|
||||
}
|
||||
if (yieldSupplyPromoBannerKey == null || yieldSupplyPromoBannerKey != token.yieldSupplyKey() ||
|
||||
yieldModuleApyMap[token.yieldSupplyKey()] == null
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,54 @@ sealed class ApiResponseError : Exception() {
|
|||
val errorBody: String?,
|
||||
) : ApiResponseError() {
|
||||
|
||||
fun isServerError(): Boolean = when (code) {
|
||||
Code.OK,
|
||||
Code.CREATED,
|
||||
Code.ACCEPTED,
|
||||
Code.NOT_MODIFIED,
|
||||
Code.BAD_REQUEST,
|
||||
Code.UNAUTHORIZED,
|
||||
Code.PAYMENT_REQUIRED,
|
||||
Code.FORBIDDEN,
|
||||
Code.NOT_FOUND,
|
||||
Code.METHOD_NOT_ALLOWED,
|
||||
Code.NOT_ACCEPTABLE,
|
||||
Code.PROXY_AUTHENTICATION_REQUIRED,
|
||||
Code.REQUEST_TIMEOUT,
|
||||
Code.CONFLICT,
|
||||
Code.GONE,
|
||||
Code.LENGTH_REQUIRED,
|
||||
Code.PRECONDITION_FAILED,
|
||||
Code.PAYLOAD_TOO_LARGE,
|
||||
Code.URI_TOO_LONG,
|
||||
Code.UNSUPPORTED_MEDIA_TYPE,
|
||||
Code.RANGE_NOT_SATISFIABLE,
|
||||
Code.EXPECTATION_FAILED,
|
||||
Code.IM_A_TEAPOT,
|
||||
Code.UNPROCESSABLE_ENTITY,
|
||||
Code.LOCKED,
|
||||
Code.FAILED_DEPENDENCY,
|
||||
Code.TOO_EARLY,
|
||||
Code.UPGRADE_REQUIRED,
|
||||
Code.PRECONDITION_REQUIRED,
|
||||
Code.TOO_MANY_REQUESTS,
|
||||
Code.REQUEST_HEADER_FIELDS_TOO_LARGE,
|
||||
Code.UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
-> false
|
||||
Code.INTERNAL_SERVER_ERROR,
|
||||
Code.NOT_IMPLEMENTED,
|
||||
Code.BAD_GATEWAY,
|
||||
Code.SERVICE_UNAVAILABLE,
|
||||
Code.GATEWAY_TIMEOUT,
|
||||
Code.HTTP_VERSION_NOT_SUPPORTED,
|
||||
Code.VARIANT_ALSO_NEGOTIATES,
|
||||
Code.INSUFFICIENT_STORAGE,
|
||||
Code.LOOP_DETECTED,
|
||||
Code.NOT_EXTENDED,
|
||||
Code.NETWORK_AUTHENTICATION_REQUIRED,
|
||||
-> true
|
||||
}
|
||||
|
||||
// TODO: extract Code from HttpException
|
||||
// region Error Codes
|
||||
enum class Code(val numericCode: Int) {
|
||||
|
|
|
|||
|
|
@ -896,6 +896,8 @@
|
|||
<string name="reset_card_without_backup_to_factory_message">La réinitialisation aux paramètres d\'usine supprimera complètement le portefeuille de la carte sélectionnée et le supprimera de l\'application. Vous ne pourrez pas restaurer le portefeuille actuel.</string>
|
||||
<string name="ring_promo_text">Les propriétaires du Tangem Ring ont droit à 3 swap gratis sur Changelly jusqu\'au 15/11 !</string>
|
||||
<string name="ring_promo_title">Échangez avec 0 % de frais !</string>
|
||||
<string name="root_detected_warning_description">Les appareils disposant d\'un accès root sont considérés comme moins sécurisés. Vos données peuvent être exposées à des risques supplémentaires.</string>
|
||||
<string name="root_detected_warning_title">Accès root détecté</string>
|
||||
<string name="save_user_wallet_agreement_access_description">Connectez-vous à l\'application et vérifiez votre solde sans scanner la carte</string>
|
||||
<string name="save_user_wallet_agreement_access_title">Accéder à l\'application</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">Autoriser l\'utilisation de la biométrie</string>
|
||||
|
|
|
|||
|
|
@ -103,10 +103,10 @@
|
|||
<string name="app_settings_enable_biometrics_description">設定に移動して、Tangemアプリで生体認証を有効にします。</string>
|
||||
<string name="app_settings_enable_biometrics_title">生体認証を有効にする</string>
|
||||
<string name="app_settings_off_biometrics_alert_message">%1$sが無効になると、アプリのロックを解除してウォレットを操作するために、パスコードを入力する必要があります。</string>
|
||||
<string name="app_settings_off_require_access_code_alert_message">後でウォレットのアクセスコードを入力してもらいます。それを安全に保存し、今後の利用に備えるためです。</string>
|
||||
<string name="app_settings_off_saved_access_code_alert_message">これにより、保存されているウォレットアクセスコードがすべて削除されます。ウォレットでの今後の操作には、アクセスコードの送信が必要になります。</string>
|
||||
<string name="app_settings_off_require_access_code_alert_message">安全に保管するため、後ほどアクセスコードの入力を求められます。</string>
|
||||
<string name="app_settings_off_saved_access_code_alert_message">保存されているすべてのウォレットのアクセスコードが削除されます。ウォレットを使用するには、再度アクセスコードを入力する必要があります。</string>
|
||||
<string name="app_settings_off_saved_wallet_alert_message">保存したデバイスを削除すると、保存されているすべてのウォレットとそのアクセスコードがアプリから削除されます。</string>
|
||||
<string name="app_settings_on_require_access_code_alert_message">これにより、保存されているウォレットアクセスコードがすべて削除されます。今後ウォレットを操作するには、アクセスコードの送信が必要になります。</string>
|
||||
<string name="app_settings_on_require_access_code_alert_message">保存されているすべてのウォレットのアクセスコードが削除されます。ウォレットを使用するには、再度アクセスコードを入力する必要があります。</string>
|
||||
<string name="app_settings_require_access_code">アクセスコードを要求する</string>
|
||||
<string name="app_settings_require_access_code_footer">このオプションを選択すると、機密性の高い操作における生体認証が無効になります。取引に署名するたびにアクセスコードを入力する必要があります。</string>
|
||||
<string name="app_settings_saved_access_codes">アクセスコードを保存</string>
|
||||
|
|
@ -144,10 +144,10 @@
|
|||
<string name="balance_hidden_title">残高は非表示</string>
|
||||
<string name="beta_mode_warning_message">ブロックチェーン開発者によると、Kaspaトークンは現在ベータ版です。アップデートをお楽しみに!</string>
|
||||
<string name="beta_mode_warning_title">ベータモード</string>
|
||||
<string name="biometric_disabled_warning_description">デバイスで生体認証がオフになっているため、ウォレットのロック解除に使用できません。生体認証を再度利用するには、デバイスの設定で有効にしてください。</string>
|
||||
<string name="biometric_disabled_warning_description">この端末では生体認証がオフになっているため、ウォレットの解除に使用できません。再度この方法を使用するには、端末の設定で生体認証を有効にしてください。</string>
|
||||
<string name="biometric_disabled_warning_title">生体認証が無効になっています</string>
|
||||
<string name="biometric_lockout_permanent_warning_description">カードまたはリングをスキャンしてください</string>
|
||||
<string name="biometric_lockout_permanent_warning_description_2">生体認証の試行回数が上限に達しました。端末タップまたはアクセスコードでウォレットを解除してください。</string>
|
||||
<string name="biometric_lockout_permanent_warning_description_2">生体認証の試行回数の上限に達しました。カード/リングでウォレットを解除するか、アクセスコードを入力してください。</string>
|
||||
<string name="biometric_lockout_permanent_warning_title">生体認証がロックされています</string>
|
||||
<string name="biometric_lockout_warning_description">30秒後に再試行するか、カードまたはリングをスキャンしてください</string>
|
||||
<string name="biometric_lockout_warning_description_2">生体認証ログインは一時的にロックされています。30秒後にもう一度お試しいただくか、端末タップまたはアクセスコードでウォレットを解除してください。</string>
|
||||
|
|
@ -179,7 +179,7 @@
|
|||
<string name="card_reset_alert_finish_ok_button">もう一度アップグレードしてください</string>
|
||||
<string name="card_reset_alert_finish_title">リセット完了</string>
|
||||
<string name="card_reset_alert_incomplete_message">このウォレット内のすべてのTangemデバイスで、リセット処理を完了することを推奨します。</string>
|
||||
<string name="card_reset_alert_incomplete_title">すべてのTangemデバイスをリセットしていません</string>
|
||||
<string name="card_reset_alert_incomplete_title">リセットが必要なTangemデバイスがあります。</string>
|
||||
<string name="card_settings_access_code_recovery_disabled_description">このカードを使用して、このウォレット内の他のカードまたはリングのアクセスコードをリセットしたくない場合は、このオプションを無効にしてください。これにより、このカードのアクセスコードもリセットできなくなりますのでご注意ください。</string>
|
||||
<string name="card_settings_access_code_recovery_enabled_description">このカードを使用して、このウォレット内の他のカードのアクセスコードをリセットできます。</string>
|
||||
<string name="card_settings_access_code_recovery_title">アクセスコードの復元</string>
|
||||
|
|
@ -378,6 +378,7 @@
|
|||
<string name="common_transfer">送金</string>
|
||||
<string name="common_unable_to_load">データを読み込めません…</string>
|
||||
<string name="common_understand">わかりました</string>
|
||||
<string name="common_understand_continue">理解して続行</string>
|
||||
<string name="common_unknown_error">エラーが発生しました。もう一度お試しください。</string>
|
||||
<string name="common_unreachable">アクセスできません</string>
|
||||
<string name="common_unstake">ステーキング解除</string>
|
||||
|
|
@ -567,7 +568,7 @@
|
|||
<string name="home_button_create_new_wallet">新しいウォレットを作成する</string>
|
||||
<string name="home_button_order">Tangemを注文</string>
|
||||
<string name="home_button_scan">Tangemをスキャン</string>
|
||||
<string name="hot_access_code_set_biometric_ask">「Tangem」に生体認証の使用を許可しますか?\n本人確認とアプリの起動のために使用されます。</string>
|
||||
<string name="hot_access_code_set_biometric_ask">「Tangem」が生体認証を使用して本人確認を行い、アプリを開くことを許可しますか?</string>
|
||||
<string name="hot_crypto_add_token_subtitle">%sへ</string>
|
||||
<string name="hot_crypto_token_network">%sネットワーク</string>
|
||||
<string name="hw_access_code_create_alert_title">アクセスコードの設定をキャンセルしてもよろしいですか?</string>
|
||||
|
|
@ -1059,6 +1060,7 @@
|
|||
<string name="reset_card_to_factory_button_title">カードをリセットする</string>
|
||||
<string name="reset_card_to_factory_condition_1">この操作を実行すると、現在のウォレットにアクセスできなくなることを理解しています。</string>
|
||||
<string name="reset_card_to_factory_condition_2">このカードを使用して、現在のウォレットの他のカードのアクセスコードを回復させられないことを認識しています。</string>
|
||||
<string name="reset_card_to_factory_condition_3">復元は不可能であり、Tangem Payカードおよびカード上のすべての資金へのアクセスを完全に失うことを理解しました</string>
|
||||
<string name="reset_card_with_backup_to_factory_message">工場出荷時設定にリセットすると、選択したカードやリングからウォレットが完全に削除されます。現在のウォレットを復元したり、カードやリングを使用してアクセスコードを復元することはできません。</string>
|
||||
<string name="reset_card_without_backup_to_factory_message">工場出荷時の状態にリセットすると、選択したカードやリングからウォレットが完全に削除され、アプリから削除されます。現在のウォレットを復元することはできません。</string>
|
||||
<string name="reset_cards_dialog_complete_description">すべてのTangemデバイスがリセットされました。</string>
|
||||
|
|
@ -1067,6 +1069,8 @@
|
|||
<string name="reset_cards_dialog_next_device_description">続行するには次のデバイスをリセットしてください</string>
|
||||
<string name="ring_promo_text">Tangem Ringユーザーは、11/15日までChangelly経由でスワップを3回手数料ゼロで行えます!</string>
|
||||
<string name="ring_promo_title">今すぐ手数料0% でスワップしましょう!</string>
|
||||
<string name="root_detected_warning_description">Rootアクセスが有効な端末は、セキュリティが低いと判断されます。データが追加のリスクにさらされる可能性があります。</string>
|
||||
<string name="root_detected_warning_title">Rootアクセスが検出されました</string>
|
||||
<string name="save_user_wallet_agreement_access_description">アプリにログインして、カードまたはリングをスキャンせずに残高を確認できます</string>
|
||||
<string name="save_user_wallet_agreement_access_title">アプリにアクセスする</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">生体認証の使用を許可する</string>
|
||||
|
|
@ -1402,6 +1406,7 @@
|
|||
<string name="tangem_pay_freeze_card_success">カードが凍結されています</string>
|
||||
<string name="tangem_pay_get_help">サポートを受ける</string>
|
||||
<string name="tangem_pay_other">その他</string>
|
||||
<string name="tangem_pay_rooted_device_subtitle">Root化された端末では使用できません</string>
|
||||
<string name="tangem_pay_status_completed">完了</string>
|
||||
<string name="tangem_pay_status_declined">拒否</string>
|
||||
<string name="tangem_pay_status_pending">保留中</string>
|
||||
|
|
@ -1414,6 +1419,8 @@
|
|||
<string name="tangem_pay_unfreeze_card_failed">カードの凍結解除に失敗しました。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="tangem_pay_unfreeze_card_success">カードの凍結が解除されました</string>
|
||||
<string name="tangem_pay_withdrawal">出金</string>
|
||||
<string name="tangempay_account_unable_to_use_rooted">Root化された端末では使用できません</string>
|
||||
<string name="tangempay_cancel_kyc">KYCをキャンセル</string>
|
||||
<string name="tangempay_card_details_add_funds">資金を追加</string>
|
||||
<string name="tangempay_card_details_add_funds_subtitle">入金オプション</string>
|
||||
<string name="tangempay_card_details_card_number">カード番号</string>
|
||||
|
|
@ -1441,6 +1448,7 @@
|
|||
<string name="tangempay_card_details_open_wallet_step_5">準備完了!カードはすぐに使用できます。</string>
|
||||
<string name="tangempay_card_details_open_wallet_title">Google Payにカードを追加する</string>
|
||||
<string name="tangempay_card_details_open_wallet_title_apple">Apple Payにカードを追加する</string>
|
||||
<string name="tangempay_card_details_pin_code">PINコード</string>
|
||||
<string name="tangempay_card_details_receive_description">アドレスを共有するか、QRコードを表示してください。</string>
|
||||
<string name="tangempay_card_details_receive_error_description">技術的な問題が検出されました。しばらくしてからもう一度お試しいただくか、サポートにお問い合わせください。</string>
|
||||
<string name="tangempay_card_details_receive_error_title">現在、受け取りは利用できません</string>
|
||||
|
|
@ -1449,12 +1457,15 @@
|
|||
<string name="tangempay_card_details_swap_description">ポートフォリオ内のあらゆる資産をカードと交換</string>
|
||||
<string name="tangempay_card_details_title">カードの詳細</string>
|
||||
<string name="tangempay_card_details_unfreeze_card">カードの一時停止を解除</string>
|
||||
<string name="tangempay_card_details_view_pin_code_description">忘れた場合は、アプリに戻ってください。</string>
|
||||
<string name="tangempay_card_details_view_pin_code_title">PINコード</string>
|
||||
<string name="tangempay_card_details_withdraw">出金</string>
|
||||
<string name="tangempay_card_details_withdraw_error_title">現在、出金ができません</string>
|
||||
<string name="tangempay_card_details_withdraw_in_progress_description">現在の処理が完了するまでは、スワップや新しい出金を開始することはできません。</string>
|
||||
<string name="tangempay_card_details_withdraw_in_progress_title">出金処理中です</string>
|
||||
<string name="tangempay_change_pin_code">PINコードを変更</string>
|
||||
<string name="tangempay_come_back_if_forget_pin">忘れた場合はアプリに戻って確認できます。</string>
|
||||
<string name="tangempay_factory_settings_warning_title">復元は不可能であり、Tangem Payカードおよびカード上のすべての資金へのアクセスを完全に失うことを理解しました</string>
|
||||
<string name="tangempay_failed_to_issue_card">カードの発行に失敗しました</string>
|
||||
<string name="tangempay_failed_to_issue_card_retry_description">技術的なエラーが発生しました。下のボタンをクリックして、もう一度お試しください。</string>
|
||||
<string name="tangempay_failed_to_issue_card_support_description">技術的なエラーが発生しました。サポートへお問い合わせください。</string>
|
||||
|
|
@ -1471,6 +1482,7 @@
|
|||
<string name="tangempay_kyc_in_progress">KYC進行中</string>
|
||||
<string name="tangempay_kyc_in_progress_notification_button">ステータスを表示</string>
|
||||
<string name="tangempay_kyc_in_progress_notification_title">Tangem PayのKYC手続き進行中</string>
|
||||
<string name="tangempay_kyc_in_progress_popup_description">以下のボタンから、現在のKYCステータスを確認するか、KYCをキャンセルできます。</string>
|
||||
<string name="tangempay_onboarding_banner_description">暗号資産を、リアルな支払いに。\n他とはまったく違う、新しいタイプの決済カード。</string>
|
||||
<string name="tangempay_onboarding_banner_title">Tangem Visaカード</string>
|
||||
<string name="tangempay_onboarding_get_card_button_text">カードをGET</string>
|
||||
|
|
@ -1488,7 +1500,7 @@
|
|||
<string name="tangempay_service_unreachable_try_later">現在、データを表示できませんが、カードでのお支払いは引き続きご利用いただけます。</string>
|
||||
<string name="tangempay_sync_needed">同期が必要です</string>
|
||||
<string name="tangempay_tangem_visa_card">Tangem Visaカード</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Payは一時的に利用できません</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Payは現在一時的に利用できません。</string>
|
||||
<string name="tangempay_title">Tangem Pay</string>
|
||||
<string name="tangempay_use_tangem_device_to_restore_payment_account">カードまたはリングを使用して、支払いアカウントへのアクセスを復元してください。</string>
|
||||
<string name="tangempay_your_pin_code">PINコード</string>
|
||||
|
|
@ -1572,7 +1584,7 @@
|
|||
<string name="user_push_notification_agreement_header">プッシュ通知を使用しますか?</string>
|
||||
<string name="user_push_notification_banner_subtitle">プッシュ通知を有効にすると、ウォレットに着金したときにアラートを受信できます。</string>
|
||||
<string name="user_push_notification_banner_title">取引を見逃さない</string>
|
||||
<string name="user_wallet_list_add_button">新しいウォレットを追加</string>
|
||||
<string name="user_wallet_list_add_button">ウォレットを追加</string>
|
||||
<string name="user_wallet_list_delete_hw_prompt">バックアップせずにこのウォレットを削除すると、資金に永久にアクセスできなくなります。</string>
|
||||
<string name="user_wallet_list_delete_prompt">このウォレットを忘れてもよろしいですか?</string>
|
||||
<string name="user_wallet_list_error_unable_to_unlock">エラーが発生しました。カードまたはリングをスキャンしてログインしてください。</string>
|
||||
|
|
@ -1879,7 +1891,7 @@
|
|||
<string name="wc_warning_transaction">不審な取引</string>
|
||||
<string name="welcome_create_wallet_already_have">すでにTangemウォレットをお持ちですか?</string>
|
||||
<string name="welcome_create_wallet_feature_assets">数千種類の資産</string>
|
||||
<string name="welcome_create_wallet_feature_class">業界最高水準のハードウェアウォレット</string>
|
||||
<string name="welcome_create_wallet_feature_class">最高水準のハードウェアウォレット</string>
|
||||
<string name="welcome_create_wallet_feature_delivery">迅速な配送</string>
|
||||
<string name="welcome_create_wallet_feature_one_tap">ワンタップで開始</string>
|
||||
<string name="welcome_create_wallet_feature_seamless">シームレスで安全</string>
|
||||
|
|
|
|||
|
|
@ -394,6 +394,7 @@
|
|||
<string name="common_transfer">Перевод</string>
|
||||
<string name="common_unable_to_load">Невозможно загрузить данные…</string>
|
||||
<string name="common_understand">Я понял</string>
|
||||
<string name="common_understand_continue">Я понимаю, продолжить</string>
|
||||
<string name="common_unknown_error">Произошла ошибка. Пожалуйста, попробуйте снова.</string>
|
||||
<string name="common_unreachable">Недоступно</string>
|
||||
<string name="common_unstake">Завершить стейкинг</string>
|
||||
|
|
@ -733,6 +734,7 @@
|
|||
<string name="markets_sort_by_top_gainers_title">Лидеры роста</string>
|
||||
<string name="markets_sort_by_top_losers_title">Лидеры падения</string>
|
||||
<string name="markets_sort_by_trending_title">В тренде</string>
|
||||
<string name="markets_sort_by_yield_mode_title">Режим доходности</string>
|
||||
<string name="markets_staking_banner_description_placeholder">Стейкинг — простой способ получать доход с вашей криптовалюты. %s</string>
|
||||
<string name="markets_staking_banner_title">Получайте до %s APY</string>
|
||||
<string name="markets_token_added">Токен добавлен</string>
|
||||
|
|
@ -994,6 +996,7 @@
|
|||
<string name="onramp_currency_other">Другие валюты</string>
|
||||
<string name="onramp_currency_popular">Популярные фиаты</string>
|
||||
<string name="onramp_currency_search">Поиск по валюте</string>
|
||||
<string name="onramp_error_transaction_already_processed">Эта транзакция уже была обработана. Дополнительных действий не требуется.</string>
|
||||
<string name="onramp_fetching_best_rates">Получение лучших курсов...</string>
|
||||
<string name="onramp_instant_status">Моментально</string>
|
||||
<string name="onramp_legal">Пользуясь сервисом покупки, вы соглашаетесь с %1$s и %2$s</string>
|
||||
|
|
@ -1104,6 +1107,8 @@
|
|||
<string name="reset_cards_dialog_next_device_description">Сбросьте следующее устройство для продолжения.</string>
|
||||
<string name="ring_promo_text">Владельцам колец — 3 обмена без комиссии на Changelly до 15.11!</string>
|
||||
<string name="ring_promo_title">Обмен с 0% комиссией!</string>
|
||||
<string name="root_detected_warning_description">Устройства с root-доступом считаются менее безопасными. Ваши данные могут быть подвержены дополнительным рискам.</string>
|
||||
<string name="root_detected_warning_title">Обнаружен root-доступ</string>
|
||||
<string name="save_user_wallet_agreement_access_description">Войдите в приложение и следите за своим балансом без сканирования карты или кольца</string>
|
||||
<string name="save_user_wallet_agreement_access_title">Доступ в приложение</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">Использовать биометрию</string>
|
||||
|
|
@ -1493,6 +1498,7 @@
|
|||
<string name="tangempay_card_details_withdraw_in_progress_title">Вывод выполняется</string>
|
||||
<string name="tangempay_change_pin_code">Изменить PIN-код</string>
|
||||
<string name="tangempay_come_back_if_forget_pin">Можно посмотреть здесь, если забудете его.</string>
|
||||
<string name="tangempay_factory_settings_warning_title">Я понимаю, что полностью потеряю доступ к своей карте Tangem Pay и ко всем средствам на ней без возможности восстановления.</string>
|
||||
<string name="tangempay_failed_to_issue_card">Не удалось выпустить карту</string>
|
||||
<string name="tangempay_failed_to_issue_card_retry_description">Техническая ошибка, попробуйте ещё раз, нажав кнопку ниже</string>
|
||||
<string name="tangempay_failed_to_issue_card_support_description">Техническая ошибка, свяжитесь с поддержкой</string>
|
||||
|
|
@ -1964,6 +1970,7 @@
|
|||
<string name="yield_module_fee_policy_sheet_title">Политика комиссий за пополнение</string>
|
||||
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem взимает комиссию за обслуживание в размере 15% от полученного дохода.</string>
|
||||
<string name="yield_module_high_fee_error">Ваши средства будут автоматически переведены в Aave, как только комиссия сети снизится или баланс достигнет минимально необходимой суммы.</string>
|
||||
<string name="yield_module_high_network_fees_notification_description">Комиссии сейчас выше обычного из-за высокой активности на рынке. Вы можете продолжить сейчас или вернуться позже, когда комиссии снизятся.</string>
|
||||
<string name="yield_module_high_network_fees_notification_title">Высокая сетевая комиссия</string>
|
||||
<string name="yield_module_historical_returns">Историческая доходность</string>
|
||||
<string name="yield_module_main_screen_promo_banner_message">Получай до %1$s APY на свой баланс</string>
|
||||
|
|
|
|||
|
|
@ -1444,6 +1444,7 @@
|
|||
<string name="tangem_pay_unfreeze_card_success">Your card is unfrozen.</string>
|
||||
<string name="tangem_pay_withdrawal">Withdrawal</string>
|
||||
<string name="tangempay_account_unable_to_use_rooted">Unable to use on rooted device</string>
|
||||
<string name="tangempay_cancel_kyc">Cancel KYC</string>
|
||||
<string name="tangempay_card_details_add_funds">Add funds</string>
|
||||
<string name="tangempay_card_details_add_funds_subtitle">Top-up options</string>
|
||||
<string name="tangempay_card_details_card_number">Card Number</string>
|
||||
|
|
@ -1505,6 +1506,7 @@
|
|||
<string name="tangempay_kyc_in_progress">KYC in progress</string>
|
||||
<string name="tangempay_kyc_in_progress_notification_button">View Status</string>
|
||||
<string name="tangempay_kyc_in_progress_notification_title">KYC in progress for Tangem Pay</string>
|
||||
<string name="tangempay_kyc_in_progress_popup_description">Use the buttons below to view your current KYC status or cancel it.</string>
|
||||
<string name="tangempay_onboarding_banner_description">Use your crypto for real world spending. \nIt’s a payment card unlike any other.</string>
|
||||
<string name="tangempay_onboarding_banner_title">Tangem Visa Card</string>
|
||||
<string name="tangempay_onboarding_get_card_button_text">Get card</string>
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ internal fun YieldSupplyPromoBanner(state: PromoBannerState.Content, modifier: M
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.background(color = bgColor, shape = TangemTheme.shapes.roundedCornersXMedium)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
.clickable(onClick = state.onPromoBannerClick)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -172,10 +172,11 @@ internal class TangemPayRequestPerformer @Inject constructor(
|
|||
refreshToken = response.refreshToken,
|
||||
refreshExpiresAt = response.refreshExpiresAt,
|
||||
)
|
||||
}.mapLeft { error ->
|
||||
Timber.tag(TAG).e("Can not refresh auth tokens: $error")
|
||||
if (error is VisaApiError.ServerUnavailable) error else VisaApiError.RefreshTokenExpired
|
||||
}.onRight { tokens ->
|
||||
tangemPayStorage.storeAuthTokens(customerWalletAddress = customerWalletAddress, tokens = tokens)
|
||||
}.onLeft {
|
||||
Timber.tag(TAG).e("Can not refresh auth tokens: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ internal class TangemPayErrorConverter @Inject constructor(
|
|||
|
||||
override fun convert(value: Throwable): VisaApiError {
|
||||
return if (value is ApiResponseError.HttpException) {
|
||||
if (value.isServerError()) return VisaApiError.ServerUnavailable
|
||||
if (value.code == ApiResponseError.HttpException.Code.NOT_FOUND) return VisaApiError.NotPaeraCustomer
|
||||
if (value.code == ApiResponseError.HttpException.Code.UNAUTHORIZED) return VisaApiError.RefreshTokenExpired
|
||||
|
||||
|
|
|
|||
|
|
@ -281,8 +281,10 @@ internal class DefaultWalletManagersFacade @Inject constructor(
|
|||
is Result.Success -> PaginationWrapper(
|
||||
currentPage = sdkPageConverter.convert(page),
|
||||
nextPage = sdkPageConverter.convert(itemsResult.data.nextPage),
|
||||
items = SdkTransactionHistoryItemConverter(smartContractMethods = readSmartContractMethods())
|
||||
.convertList(itemsResult.data.items),
|
||||
items = SdkTransactionHistoryItemConverter(
|
||||
smartContractMethods = readSmartContractMethods(),
|
||||
yieldSupplyAddresses = YIELD_SUPPLY_ADDRESSES,
|
||||
).convertList(itemsResult.data.items),
|
||||
)
|
||||
is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,13 @@ import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem as
|
|||
|
||||
internal class SdkTransactionHistoryItemConverter(
|
||||
smartContractMethods: Map<String, SmartContractMethod>,
|
||||
yieldSupplyAddresses: Set<String>,
|
||||
) : Converter<SdkTransactionHistoryItem, TxInfo> {
|
||||
|
||||
private val typeConverter by lazy { SdkTransactionTypeConverter(smartContractMethods) }
|
||||
private val typeConverter by lazy { SdkTransactionTypeConverter(
|
||||
smartContractMethods = smartContractMethods,
|
||||
yieldSupplyAddresses = yieldSupplyAddresses,
|
||||
) }
|
||||
|
||||
override fun convert(value: SdkTransactionHistoryItem): TxInfo = TxInfo(
|
||||
txHash = value.txHash,
|
||||
|
|
@ -24,7 +28,7 @@ internal class SdkTransactionHistoryItemConverter(
|
|||
SdkTransactionHistoryItem.TransactionStatus.Failed -> TxInfo.TransactionStatus.Failed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Unconfirmed -> TxInfo.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = typeConverter.convert(value.type to value.destinationType.toDomain()),
|
||||
type = typeConverter.convert(value),
|
||||
amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" },
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.data.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem.TransactionType
|
||||
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyEnterCallData
|
||||
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyExitCallData
|
||||
|
|
@ -11,17 +12,29 @@ import com.tangem.utils.converter.Converter
|
|||
|
||||
internal class SdkTransactionTypeConverter(
|
||||
private val smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<Pair<TransactionType, TxInfo.DestinationType>, TxInfo.TransactionType> {
|
||||
private val yieldSupplyAddresses: Set<String>,
|
||||
) : Converter<TransactionHistoryItem, TxInfo.TransactionType> {
|
||||
|
||||
override fun convert(value: Pair<TransactionType, TxInfo.DestinationType>): TxInfo.TransactionType {
|
||||
val (type, destination) = value
|
||||
override fun convert(value: TransactionHistoryItem): TxInfo.TransactionType {
|
||||
val (type, destination) = value.type to value.destinationType
|
||||
val source = value.sourceType
|
||||
|
||||
return when (type) {
|
||||
is TransactionType.ContractMethod -> {
|
||||
getTransactionType(methodName = smartContractMethods[type.id]?.name, type.callData, destination)
|
||||
getTransactionType(
|
||||
methodName = smartContractMethods[type.id]?.name,
|
||||
callData = type.callData,
|
||||
destination = destination,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
is TransactionType.ContractMethodName -> {
|
||||
getTransactionType(methodName = type.name, type.callData, destination)
|
||||
getTransactionType(
|
||||
methodName = type.name,
|
||||
callData = type.callData,
|
||||
destination = destination,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
is TransactionType.Transfer -> {
|
||||
TxInfo.TransactionType.Transfer
|
||||
|
|
@ -48,7 +61,8 @@ internal class SdkTransactionTypeConverter(
|
|||
private fun getTransactionType(
|
||||
methodName: String?,
|
||||
callData: String?,
|
||||
destination: TxInfo.DestinationType,
|
||||
destination: TransactionHistoryItem.DestinationType,
|
||||
source: TransactionHistoryItem.SourceType,
|
||||
): TxInfo.TransactionType {
|
||||
return when (methodName) {
|
||||
"transfer" -> TxInfo.TransactionType.Transfer
|
||||
|
|
@ -70,7 +84,21 @@ internal class SdkTransactionTypeConverter(
|
|||
"withdrawRewardsPOL",
|
||||
-> TxInfo.TransactionType.Staking.ClaimRewards
|
||||
"redelegate" -> TxInfo.TransactionType.Staking.Restake
|
||||
"yieldSend" -> TxInfo.TransactionType.YieldSupply.Send
|
||||
"yieldSend" -> {
|
||||
val sourceAddresses = when (source) {
|
||||
is TransactionHistoryItem.SourceType.Multiple -> source.addresses
|
||||
is TransactionHistoryItem.SourceType.Single -> listOf(source.address)
|
||||
}.map {
|
||||
it.lowercase()
|
||||
}.toSet()
|
||||
|
||||
val isYieldSupplyWithdraw =
|
||||
yieldSupplyAddresses.intersect(sourceAddresses).isNotEmpty()
|
||||
|
||||
TxInfo.TransactionType.YieldSupply.Send(
|
||||
isYieldSupplyWithdraw = isYieldSupplyWithdraw,
|
||||
)
|
||||
}
|
||||
"enterProtocolByOwner" -> callData?.let { data ->
|
||||
TxInfo.TransactionType.YieldSupply.Enter(
|
||||
EthereumYieldSupplyEnterCallData.decode(data)?.tokenContractAddress.orEmpty(),
|
||||
|
|
@ -82,7 +110,7 @@ internal class SdkTransactionTypeConverter(
|
|||
)
|
||||
}
|
||||
"deployYieldModule" -> TxInfo.TransactionType.YieldSupply.DeployContract(
|
||||
(destination as? TxInfo.DestinationType.Single)?.addressType?.address.orEmpty(),
|
||||
(destination as? TransactionHistoryItem.DestinationType.Single)?.addressType?.address.orEmpty(),
|
||||
)
|
||||
"initYieldToken" -> callData?.let { data ->
|
||||
TxInfo.TransactionType.YieldSupply.InitializeToken(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
package com.tangem.data.walletmanager.utils
|
||||
|
||||
// Remove after moving this data to BE
|
||||
internal val YIELD_SUPPLY_ADDRESSES: Set<String> = setOf(
|
||||
"0x00901a076785e0906d1028c7d6372d247bec7d61",
|
||||
"0x00907f9921424583e7ffbfedf84f92b7b2be4977",
|
||||
"0x018008bfb33d285247a21d44e50697654f754e63",
|
||||
"0x067ae75628177fd257c2b1e500993e1a0babcbd1",
|
||||
"0x078f358208685046a11c85e8ad32895ded33a249",
|
||||
"0x0a1d576f3efef75b330424287a95a366e8281d54",
|
||||
"0x0b925ed163218f6662a35e0f0371ac234f9e9371",
|
||||
"0x0c0d01abf3e6adfca0989ebba9d6e85dd58eab1e",
|
||||
"0x10ac93971cdb1f5c778144084242374473c350da",
|
||||
"0x191c10aa4af7c30e871e70c95db0e4eb77237530",
|
||||
"0x1ba9843bd4327c6c77011406de5fa8749f7e3479",
|
||||
"0x1c0e06a0b1a4c160c17545ff2a951bfca57c0002",
|
||||
"0x23878914efe38d27c4d67ab83ed1b93a74d4086a",
|
||||
"0x24ab03a9a5bc2c49e5523e8d915a3536ac38b91d",
|
||||
"0x2516e7b3f76294e03c42aa4c5b5b4dce9c436fb8",
|
||||
"0x285866acb0d60105b4ed350a463361c2d9afa0e2",
|
||||
"0x2d62109243b87c4ba3ee7ba1d91b0dd0a074d7b1",
|
||||
"0x2e94171493fabe316b6205f1585779c887771e2f",
|
||||
"0x2edff5af94334fbd7c38ae318edf1c40e072b73b",
|
||||
"0x312ffc57778cefa11989733e6e08143e7e229c1c",
|
||||
"0x32a6268f9ba3642dda7892add74f1d34469a4259",
|
||||
"0x38a5357ce55c81add62abc84fb32981e2626adef",
|
||||
"0x38c503a438185cde29b5cf4dc1442fd6f074f1cc",
|
||||
"0x38d693ce1df5aadf7bc62595a37d667ad57922e5",
|
||||
"0x3fe6a295459fae07df8a0cecc36f37160fe86aa9",
|
||||
"0x40b4baecc69b882e8804f9286b12228c27f8c9bf",
|
||||
"0x4199cc1f5ed0d796563d7ccb2e036253e2c18281",
|
||||
"0x44705f578135cc5d703b4c9c122528c73eb87145",
|
||||
"0x4579a27af00a62c0eb156349f31b345c08386419",
|
||||
"0x481a2acf3a72ffdc602a9541896ca1db87f86cf7",
|
||||
"0x4b0821e768ed9039a70ed1e80e15e76a5be5df5f",
|
||||
"0x4c612e3b15b96ff9a6faed838f8d07d479a8dd4c",
|
||||
"0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8",
|
||||
"0x4e2a4d9b3df7aae73b418bd39f3af9e148e3f479",
|
||||
"0x4e65fe4dba92790696d040ac24aa414708f5c0ab",
|
||||
"0x4f5923fc5fd4a93352581b38b7cd26943012decf",
|
||||
"0x513c7e3a9c69ca3e22550ef58ac1c0088e918fff",
|
||||
"0x545bd6c032efdde65a377a6719def2796c8e0f2e",
|
||||
"0x56a7ddc4e848ebf43845854205ad71d5d5f72d3d",
|
||||
"0x5b502e3796385e1e9755d7043b9c945c3accec9c",
|
||||
"0x5c647ce0ae10658ec44fa4e11a51c96e94efd1dd",
|
||||
"0x5e8c8a7243651db1384c0ddfdbe39761e8e7e51a",
|
||||
"0x5ee5bf7ae06d1be5997a1a72006fe6c607ec6de8",
|
||||
"0x5f4a0873a3a02f7c0cb0e13a1d4362a1ad90e751",
|
||||
"0x5f9190496e0dfc831c3bd307978de4a245e2f5cd",
|
||||
"0x5fefd7069a7d91d01f269dade14526ccf3487810",
|
||||
"0x625e7708f30ca75bfd92586e17077590c60eb4cd",
|
||||
"0x62fc96b27a510cf4977b59ff952dc32378cc221d",
|
||||
"0x6533afac2e7bccb20dca161449a13a32d391fb00",
|
||||
"0x65906988adee75306021c417a1a3458040239602",
|
||||
"0x67eaf2bee4384a2f84da9eb8105c661c123736ba",
|
||||
"0x6ab707aca953edaefbc4fd23ba73294241490620",
|
||||
"0x6b030ff3fb9956b1b69f475b77ae0d3cf2cc5afa",
|
||||
"0x6d80113e533a2c0fe82eabd35f1875dcea89ea97",
|
||||
"0x71aef7b30728b9bb371578f36c5a1f1502a5723e",
|
||||
"0x724dc807b04555b71ed48a6896b6f41593b8c637",
|
||||
"0x75bd1a659bdc62e4c313950d44a2416fab43e785",
|
||||
"0x7b95ec873268a6bfc6427e7a28e396db9d0ebc65",
|
||||
"0x7c307e128efa31f540f2e2d976c995e0b65f51f6",
|
||||
"0x80a94c36747cf51b2fbabdff045f6d22c1930ed1",
|
||||
"0x80ca0d8c38d2e2bcbab66aa1648bd1c7160500fe",
|
||||
"0x82e64f49ed5ec1bc6e43dad4fc8af9bb3a2312ee",
|
||||
"0x82f9c5ad306bba1ad0de49bb5fa6f01bf61085ef",
|
||||
"0x8437d7c167dfb82ed4cb79cd44b7a32a1dd95c77",
|
||||
"0x8a2b6f94ff3a89a03e8c02ee92b55af90c9454a2",
|
||||
"0x8a458a9dc9048e005d22849f470891b840296619",
|
||||
"0x8a9fde6925a839f6b1932d16b36ac026f8d3fbdb",
|
||||
"0x8eb270e296023e9d92081fdf967ddd7878724424",
|
||||
"0x8ffdf2de812095b1d19cb146e4c004587c0a0692",
|
||||
"0x90072a4aa69b5eb74984ab823efc5f91e90b3a72",
|
||||
"0x90da57e0a6c0d166bf15764e03b83745dc90025b",
|
||||
"0x927709711794f3de5ddbf1d176bee2d55ba13c21",
|
||||
"0x977b6fc5de62598b08c85ac8cf2b745874e8b78c",
|
||||
"0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c",
|
||||
"0x99cbc45ea5bb7ef3a5bc08fb1b7e56bb2442ef0d",
|
||||
"0x9a44fd41566876a39655f74971a3a6ea0a17a454",
|
||||
"0x9b00a09492a626678e5a3009982191586c444df9",
|
||||
"0xa4d94019934d8333ef880abffbf2fdd611c762bd",
|
||||
"0xa700b4eb416be35b2911fd5dee80678ff64ff6c9",
|
||||
"0xa9251ca9de909cb71783723713b21e4233fbf1b1",
|
||||
"0xaa0200d169ff3ba9385c12e073c5d1d30434ae7b",
|
||||
"0xaa6e91c82942aeae040303bf96c15a6dbcb82ca0",
|
||||
"0xb76cf92076adbf1d9c39294fa8e7a67579fde357",
|
||||
"0xb82fa9f31612989525992fcfbb09ab22eff5c85a",
|
||||
"0xbcffb4b3beadc989bd1458740952af6ec8fbe431",
|
||||
"0xbdb9300b7cde636d9cd4aff00f6f009ffbbc8ee6",
|
||||
"0xbdfa7b7893081b35fb54027489e2bc7a38275129",
|
||||
"0xbdfd4e51d3c14a232135f04988a42576efb31519",
|
||||
"0xbe54767735fb7acca2aa7e2d209a6f705073536d",
|
||||
"0xc45a479877e1e9dfe9fcd4056c699575a1045daa",
|
||||
"0xc7b4c17861357b8abb91f25581e7263e08dcb59c",
|
||||
"0xcc9ee9483f662091a1de4795249e24ac0ac2630f",
|
||||
"0xcca43cef272c30415866914351fdfc3e881bb7c2",
|
||||
"0xcf3d55c10db69f28fd1a75bd73f3d8a2d9c595ad",
|
||||
"0xd4a0e0b9149bcee3c920d2e00b5de09138fd8bb7",
|
||||
"0xd4e245848d6e1220dbe62e155d89fa327e43cb06",
|
||||
"0xdd5745756c2de109183c6b5bb886f9207bef114d",
|
||||
"0xde6ef6cb4abd3a473ffc2942eef5d84536f8e864",
|
||||
"0xe50fa9b3c56ffb159cb0fca61f5c9d750e8128c8",
|
||||
"0xe728577e9a1fe7032bc309b4541f58f45443866e",
|
||||
"0xea1132120ddcdda2f119e99fa7a27a0d036f7ac9",
|
||||
"0xebe517846d0f36eced99c735cbf6131e1feb775d",
|
||||
"0xec4ef66d4fceeba34abb4de69db391bc5476ccc8",
|
||||
"0xf329e36c7bf6e5e86ce2150875a84ce77f477375",
|
||||
"0xf59036caebea7dc4b86638dfa2e3c97da9fccd40",
|
||||
"0xf611aeb5013fd2c0511c9cd55c7dc5c1140741a6",
|
||||
"0xf6d2224916ddfbbab6e6bd0d1b7034f4ae0cab18",
|
||||
"0xfa82580c16a31d0c1bc632a36f82e83efef3eec0",
|
||||
)
|
||||
|
|
@ -7,16 +7,16 @@ fun CryptoCurrency.Token.yieldSupplyKey(): String {
|
|||
}
|
||||
|
||||
fun CryptoCurrencyStatus.hasNotSuppliedAmount(): Boolean {
|
||||
val notSupplied = notSuppliedAmountOrNull() ?: return false
|
||||
val notSupplied = notSuppliedCryptoAmountOrNull() ?: return false
|
||||
return notSupplied > BigDecimal.ZERO
|
||||
}
|
||||
|
||||
fun CryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(dustAmount: BigDecimal): Boolean {
|
||||
val notSupplied = notSuppliedAmountOrNull() ?: return false
|
||||
fun CryptoCurrencyStatus.shouldShowNotSuppliedNotification(dustAmount: BigDecimal): Boolean {
|
||||
val notSupplied = notSuppliedCryptoAmountOrNull()?.multiply(this.value.fiatRate) ?: return false
|
||||
return notSupplied >= dustAmount
|
||||
}
|
||||
|
||||
fun CryptoCurrencyStatus.notSuppliedAmountOrNull(): BigDecimal? {
|
||||
fun CryptoCurrencyStatus.notSuppliedCryptoAmountOrNull(): BigDecimal? {
|
||||
if (this.currency !is CryptoCurrency.Token) return null
|
||||
|
||||
val supplyStatus = this.value.yieldSupplyStatus
|
||||
|
|
|
|||
|
|
@ -124,9 +124,10 @@ data class TxInfo(
|
|||
}
|
||||
|
||||
@Serializable
|
||||
data object Send : YieldSupply {
|
||||
override val address: String? = null
|
||||
}
|
||||
data class Send(
|
||||
override val address: String? = null,
|
||||
val isYieldSupplyWithdraw: Boolean,
|
||||
) : YieldSupply
|
||||
|
||||
@Serializable
|
||||
data class DeployContract(override val address: String) : YieldSupply
|
||||
|
|
|
|||
|
|
@ -0,0 +1,417 @@
|
|||
package com.tangem.domain.models.currency
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import java.math.BigDecimal
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class CryptoCurrencyExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `GIVEN currency is Coin WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Coin>(),
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yieldSupplyStatus is null WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = null,
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yieldSupplyStatus isActive is false WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = false,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN effectiveProtocolBalance is null WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = null,
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN amount is null WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
amount = null,
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN notSupplied is zero WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN notSupplied is negative WHEN hasNotSuppliedAmount THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
amount = BigDecimal.ONE,
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN notSupplied is positive WHEN hasNotSuppliedAmount THEN returns true`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.ONE,
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
)
|
||||
|
||||
val result = status.hasNotSuppliedAmount()
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN currency is Coin WHEN shouldShowNotSuppliedNotification THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Coin>(),
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.ONE)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yieldSupplyStatus is null WHEN shouldShowNotSuppliedNotification THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = null,
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.ONE)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yieldSupplyStatus isActive is false WHEN shouldShowNotSuppliedNotification THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = false,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.ONE)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN effectiveProtocolBalance is null WHEN shouldShowNotSuppliedNotification THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = null,
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
fiatRate = BigDecimal.ONE,
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.ONE)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN notSupplied in fiat is less than dustAmount WHEN shouldShowNotSuppliedNotification THEN returns false`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal("9"),
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
fiatRate = BigDecimal.ONE,
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.TEN)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN notSupplied in fiat equals dustAmount WHEN shouldShowNotSuppliedNotification THEN returns true`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal("5"),
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
fiatRate = BigDecimal("2"),
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.TEN)
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN notSupplied in fiat is greater than dustAmount WHEN shouldShowNotSuppliedNotification THEN returns true`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.ONE,
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
fiatRate = BigDecimal("2"),
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = BigDecimal.TEN)
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN USDT with EUR fiat rate and notSupplied below dust WHEN shouldShowNotSuppliedNotification THEN returns false`() {
|
||||
val usdtToEurRate = BigDecimal("0.93")
|
||||
val notSuppliedUsdt = BigDecimal("0.05")
|
||||
val protocolBalance = BigDecimal("100")
|
||||
val totalAmount = protocolBalance.add(notSuppliedUsdt)
|
||||
val dustAmountEur = BigDecimal("0.1")
|
||||
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = protocolBalance,
|
||||
),
|
||||
amount = totalAmount,
|
||||
fiatRate = usdtToEurRate,
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = dustAmountEur)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN USDT with EUR fiat rate and notSupplied above dust WHEN shouldShowNotSuppliedNotification THEN returns true`() {
|
||||
val usdtToEurRate = BigDecimal("0.93")
|
||||
val notSuppliedUsdt = BigDecimal("0.15")
|
||||
val protocolBalance = BigDecimal("100")
|
||||
val totalAmount = protocolBalance.add(notSuppliedUsdt)
|
||||
val dustAmountEur = BigDecimal("0.1")
|
||||
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = protocolBalance,
|
||||
),
|
||||
amount = totalAmount,
|
||||
fiatRate = usdtToEurRate,
|
||||
)
|
||||
|
||||
val result = status.shouldShowNotSuppliedNotification(dustAmount = dustAmountEur)
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN currency is Coin WHEN notSuppliedCryptoAmountOrNull THEN returns null`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Coin>(),
|
||||
)
|
||||
|
||||
val result = status.notSuppliedCryptoAmountOrNull()
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yieldSupplyStatus is null WHEN notSuppliedCryptoAmountOrNull THEN returns null`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = null,
|
||||
)
|
||||
|
||||
val result = status.notSuppliedCryptoAmountOrNull()
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yieldSupplyStatus isActive is false WHEN notSuppliedCryptoAmountOrNull THEN returns null`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = false,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
)
|
||||
|
||||
val result = status.notSuppliedCryptoAmountOrNull()
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN effectiveProtocolBalance is null WHEN notSuppliedCryptoAmountOrNull THEN returns null`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = null,
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
)
|
||||
|
||||
val result = status.notSuppliedCryptoAmountOrNull()
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN amount is null WHEN notSuppliedCryptoAmountOrNull THEN returns null`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal.TEN,
|
||||
),
|
||||
amount = null,
|
||||
)
|
||||
|
||||
val result = status.notSuppliedCryptoAmountOrNull()
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN valid data WHEN notSuppliedCryptoAmountOrNull THEN returns amount minus protocolBalance`() {
|
||||
val status = createCryptoCurrencyStatus(
|
||||
currency = mockk<CryptoCurrency.Token>(),
|
||||
yieldSupplyStatus = YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = BigDecimal("3"),
|
||||
),
|
||||
amount = BigDecimal.TEN,
|
||||
)
|
||||
|
||||
val result = status.notSuppliedCryptoAmountOrNull()
|
||||
|
||||
assertThat(result).isEqualTo(BigDecimal("7"))
|
||||
}
|
||||
|
||||
private fun createCryptoCurrencyStatus(
|
||||
currency: CryptoCurrency,
|
||||
yieldSupplyStatus: YieldSupplyStatus? = null,
|
||||
amount: BigDecimal? = null,
|
||||
fiatRate: BigDecimal? = null,
|
||||
): CryptoCurrencyStatus {
|
||||
val value = mockk<CryptoCurrencyStatus.Value> {
|
||||
every { this@mockk.yieldSupplyStatus } returns yieldSupplyStatus
|
||||
every { this@mockk.amount } returns amount
|
||||
every { this@mockk.fiatRate } returns fiatRate
|
||||
}
|
||||
return CryptoCurrencyStatus(currency = currency, value = value)
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +65,7 @@ sealed class VisaApiError(
|
|||
data object WithdrawalDataError : VisaApiError(104004003)
|
||||
data object SignWithdrawError : VisaApiError(104004004)
|
||||
data object WithdrawError : VisaApiError(104004005)
|
||||
data object ServerUnavailable : VisaApiError(104004006)
|
||||
|
||||
companion object {
|
||||
fun fromBackendError(backendErrorCode: Int): VisaApiError {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
package com.tangem.domain.yield.supply.usecase
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Use case for getting dust minimum amount for yield supply.
|
||||
*
|
||||
* Returns the minimum amount based on the selected [AppCurrency].
|
||||
* If [AppCurrency] is in [SUPPORTED_DUST_CURRENCIES], returns [DUST_MIN_AMOUNT],
|
||||
* otherwise returns the original [minAmount] with trailing zeros stripped.
|
||||
*/
|
||||
class YieldSupplyGetDustMinAmountUseCase {
|
||||
|
||||
operator fun invoke(minAmount: BigDecimal, appCurrency: AppCurrency): BigDecimal {
|
||||
operator fun invoke(
|
||||
minAmountTokenCurrency: BigDecimal,
|
||||
appCurrency: AppCurrency,
|
||||
tokenCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): BigDecimal {
|
||||
return if (appCurrency.code in SUPPORTED_DUST_CURRENCIES) {
|
||||
DUST_MIN_AMOUNT
|
||||
} else {
|
||||
minAmount.stripTrailingZeros()
|
||||
minAmountTokenCurrency.multiply(tokenCryptoCurrencyStatus.value.fiatRate)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ import com.tangem.domain.yield.supply.fixFee
|
|||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
/**
|
||||
* Use case that calculates the minimum amount required for yield supply operations.
|
||||
*
|
||||
* The calculation is based on the estimated transaction fee converted to the token currency,
|
||||
* with a buffer multiplier applied to account for fee fluctuations.
|
||||
*
|
||||
* @return [BigDecimal] minimum amount in token currency (not native/network currency)
|
||||
*/
|
||||
class YieldSupplyMinAmountUseCase(
|
||||
private val feeRepository: FeeRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ package com.tangem.domain.yield.supply.usecase
|
|||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -13,19 +17,79 @@ class YieldSupplyGetDustMinAmountUseCaseTest {
|
|||
fun `GIVEN supported currency WHEN invoke THEN return dust min amount`() {
|
||||
val minAmount = BigDecimal("123.456")
|
||||
val appCurrency = AppCurrency(code = "EUR", name = "Euro", symbol = "€")
|
||||
val tokenStatus = createTokenStatus(fiatRate = BigDecimal("2.0"))
|
||||
|
||||
val result = useCase(minAmount, appCurrency)
|
||||
val result = useCase(minAmount, appCurrency, tokenStatus)
|
||||
|
||||
assertThat(result).isEqualTo(BigDecimal("0.1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unsupported currency WHEN invoke THEN return min amount stripped`() {
|
||||
val minAmount = BigDecimal("1.2300")
|
||||
fun `GIVEN unsupported currency WHEN invoke THEN return min amount multiplied by fiat rate`() {
|
||||
val minAmount = BigDecimal("1.23")
|
||||
val fiatRate = BigDecimal("150.0")
|
||||
val appCurrency = AppCurrency(code = "JPY", name = "Japanese Yen", symbol = "¥")
|
||||
val tokenStatus = createTokenStatus(fiatRate = fiatRate)
|
||||
|
||||
val result = useCase(minAmount, appCurrency)
|
||||
val result = useCase(minAmount, appCurrency, tokenStatus)
|
||||
|
||||
assertThat(result).isEqualTo(BigDecimal("1.23"))
|
||||
assertThat(result).isEqualTo(minAmount.multiply(fiatRate))
|
||||
}
|
||||
|
||||
private fun createTokenStatus(fiatRate: BigDecimal): CryptoCurrencyStatus {
|
||||
val network = createNetwork()
|
||||
val token = createToken(network)
|
||||
return CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = CryptoCurrencyStatus.Loaded(
|
||||
amount = BigDecimal.ZERO,
|
||||
fiatAmount = BigDecimal.ZERO,
|
||||
fiatRate = fiatRate,
|
||||
priceChange = BigDecimal.ZERO,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = NetworkAddress.Single(
|
||||
NetworkAddress.Address(value = "0xabc", type = NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createNetwork(): Network {
|
||||
val derivationPath = Network.DerivationPath.None
|
||||
return Network(
|
||||
id = Network.ID(Network.RawID("polygon"), derivationPath),
|
||||
backendId = "polygon",
|
||||
name = "Polygon",
|
||||
currencySymbol = "MATIC",
|
||||
derivationPath = derivationPath,
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
hasFiatFeeRate = false,
|
||||
canHandleTokens = true,
|
||||
transactionExtrasType = Network.TransactionExtrasType.NONE,
|
||||
nameResolvingType = Network.NameResolvingType.ENS,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createToken(network: Network): CryptoCurrency.Token {
|
||||
val tokenId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId(network.rawId),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("test-token", "0xContract"),
|
||||
)
|
||||
return CryptoCurrency.Token(
|
||||
id = tokenId,
|
||||
network = network,
|
||||
name = "Test Token",
|
||||
symbol = "TT",
|
||||
decimals = 18,
|
||||
iconUrl = null,
|
||||
isCustom = false,
|
||||
contractAddress = "0xContract",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ internal fun getRewardScheduleText(
|
|||
-> getCustomRewardSchedule(
|
||||
networkId = networkId,
|
||||
decapitalize = decapitalize,
|
||||
)
|
||||
) ?: stringReference(rewardSchedule.name.lowercase().capitalize())
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.TokenReceiveType
|
||||
import com.tangem.domain.models.TokenReceiveNotification
|
||||
import com.tangem.domain.models.TokenReceiveType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.ens.EnsAddress
|
||||
import com.tangem.features.tokenreceive.entity.ReceiveAddress.Type.Ens
|
||||
|
|
@ -111,7 +108,10 @@ internal class TokenReceiveStateFactory(
|
|||
}
|
||||
ReceiveAddressModel.NameService.Ens -> Ens
|
||||
ReceiveAddressModel.NameService.Legacy -> Primary.Legacy(
|
||||
displayName = TextReference.Res(R.string.domain_receive_assets_legacy_address),
|
||||
displayName = resourceReference(
|
||||
R.string.domain_receive_assets_legacy_address,
|
||||
WrappedList(listOf(cryptoCurrency.name)),
|
||||
),
|
||||
)
|
||||
}
|
||||
ReceiveAddress(
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ internal class TxHistoryItemToTransactionStateConverter(
|
|||
is TransactionType.Swap,
|
||||
is TransactionType.Transfer,
|
||||
is TransactionType.UnknownOperation,
|
||||
TransactionType.YieldSupply.Send,
|
||||
is TransactionType.YieldSupply.Send,
|
||||
TransactionType.YieldSupply.Topup,
|
||||
-> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24
|
||||
}
|
||||
|
|
@ -83,10 +83,12 @@ internal class TxHistoryItemToTransactionStateConverter(
|
|||
is TransactionType.YieldSupply.Enter -> resourceReference(R.string.yield_module_transaction_enter)
|
||||
is TransactionType.YieldSupply.Exit -> resourceReference(R.string.yield_module_transaction_exit)
|
||||
TransactionType.YieldSupply.Topup -> resourceReference(R.string.yield_module_transaction_topup)
|
||||
TransactionType.YieldSupply.Send -> if (isOutgoing) {
|
||||
resourceReference(R.string.common_transfer)
|
||||
} else {
|
||||
resourceReference(R.string.yield_module_transaction_withdraw)
|
||||
is TransactionType.YieldSupply.Send -> {
|
||||
if (type.isYieldSupplyWithdraw || isOutgoing) {
|
||||
resourceReference(R.string.yield_module_transaction_withdraw)
|
||||
} else {
|
||||
resourceReference(R.string.common_transfer)
|
||||
}
|
||||
}
|
||||
is TransactionType.YieldSupply.DeployContract -> resourceReference(
|
||||
R.string
|
||||
|
|
@ -107,7 +109,7 @@ internal class TxHistoryItemToTransactionStateConverter(
|
|||
private fun TxInfo.extractSubtitle(): TextReference {
|
||||
return when (val type = this.type) {
|
||||
is TransactionType.YieldSupply -> if (currency is CryptoCurrency.Coin) {
|
||||
if (type == TransactionType.YieldSupply.Send) {
|
||||
if (type is TransactionType.YieldSupply.Send) {
|
||||
extractSubtitleByAddressType()
|
||||
} else {
|
||||
resourceReference(
|
||||
|
|
@ -129,8 +131,8 @@ internal class TxHistoryItemToTransactionStateConverter(
|
|||
val amount = amount.format { crypto(symbol = currency.symbol, decimals = currency.decimals) }
|
||||
resourceReference(R.string.yield_module_transaction_exit_subtitle, wrappedList(amount))
|
||||
}
|
||||
TransactionType.YieldSupply.Send -> {
|
||||
if (isOutgoing) {
|
||||
is TransactionType.YieldSupply.Send -> {
|
||||
if (isOutgoing || !type.isYieldSupplyWithdraw) {
|
||||
extractSubtitleByAddressType()
|
||||
} else {
|
||||
val amount =
|
||||
|
|
@ -190,12 +192,9 @@ internal class TxHistoryItemToTransactionStateConverter(
|
|||
TransactionType.Staking.Withdraw,
|
||||
-> return ""
|
||||
|
||||
is TransactionType.YieldSupply -> if (currency is CryptoCurrency.Token) {
|
||||
when (type) {
|
||||
TransactionType.YieldSupply.Send -> if (!isOutgoing) {
|
||||
return ""
|
||||
}
|
||||
else -> return ""
|
||||
is TransactionType.YieldSupply -> {
|
||||
if (currency is CryptoCurrency.Token && type == TransactionType.YieldSupply.Send && !isOutgoing) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ internal class TxHistoryItemStateConverter(
|
|||
is TransactionType.Swap,
|
||||
is TransactionType.Transfer,
|
||||
is TransactionType.UnknownOperation,
|
||||
TransactionType.YieldSupply.Send,
|
||||
is TransactionType.YieldSupply.Send,
|
||||
TransactionType.YieldSupply.Topup,
|
||||
-> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24
|
||||
}
|
||||
|
|
@ -93,10 +93,12 @@ internal class TxHistoryItemStateConverter(
|
|||
is TransactionType.YieldSupply.Enter -> resourceReference(R.string.yield_module_transaction_enter)
|
||||
is TransactionType.YieldSupply.Exit -> resourceReference(R.string.yield_module_transaction_exit)
|
||||
TransactionType.YieldSupply.Topup -> resourceReference(R.string.yield_module_transaction_topup)
|
||||
TransactionType.YieldSupply.Send -> if (isOutgoing) {
|
||||
resourceReference(R.string.common_transfer)
|
||||
} else {
|
||||
resourceReference(R.string.yield_module_transaction_withdraw)
|
||||
is TransactionType.YieldSupply.Send -> {
|
||||
if (type.isYieldSupplyWithdraw || isOutgoing) {
|
||||
resourceReference(R.string.yield_module_transaction_withdraw)
|
||||
} else {
|
||||
resourceReference(R.string.common_transfer)
|
||||
}
|
||||
}
|
||||
is TransactionType.YieldSupply.DeployContract -> resourceReference(
|
||||
R.string
|
||||
|
|
@ -137,8 +139,8 @@ internal class TxHistoryItemStateConverter(
|
|||
wrappedList(amount),
|
||||
)
|
||||
}
|
||||
TransactionType.YieldSupply.Send -> {
|
||||
if (isOutgoing) {
|
||||
is TransactionType.YieldSupply.Send -> {
|
||||
if (isOutgoing || !type.isYieldSupplyWithdraw) {
|
||||
extractSubtitleByAddressType()
|
||||
} else {
|
||||
val amount =
|
||||
|
|
|
|||
|
|
@ -20,16 +20,15 @@ internal class YieldSupplyPromoBannerKeyConverter(
|
|||
is TokenConverterParams.Wallet -> value.tokenList.flattenCurrencies()
|
||||
is TokenConverterParams.Account -> value.accountList.flattenCurrencies()
|
||||
}.filter { status ->
|
||||
status.value is CryptoCurrencyStatus.Loaded ||
|
||||
status.value is CryptoCurrencyStatus.Custom
|
||||
status.value is CryptoCurrencyStatus.Loaded
|
||||
}
|
||||
|
||||
val tokens = currencies.filter { it.currency is CryptoCurrency.Token }
|
||||
val cryptoCurrencyStatuses = currencies.filter { it.currency is CryptoCurrency.Token }
|
||||
|
||||
if (tokens.any { it.value.yieldSupplyStatus?.isActive == true }) return null
|
||||
if (cryptoCurrencyStatuses.any { it.value.yieldSupplyStatus?.isActive == true }) return null
|
||||
if (yieldModuleApyMap.isEmpty()) return null
|
||||
|
||||
val max = tokens.asSequence()
|
||||
val max = cryptoCurrencyStatuses.asSequence()
|
||||
.mapNotNull { status ->
|
||||
val token = status.currency as? CryptoCurrency.Token ?: return@mapNotNull null
|
||||
val tokenKey = token.yieldSupplyKey()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
@Test
|
||||
fun `GIVEN promo disabled WHEN convert THEN return null`() {
|
||||
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xABCDEF")
|
||||
val status = createStatus(token = token, amount = BigDecimal.ONE, isYieldActive = false)
|
||||
val status = createLoadedStatus(token = token, amount = BigDecimal.ONE, isYieldActive = false)
|
||||
val tokenList = ungroupedTokenList(status)
|
||||
val params = TokenConverterParams.Wallet(
|
||||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
|
|
@ -39,7 +39,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
@Test
|
||||
fun `GIVEN empty apy map WHEN convert THEN return null`() {
|
||||
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xA1")
|
||||
val status = createStatus(token = token, amount = BigDecimal("2.0"), isYieldActive = false)
|
||||
val status = createLoadedStatus(token = token, amount = BigDecimal("2.0"), isYieldActive = false)
|
||||
val params = TokenConverterParams.Wallet(
|
||||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(status),
|
||||
|
|
@ -57,7 +57,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
@Test
|
||||
fun `GIVEN active yield token present WHEN convert THEN return null`() {
|
||||
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xAA")
|
||||
val statusActive = createStatus(token = token, amount = BigDecimal("5"), isYieldActive = true)
|
||||
val statusActive = createLoadedStatus(token = token, amount = BigDecimal("5"), isYieldActive = true)
|
||||
val params = TokenConverterParams.Wallet(
|
||||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(statusActive),
|
||||
|
|
@ -78,8 +78,8 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
val tokenSmall = createToken(networkId = evmNetworkId, backendId = evmNetworkId, contract = "0xAbCd")
|
||||
val tokenBig = createToken(networkId = evmNetworkId, backendId = evmNetworkId, contract = "0xBEEF")
|
||||
|
||||
val statusSmall = createStatus(token = tokenSmall, amount = BigDecimal("1.00"), isYieldActive = false)
|
||||
val statusBig = createStatus(token = tokenBig, amount = BigDecimal("10.00"), isYieldActive = false)
|
||||
val statusSmall = createLoadedStatus(token = tokenSmall, amount = BigDecimal("1.00"), isYieldActive = false)
|
||||
val statusBig = createLoadedStatus(token = tokenBig, amount = BigDecimal("10.00"), isYieldActive = false)
|
||||
|
||||
val apyMap = mapOf(
|
||||
"${tokenSmall.network.backendId}_${tokenSmall.contractAddress.lowercase()}" to BigDecimal("0.05"),
|
||||
|
|
@ -105,7 +105,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
fun `GIVEN non evm case sensitive mismatch WHEN convert THEN return null`() {
|
||||
val nonEvmId = "xrp"
|
||||
val token = createToken(networkId = nonEvmId, backendId = nonEvmId, contract = "rAbC123")
|
||||
val status = createStatus(token = token, amount = BigDecimal("3"), isYieldActive = false)
|
||||
val status = createLoadedStatus(token = token, amount = BigDecimal("3"), isYieldActive = false)
|
||||
|
||||
val mismatchedKey = "${token.network.backendId}_${token.contractAddress.lowercase()}"
|
||||
val apyMap = mapOf(mismatchedKey to BigDecimal("0.07"))
|
||||
|
|
@ -124,6 +124,24 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN custom status WHEN convert THEN return null`() {
|
||||
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xCUSTOM")
|
||||
val status = createCustomStatus(token = token, amount = BigDecimal("5.0"), isYieldActive = false)
|
||||
val params = TokenConverterParams.Wallet(
|
||||
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
|
||||
tokenList = ungroupedTokenList(status),
|
||||
)
|
||||
val converter = YieldSupplyPromoBannerKeyConverter(
|
||||
yieldModuleApyMap = mapOf(token.yieldSupplyKey() to BigDecimal("0.10")),
|
||||
shouldShowMainPromo = true,
|
||||
)
|
||||
|
||||
val result = converter.convert(params)
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
private fun ungroupedTokenList(vararg statuses: CryptoCurrencyStatus): TokenList.Ungrouped {
|
||||
return TokenList.Ungrouped(
|
||||
totalFiatBalance = com.tangem.domain.models.TotalFiatBalance.Loaded(
|
||||
|
|
@ -135,7 +153,45 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
)
|
||||
}
|
||||
|
||||
private fun createStatus(
|
||||
private fun createLoadedStatus(
|
||||
token: CryptoCurrency.Token,
|
||||
amount: BigDecimal,
|
||||
isYieldActive: Boolean,
|
||||
): CryptoCurrencyStatus {
|
||||
val networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(
|
||||
value = "addr",
|
||||
type = NetworkAddress.Address.Type.Primary,
|
||||
),
|
||||
)
|
||||
val value = CryptoCurrencyStatus.Loaded(
|
||||
amount = amount,
|
||||
fiatAmount = BigDecimal.ZERO,
|
||||
fiatRate = BigDecimal.ONE,
|
||||
priceChange = BigDecimal.ZERO,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = if (isYieldActive) {
|
||||
YieldSupplyStatus(
|
||||
isActive = true,
|
||||
isInitialized = true,
|
||||
isAllowedToSpend = true,
|
||||
effectiveProtocolBalance = null,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
)
|
||||
return CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCustomStatus(
|
||||
token: CryptoCurrency.Token,
|
||||
amount: BigDecimal,
|
||||
isYieldActive: Boolean,
|
||||
|
|
|
|||
|
|
@ -242,7 +242,11 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
userWalletId,
|
||||
cryptoCurrencyStatusFlow.value,
|
||||
).onRight { minAmount ->
|
||||
val dustAmount = yieldSupplyGetDustMinAmountUseCase(minAmount = minAmount, appCurrency = appCurrency)
|
||||
val dustAmount = yieldSupplyGetDustMinAmountUseCase(
|
||||
minAmountTokenCurrency = minAmount,
|
||||
appCurrency = appCurrency,
|
||||
tokenCryptoCurrencyStatus = cryptoCurrencyStatusFlow.value,
|
||||
)
|
||||
uiState.update(
|
||||
YieldSupplyActiveMinAmountTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ 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.domain.models.currency.notSuppliedAmountOrNull
|
||||
import com.tangem.domain.models.currency.shouldShowNotSuppliedInfoIcon
|
||||
import com.tangem.domain.models.currency.notSuppliedCryptoAmountOrNull
|
||||
import com.tangem.domain.models.currency.shouldShowNotSuppliedNotification
|
||||
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.active.entity.YieldSupplyActiveContentUM
|
||||
|
|
@ -91,11 +91,11 @@ internal class YieldSupplyActiveMinAmountTransformer(
|
|||
}
|
||||
|
||||
private fun getNotSuppliedNotification(cryptoCurrencyStatus: CryptoCurrencyStatus): NotificationUM? {
|
||||
return if (cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(dustMinAmount)) {
|
||||
return if (cryptoCurrencyStatus.shouldShowNotSuppliedNotification(dustMinAmount)) {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val notDepositedAmount = cryptoCurrencyStatus.notSuppliedAmountOrNull()
|
||||
val notSuppliedAmount = cryptoCurrencyStatus.notSuppliedCryptoAmountOrNull()
|
||||
val formattedAmount =
|
||||
notDepositedAmount.format { crypto(symbol = "", decimals = cryptoCurrencyStatus.currency.decimals) }
|
||||
notSuppliedAmount.format { crypto(symbol = "", decimals = cryptoCurrencyStatus.currency.decimals) }
|
||||
analyticsHandler.send(
|
||||
YieldSupplyAnalytics.NoticeAmountNotDeposited(
|
||||
token = cryptoCurrency.symbol,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ 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.hasNotSuppliedAmount
|
||||
import com.tangem.domain.models.currency.shouldShowNotSuppliedInfoIcon
|
||||
import com.tangem.domain.models.currency.shouldShowNotSuppliedNotification
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||
|
|
@ -347,10 +347,11 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
.getOrNull()
|
||||
if (minAmount != null) {
|
||||
val dustAmount = yieldSupplyGetDustMinAmountUseCase(
|
||||
minAmount = minAmount,
|
||||
minAmountTokenCurrency = minAmount,
|
||||
appCurrency = appCurrency,
|
||||
tokenCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(dustAmount)
|
||||
cryptoCurrencyStatus.shouldShowNotSuppliedNotification(dustAmount)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue