Updated on 2026-08-14
This commit is contained in:
commit
ee56b2f716
46 changed files with 492 additions and 233 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 328c13826f7e52ba783db1ba39de5b17b1b79bc5
|
||||
Subproject commit d52c8382e9e94b2b44ceecd7cb7bd897e091073c
|
||||
|
|
@ -152,8 +152,8 @@ internal object StakingDomainModule {
|
|||
fun provideGetPendingTransactionsUseCase(
|
||||
stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetPendingTransactionsUseCase {
|
||||
return GetPendingTransactionsUseCase(
|
||||
): GetStakingPendingTransactionsUseCase {
|
||||
return GetStakingPendingTransactionsUseCase(
|
||||
stakingPendingTransactionRepository = stakingPendingTransactionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
<string name="common_no_address">Keine Adresse</string>
|
||||
<string name="common_now">Jetzt</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">Primär Karte</string>
|
||||
<string name="common_origin_card">Hauptkarte</string>
|
||||
<string name="common_passphrase">Passphrase</string>
|
||||
<string name="common_paste">Einfügen</string>
|
||||
<string name="common_privacy_policy">Datenschutzbestimmungen</string>
|
||||
|
|
@ -275,7 +275,7 @@
|
|||
<string name="express_token_list_empty_search">Keine Token gefunden. Bitte versuche eine andere Anfrage</string>
|
||||
<string name="express_transaction_id">ID: %s</string>
|
||||
<string name="express_transaction_id_copied">Transaktions-ID kopiert</string>
|
||||
<string name="exсhange_token_description">Wechsel eine Ihrer Währungen in eine andere</string>
|
||||
<string name="exсhange_token_description">Tauschen jeden Vermögenswert in Deinem Portfolio gegen diesen Token</string>
|
||||
<string name="feedback_data_collection_message">Die folgenden Angaben sind freiwillig. Du kannst diese löschen, wenn du sie nicht weitergeben möchtest.</string>
|
||||
<string name="feedback_preface_rate_negative">Teile uns mit, welche Funktionen du vermisst, und wir werden versuchen, dir zu helfen.</string>
|
||||
<string name="feedback_preface_scan_failed">Bitte sag uns, welche Karte du hast</string>
|
||||
|
|
@ -345,6 +345,7 @@
|
|||
<string name="manage_tokens_wallet_support_only_one_network_title">Das Wallet unterstützt nicht mehr als ein Netzwerk</string>
|
||||
<string name="markets_add_to_my_portfolio_description">Um mit dem Kauf, Tausch oder Erhalt dieses Vermögenswerts zu beginnen, füge diesen Token zu mindestens 1 Netzwerk hinzu</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_description">Dieses Asset ist nicht verfügbar</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">Dieses Asset ist für dieses Wallet nicht verfügbar</string>
|
||||
<string name="markets_add_to_portfolio_button">Zum Portfolio hinzufügen</string>
|
||||
<string name="markets_add_token">Hinzufügen</string>
|
||||
<string name="markets_available_networks">Verfügbare Netzwerke</string>
|
||||
|
|
@ -561,7 +562,7 @@
|
|||
<item quantity="one">%d Wallet</item>
|
||||
<item quantity="other">%d Wallets</item>
|
||||
</plurals>
|
||||
<string name="reset_card_to_factory_button_title">Zurücksetzen der Karte</string>
|
||||
<string name="reset_card_to_factory_button_title">Karte zurücksetzen</string>
|
||||
<string name="reset_card_to_factory_condition_1">Mir ist bewusst, dass ich nach der Durchführung dieser Aktion keinen Zugriff mehr auf die aktuelle Wallet habe.</string>
|
||||
<string name="reset_card_to_factory_condition_2">Mir ist klar, dass ich diese Karte nicht verwenden kann, um meinen Zugangscode auf den anderen Karten der aktuellen Wallet wiederherzustellen</string>
|
||||
<string name="reset_card_with_backup_to_factory_message">Durch das Zurücksetzen auf Werkseinstellungen wird die Wallet vollständig von der ausgewählten Karte gelöscht. Du kannst die aktuelle Wallet nicht wiederherstellen oder die Karte verwenden, um den Zugangscode wiederherzustellen.</string>
|
||||
|
|
@ -686,7 +687,7 @@
|
|||
<string name="staking_details_unbonding_period_info">Der Zeitraum, den du nach der Beantragung der Abhebung von Geldern aus dem Staking warten musst, bevor die Token verfügbar werden.</string>
|
||||
<string name="staking_details_warmup_period">Aufwärmphase</string>
|
||||
<string name="staking_details_warmup_period_info">Die zugewiesene Zeit für die Aktivierung der Teilnahme am Staking.</string>
|
||||
<string name="staking_legal">Indem Du die Staking-Funktionalität nutzen, stimmst Du den %1$s und %2$s des Anbieters zu.</string>
|
||||
<string name="staking_legal">Indem Du die Staking-Funktionalität nutzt, stimmst Du den %1$s und %2$s des Anbieters zu.</string>
|
||||
<string name="staking_locked">Gesperrt</string>
|
||||
<string name="staking_migrate">Migrieren</string>
|
||||
<string name="staking_native">Natives Staking</string>
|
||||
|
|
@ -718,14 +719,14 @@
|
|||
<string name="staking_revote">Neuwahl</string>
|
||||
<string name="staking_reward_claiming_auto">Automatisch</string>
|
||||
<string name="staking_reward_claiming_manual">Händisch</string>
|
||||
<string name="staking_reward_schedule_block">Block</string>
|
||||
<string name="staking_reward_schedule_day">Tag</string>
|
||||
<string name="staking_reward_schedule_block">Pro Block</string>
|
||||
<string name="staking_reward_schedule_day">Täglich</string>
|
||||
<string name="staking_reward_schedule_each_day">Täglich</string>
|
||||
<string name="staking_reward_schedule_epoch">Epoche</string>
|
||||
<string name="staking_reward_schedule_era">Ära</string>
|
||||
<string name="staking_reward_schedule_hour">Stunde</string>
|
||||
<string name="staking_reward_schedule_month">Monat</string>
|
||||
<string name="staking_reward_schedule_week">Woche</string>
|
||||
<string name="staking_reward_schedule_epoch">Pro Epoche</string>
|
||||
<string name="staking_reward_schedule_era">Pro Ära</string>
|
||||
<string name="staking_reward_schedule_hour">Stündlich</string>
|
||||
<string name="staking_reward_schedule_month">Monatlich</string>
|
||||
<string name="staking_reward_schedule_week">Wöchentlich</string>
|
||||
<string name="staking_rewards">Belohnungen</string>
|
||||
<string name="staking_stake_locked">Stake gesperrt</string>
|
||||
<string name="staking_stake_more">Mehr staken</string>
|
||||
|
|
|
|||
|
|
@ -342,6 +342,7 @@
|
|||
<string name="manage_tokens_wallet_support_only_one_network_title">ウォレットは複数のネットワークをサポートしていません。</string>
|
||||
<string name="markets_add_to_my_portfolio_description">このアセットの購入、交換、受け取りを開始するには、ポートフォリオに追加してください。</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_description">このアセットは利用できません</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">このアセットはこのウォレットでは使用できません。</string>
|
||||
<string name="markets_add_to_portfolio_button">ポートフォリオに追加</string>
|
||||
<string name="markets_add_token">追加</string>
|
||||
<string name="markets_available_networks">利用可能なネットワーク</string>
|
||||
|
|
@ -799,6 +800,7 @@
|
|||
<string name="transaction_history_operation">オペレーション</string>
|
||||
<string name="transaction_history_transaction_from_address">送金元: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">送金先: %s</string>
|
||||
<string name="transaction_history_transaction_validator">バリデーター: %s</string>
|
||||
<string name="try_to_load_data_again_button_title">もう一度やり直してください</string>
|
||||
<string name="twin_error_same_card">同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。</string>
|
||||
<string name="twin_error_wrong_twin">間違ったツインカードをスキャンしました。別のカードをお試しください。</string>
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@
|
|||
<string name="markets_token_details_trading_volume">Объем торгов (24ч)</string>
|
||||
<string name="markets_token_details_trading_volume_24h_description">Общая сумма криптовалюты, которая была продана за последние 24 часа, показывающая уровень активности и ликвидности на рынке.</string>
|
||||
<string name="markets_token_details_trading_volume_full">Объем торгов (24ч)</string>
|
||||
<string name="markets_tooltip_message">Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из маркета</string>
|
||||
<string name="markets_tooltip_message">Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка</string>
|
||||
<string name="markets_tooltip_title">Добавить токены</string>
|
||||
<string name="nfc_error_unavailable">Функция NFC недоступна на вашем устройстве</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
|
||||
|
|
@ -744,6 +744,7 @@
|
|||
<string name="staking_stake_more">Застейкать еще</string>
|
||||
<string name="staking_summary_description_text">Вы стейкаете %1$s и будете получать %2$s ежегодно</string>
|
||||
<string name="staking_tap_to_unlock">Нажмите для разблокировки</string>
|
||||
<string name="staking_tap_to_unlock_or_vote">Нажмите для разблокировки или голосования</string>
|
||||
<string name="staking_tap_to_withdraw">Нажмите для вывода</string>
|
||||
<string name="staking_title_stake">Застейкать %s</string>
|
||||
<string name="staking_title_unstake">Вывести %s</string>
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@
|
|||
<string name="common_read_more">Детальніше</string>
|
||||
<string name="common_receive">Отримати</string>
|
||||
<string name="common_reject">Відхилити</string>
|
||||
<string name="common_reload">Перезавантажити</string>
|
||||
<string name="common_reload">Оновити</string>
|
||||
<string name="common_rename">Перейменувати</string>
|
||||
<string name="common_save">Зберегти</string>
|
||||
<string name="common_save_changes">Зберегти зміни</string>
|
||||
|
|
@ -351,6 +351,7 @@
|
|||
<string name="manage_tokens_wallet_support_only_one_network_title">Гаманець не підтримує більше однієї мережі</string>
|
||||
<string name="markets_add_to_my_portfolio_description">Щоб почати купувати, обмінювати або отримувати цей актив, додайте цей токен принаймні в 1 мережу</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_description">Цей актив недоступний</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">Цей токен недоступний для цього гаманця</string>
|
||||
<string name="markets_add_to_portfolio_button">Додати в портфоліо</string>
|
||||
<string name="markets_add_token">Додати</string>
|
||||
<string name="markets_available_networks">Доступні мережі</string>
|
||||
|
|
@ -401,6 +402,7 @@
|
|||
<string name="markets_token_details_experienced_buyers_full">Досвідчені покупці</string>
|
||||
<string name="markets_token_details_fully_diluted_valuation">Повн. розведена кап.</string>
|
||||
<string name="markets_token_details_fully_diluted_valuation_description">Загальна теоретична вартість криптовалюти, якщо всі монети, які могли б існувати, перебувають в обігу, включаючи ті, що не перебувають в обігу в даний час</string>
|
||||
<string name="markets_token_details_fully_diluted_valuation_full">Повністю розведена капіталізація</string>
|
||||
<string name="markets_token_details_genesis_date">Дата створення</string>
|
||||
<string name="markets_token_details_high">Високий</string>
|
||||
<string name="markets_token_details_holders">Тримачі</string>
|
||||
|
|
@ -415,9 +417,11 @@
|
|||
<string name="markets_token_details_low">Низький</string>
|
||||
<string name="markets_token_details_market_capitalization">Рин. кап.</string>
|
||||
<string name="markets_token_details_market_capitalization_description">Загальна ринкова вартість криптовалюти, що розраховується шляхом множення поточної ціни монети на загальну кількість монет в обігу</string>
|
||||
<string name="markets_token_details_market_capitalization_full">Ринкова капіталізація</string>
|
||||
<string name="markets_token_details_market_rating">Рейтинг</string>
|
||||
<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_description">Максимальна кількість монет або токенів, яка може коли-небудь існувати для певної криптовалюти</string>
|
||||
<string name="markets_token_details_max_supply_full">Максимальна пропозиція</string>
|
||||
<string name="markets_token_details_metrics">Метрики</string>
|
||||
|
|
@ -713,6 +717,7 @@
|
|||
<string name="staking_notification_earn_rewards_text_period_week">Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щотиждня.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Стейкайте безпечно та почніть отримувати винагороди щотижня</string>
|
||||
<string name="staking_notification_earn_rewards_title">Отримуйте винагороду за стейкінг</string>
|
||||
<string name="staking_notification_network_error_text">Стейкінг тимчасово недоступний через проблеми в мережі. Будь ласка, спробуйте пізніше.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Стейкінг в мережі %1$s з новим валідатором автоматично переведе всі раніше застейкані кошти до цього валідатора</string>
|
||||
<string name="staking_notification_restake_rewards_text">Реінвестуйте зароблені винагороди у суму стейкінгу, щоб збільшити потенційний прибуток.</string>
|
||||
<string name="staking_notification_unlock_text">Розблокуйте свої кошти, щоб вивести їх зі стейкінгу. Розблокування займе %s.</string>
|
||||
|
|
@ -813,12 +818,13 @@
|
|||
<string name="token_swap_promotion_button">Обміняти</string>
|
||||
<string name="transaction_history_contract_address">контракт: %s</string>
|
||||
<string name="transaction_history_empty_transactions">У вас ще немає транзакцій</string>
|
||||
<string name="transaction_history_error_failed_to_load">Не вдалося завантажити історію транзакцій.\nНатисніть кнопку перезавантаження, щоб оновити інформацію.</string>
|
||||
<string name="transaction_history_error_failed_to_load">Не вдалося завантажити історію транзакцій.\nНатисніть кнопку нижче, щоб оновити інформацію</string>
|
||||
<string name="transaction_history_multiple_addresses">Кілька адрес</string>
|
||||
<string name="transaction_history_not_supported_description">Історія транзакцій наразі не підтримується для цього блокчейну. Але не хвилюйтеся, ми працюємо над цим! А поки можете перевірити це в оглядачі.</string>
|
||||
<string name="transaction_history_operation">Операція</string>
|
||||
<string name="transaction_history_transaction_from_address">від: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">до: %s</string>
|
||||
<string name="transaction_history_transaction_validator">валідатор: %s</string>
|
||||
<string name="try_to_load_data_again_button_title">Спробуйте знову</string>
|
||||
<string name="twin_error_same_card">Ви відсканували одну й ту саму картку. Для створення twin-гаманця вам потрібно відсканувати картку з номером %d</string>
|
||||
<string name="twin_error_wrong_twin">Ви відсканували не ту twin-картку. Будь ласка, спробуйте відсканувати іншу</string>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
<string name="common_no_address">No address</string>
|
||||
<string name="common_now">Now</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">Primary Card</string>
|
||||
<string name="common_origin_card">Primary card</string>
|
||||
<string name="common_passphrase">Passphrase</string>
|
||||
<string name="common_paste">Paste</string>
|
||||
<string name="common_privacy_policy">Privacy Policy</string>
|
||||
|
|
@ -561,7 +561,7 @@
|
|||
<item quantity="one">%d wallet</item>
|
||||
<item quantity="other">%d wallets</item>
|
||||
</plurals>
|
||||
<string name="reset_card_to_factory_button_title">Reset the Card</string>
|
||||
<string name="reset_card_to_factory_button_title">Reset the card</string>
|
||||
<string name="reset_card_to_factory_condition_1">I understand that after performing this action, I will no longer have access to the current wallet</string>
|
||||
<string name="reset_card_to_factory_condition_2">I realize that I can\'t use this card to recover my access code on the other cards of the current wallet</string>
|
||||
<string name="reset_card_with_backup_to_factory_message">Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code.</string>
|
||||
|
|
@ -577,7 +577,7 @@
|
|||
<string name="save_user_wallet_agreement_enroll_biometrics_title">Enable biometric authorization</string>
|
||||
<string name="save_user_wallet_agreement_header_biometrics">Would you like to use biometrics?</string>
|
||||
<string name="save_user_wallet_agreement_notice">Note that making a transaction with your funds will still require your card</string>
|
||||
<string name="scan_card_settings_button">Scan Card</string>
|
||||
<string name="scan_card_settings_button">Scan card</string>
|
||||
<string name="scan_card_settings_message">Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet.</string>
|
||||
<string name="scan_card_settings_title">Get your card ready!</string>
|
||||
<string name="send_additional_field_already_included">Already included in the entered address</string>
|
||||
|
|
@ -731,6 +731,7 @@
|
|||
<string name="staking_stake_more">Stake more</string>
|
||||
<string name="staking_summary_description_text">You stake %1$s and will be receiving %2$s yearly</string>
|
||||
<string name="staking_tap_to_unlock">Tap to unlock</string>
|
||||
<string name="staking_tap_to_unlock_or_vote">Tap to unlock or vote</string>
|
||||
<string name="staking_tap_to_withdraw">Tap to withdraw</string>
|
||||
<string name="staking_title_stake">Stake %s</string>
|
||||
<string name="staking_title_unstake">Unstake %s</string>
|
||||
|
|
@ -831,7 +832,7 @@
|
|||
<string name="user_wallet_list_error_wallet_already_saved">This wallet has already been saved, you can add another one</string>
|
||||
<string name="user_wallet_list_rename_popup_error_already_exists">The wallet with name %s already exists</string>
|
||||
<string name="user_wallet_list_rename_popup_placeholder">Wallet name</string>
|
||||
<string name="user_wallet_list_rename_popup_title">Rename Wallet</string>
|
||||
<string name="user_wallet_list_rename_popup_title">Rename wallet</string>
|
||||
<string name="user_wallet_list_unlock_all">Unlock all</string>
|
||||
<string name="user_wallet_list_unlock_all_with">Unlock all with %s</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Blockchain is unreachable. Try later</string>
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"cyber", "cyber/test" -> R.drawable.img_cyber_22
|
||||
"sei", "sei/test" -> R.drawable.img_sei_22
|
||||
"internet-computer" -> R.drawable.img_icp_22
|
||||
"sui", "sui/test" -> R.drawable.img_sui_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -151,6 +152,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int {
|
|||
"cyber", "cyber/test" -> R.drawable.img_cyber_22
|
||||
"sei", "sei/test" -> R.drawable.img_sei_22
|
||||
"internet-computer" -> R.drawable.img_icp_22
|
||||
"sui", "sui/test" -> R.drawable.img_sui_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +226,7 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"cyber", "cyber/test" -> R.drawable.img_cyber_22
|
||||
"sei", "sei/test" -> R.drawable.img_sei_22
|
||||
"internet-computer" -> R.drawable.img_icp_22
|
||||
"sui", "sui/test" -> R.drawable.img_sui_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -300,6 +303,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"cyber", "cyber/test" -> R.drawable.ic_cyber_22
|
||||
"sei", "sei/test" -> R.drawable.ic_sei_22
|
||||
"internet-computer" -> R.drawable.ic_icp_22
|
||||
"sui", "sui/test" -> R.drawable.ic_sui_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -376,6 +380,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int {
|
|||
"cyber", "cyber/test" -> R.drawable.ic_cyber_22
|
||||
"sei", "sei/test" -> R.drawable.ic_sei_22
|
||||
"internet-computer" -> R.drawable.ic_icp_22
|
||||
"sui", "sui/test" -> R.drawable.ic_sui_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
9
core/ui/src/main/res/drawable/ic_sui_22.xml
Normal file
9
core/ui/src/main/res/drawable/ic_sui_22.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M14.8,9.7C15.4,10.5 15.8,11.5 15.8,12.7C15.8,13.8 15.4,14.9 14.7,15.7L14.7,15.8L14.7,15.7C14.6,15.6 14.6,15.5 14.6,15.4C14.3,14 13.2,12.7 11.4,11.7C10.2,11 9.5,10.1 9.3,9.2C9.2,8.6 9.3,8 9.4,7.5C9.6,6.9 9.9,6.5 10.1,6.2L10.8,5.4C10.9,5.2 11.1,5.2 11.2,5.4L14.8,9.7H14.8ZM15.9,8.8L11.2,3.1C11.1,3 10.9,3 10.8,3.1L6.1,8.8L6.1,8.8C5.3,9.9 4.8,11.3 4.8,12.8C4.8,16.2 7.6,19 11,19C14.5,19 17.3,16.2 17.3,12.8C17.3,11.3 16.7,9.9 15.9,8.8L15.9,8.8L15.9,8.8ZM7.3,9.7L7.7,9.1L7.7,9.2C7.7,9.3 7.7,9.4 7.7,9.5C8,10.9 9,12.1 10.6,13C12,13.8 12.8,14.7 13.1,15.7C13.2,16.2 13.2,16.6 13.1,16.9L13.1,17L13.1,17C12.5,17.3 11.8,17.5 11,17.5C8.4,17.5 6.2,15.3 6.2,12.7C6.2,11.5 6.6,10.5 7.3,9.7L7.3,9.7Z"
|
||||
android:fillColor="#000" />
|
||||
</vector>
|
||||
15
core/ui/src/main/res/drawable/img_sui_22.xml
Normal file
15
core/ui/src/main/res/drawable/img_sui_22.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<clip-path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"/>
|
||||
|
||||
<path
|
||||
android:pathData="M0,0h22v22h-22z"
|
||||
android:fillColor="#4CA3FF"/>
|
||||
<path
|
||||
android:pathData="M14.8,9.7C15.4,10.5 15.8,11.5 15.8,12.7C15.8,13.8 15.4,14.9 14.7,15.7L14.7,15.8L14.7,15.7C14.6,15.6 14.6,15.5 14.6,15.4C14.3,14 13.2,12.7 11.4,11.7C10.2,11 9.5,10.1 9.3,9.2C9.2,8.6 9.3,8 9.4,7.5C9.6,6.9 9.9,6.5 10.1,6.2L10.8,5.4C10.9,5.2 11.1,5.2 11.2,5.4L14.8,9.7H14.8ZM15.9,8.8L11.2,3.1C11.1,3 10.9,3 10.8,3.1L6.1,8.8L6.1,8.8C5.3,9.9 4.8,11.3 4.8,12.8C4.8,16.2 7.6,19 11,19C14.5,19 17.3,16.2 17.3,12.8C17.3,11.3 16.7,9.9 15.9,8.8L15.9,8.8L15.9,8.8ZM7.3,9.7L7.7,9.1L7.7,9.2C7.7,9.3 7.7,9.4 7.7,9.5C8,10.9 9,12.1 10.6,13C12,13.8 12.8,14.7 13.1,15.7C13.2,16.2 13.2,16.6 13.1,16.9L13.1,17L13.1,17C12.5,17.3 11.8,17.5 11,17.5C8.4,17.5 6.2,15.3 6.2,12.7C6.2,11.5 6.6,10.5 7.3,9.7L7.3,9.7Z"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.data.managetokens
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.compatibility.l2BlockchainsCoinIds
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.isSupportedInApp
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
|
|
@ -15,7 +15,9 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
|||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.extensions.canHandleBlockchain
|
||||
import com.tangem.domain.common.extensions.canHandleToken
|
||||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
|
|
@ -31,6 +33,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.pagination.BatchFetchResult
|
||||
import com.tangem.pagination.BatchListSource
|
||||
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
|
||||
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher.Request
|
||||
import com.tangem.pagination.toBatchFlow
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
|
|
@ -40,6 +43,7 @@ internal class DefaultManageTokensRepository(
|
|||
private val userWalletsStore: UserWalletsStore,
|
||||
private val manageTokensUpdateFetcher: ManageTokensUpdateFetcher,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val testnetTokensStorage: TestnetTokensStorage,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : ManageTokensRepository {
|
||||
|
||||
|
|
@ -59,7 +63,6 @@ internal class DefaultManageTokensRepository(
|
|||
).toBatchFlow()
|
||||
}
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private fun createFetcher(
|
||||
batchSize: Int,
|
||||
): LimitOffsetBatchFetcher<ManageTokensListConfig, List<ManagedCryptoCurrency>> = LimitOffsetBatchFetcher(
|
||||
|
|
@ -67,59 +70,101 @@ internal class DefaultManageTokensRepository(
|
|||
batchSize = batchSize,
|
||||
subFetcher = { request, _, isFirstBatchFetching ->
|
||||
val userWallet = request.params.userWalletId?.let { getUserWallet(it) }
|
||||
val supportedBlockchains = getSupportedBlockchains(userWallet)
|
||||
val searchText = request.params.searchText?.takeIf { it.isNotBlank() }
|
||||
|
||||
val call = suspend {
|
||||
tangemTechApi.getCoins(
|
||||
networkIds = supportedBlockchains.joinToString(
|
||||
separator = ",",
|
||||
transform = Blockchain::toNetworkId,
|
||||
),
|
||||
active = true,
|
||||
searchText = searchText,
|
||||
offset = request.offset * request.limit,
|
||||
limit = request.limit,
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
val coinsResponse = if (isFirstBatchFetching) {
|
||||
call()
|
||||
if (userWallet?.scanResponse?.card?.isTestCard == true) {
|
||||
fetchTestnetCurrencies(userWallet, request)
|
||||
} else {
|
||||
retryOnError(call = call)
|
||||
fetchCurrencies(userWallet, request, isFirstBatchFetching)
|
||||
}
|
||||
// filter l2 coins
|
||||
val updatedCoinsResponse = coinsResponse.copy(
|
||||
coins = coinsResponse.coins.filterNot { l2BlockchainsCoinIds.contains(it.id) },
|
||||
)
|
||||
|
||||
val tokensResponse = request.params.userWalletId?.let { getSavedUserTokensResponseSync(it) }
|
||||
val items = if (isFirstBatchFetching &&
|
||||
tokensResponse != null &&
|
||||
userWallet != null &&
|
||||
request.params.searchText.isNullOrBlank()
|
||||
) {
|
||||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
} else {
|
||||
managedCryptoCurrencyFactory.create(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
derivationStyleProvider = userWallet?.scanResponse?.derivationStyleProvider,
|
||||
)
|
||||
}
|
||||
|
||||
BatchFetchResult.Success(
|
||||
data = items,
|
||||
empty = items.isEmpty(),
|
||||
last = items.size < request.limit,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private suspend fun fetchCurrencies(
|
||||
userWallet: UserWallet?,
|
||||
request: Request<ManageTokensListConfig>,
|
||||
isFirstBatchFetching: Boolean,
|
||||
): BatchFetchResult.Success<List<ManagedCryptoCurrency>> {
|
||||
val supportedBlockchains = getSupportedBlockchains(userWallet)
|
||||
|
||||
val call = suspend {
|
||||
tangemTechApi.getCoins(
|
||||
networkIds = supportedBlockchains.joinToString(
|
||||
separator = ",",
|
||||
transform = Blockchain::toNetworkId,
|
||||
),
|
||||
active = true,
|
||||
searchText = request.params.searchText,
|
||||
offset = request.offset * request.limit,
|
||||
limit = request.limit,
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
val coinsResponse = if (isFirstBatchFetching) {
|
||||
call()
|
||||
} else {
|
||||
retryOnError(call = call)
|
||||
}
|
||||
// filter l2 coins
|
||||
val updatedCoinsResponse = coinsResponse.copy(
|
||||
coins = coinsResponse.coins.filterNot { l2BlockchainsCoinIds.contains(it.id) },
|
||||
)
|
||||
|
||||
val tokensResponse = request.params.userWalletId?.let { getSavedUserTokensResponseSync(it) }
|
||||
val items = if (isFirstBatchFetching &&
|
||||
tokensResponse != null &&
|
||||
userWallet != null &&
|
||||
request.params.searchText.isNullOrBlank()
|
||||
) {
|
||||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
} else {
|
||||
managedCryptoCurrencyFactory.create(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
derivationStyleProvider = userWallet?.scanResponse?.derivationStyleProvider,
|
||||
)
|
||||
}
|
||||
|
||||
return BatchFetchResult.Success(
|
||||
data = items,
|
||||
empty = items.isEmpty(),
|
||||
last = items.size < request.limit,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun fetchTestnetCurrencies(
|
||||
userWallet: UserWallet,
|
||||
request: Request<ManageTokensListConfig>,
|
||||
): BatchFetchResult.Success<List<ManagedCryptoCurrency>> {
|
||||
val searchText = request.params.searchText
|
||||
val testnetTokensConfig = testnetTokensStorage.getConfig()
|
||||
|
||||
val items = managedCryptoCurrencyFactory.createTestnetWithCustomTokens(
|
||||
testnetTokensConfig = if (!searchText.isNullOrBlank()) {
|
||||
testnetTokensConfig.copy(
|
||||
tokens = testnetTokensConfig.tokens.filter { token ->
|
||||
token.symbol.contains(other = searchText, ignoreCase = true) ||
|
||||
token.name.contains(other = searchText, ignoreCase = true)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
testnetTokensConfig
|
||||
},
|
||||
tokensResponse = getSavedUserTokensResponseSync(userWallet.walletId),
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
|
||||
return BatchFetchResult.Success(
|
||||
data = items,
|
||||
empty = items.isEmpty(),
|
||||
last = true,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
||||
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.data.managetokens.DefaultManageTokensRepository
|
|||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
|
|
@ -27,6 +28,7 @@ internal object ManageTokensDataModule {
|
|||
userWalletsStore: UserWalletsStore,
|
||||
manageTokensUpdateFetcher: ManageTokensUpdateFetcher,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
testnetTokensStorage: TestnetTokensStorage,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): ManageTokensRepository {
|
||||
return DefaultManageTokensRepository(
|
||||
|
|
@ -34,6 +36,7 @@ internal object ManageTokensDataModule {
|
|||
userWalletsStore,
|
||||
manageTokensUpdateFetcher,
|
||||
appPreferencesStore,
|
||||
testnetTokensStorage,
|
||||
dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.data.common.currency.getNetwork
|
|||
import com.tangem.data.common.currency.getTokenId
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
|
|
@ -34,16 +35,49 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
tokensResponse: UserTokensResponse,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val customTokens = tokensResponse.tokens
|
||||
.mapNotNull { token ->
|
||||
maybeCreateCustomToken(token, coinsResponse.imageHost, derivationStyleProvider)
|
||||
}
|
||||
|
||||
val customTokens = createCustomTokens(tokensResponse, derivationStyleProvider)
|
||||
val tokens = create(coinsResponse, tokensResponse, derivationStyleProvider)
|
||||
|
||||
return customTokens + tokens
|
||||
}
|
||||
|
||||
fun createTestnetWithCustomTokens(
|
||||
testnetTokensConfig: TestnetTokensConfig,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val customTokens = tokensResponse
|
||||
?.let { createCustomTokens(it, derivationStyleProvider) }
|
||||
?: emptyList()
|
||||
val testnetTokens = testnetTokensConfig.tokens.map { testnetToken ->
|
||||
ManagedCryptoCurrency.Token(
|
||||
id = ManagedCryptoCurrency.ID(testnetToken.id),
|
||||
name = testnetToken.name,
|
||||
symbol = testnetToken.symbol,
|
||||
iconUrl = getIconUrl(testnetToken.id),
|
||||
availableNetworks = testnetToken.networks?.mapNotNull { network ->
|
||||
createSource(
|
||||
networkId = network.id,
|
||||
contractAddress = network.address,
|
||||
decimals = network.decimalCount ?: return@mapNotNull null,
|
||||
derivationStyleProvider = derivationStyleProvider,
|
||||
)
|
||||
} ?: emptyList(),
|
||||
addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, derivationStyleProvider),
|
||||
)
|
||||
}
|
||||
|
||||
return customTokens + testnetTokens
|
||||
}
|
||||
|
||||
private fun createCustomTokens(
|
||||
tokensResponse: UserTokensResponse,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): List<ManagedCryptoCurrency> = tokensResponse.tokens
|
||||
.mapNotNull { token ->
|
||||
maybeCreateCustomToken(token, null, derivationStyleProvider)
|
||||
}
|
||||
|
||||
private fun maybeCreateCustomToken(
|
||||
token: UserTokensResponse.Token,
|
||||
imageHost: String?,
|
||||
|
|
@ -100,23 +134,29 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
symbol = coinResponse.symbol,
|
||||
iconUrl = getIconUrl(coinResponse.id, imageHost),
|
||||
availableNetworks = updatedNetworks.mapNotNull { network ->
|
||||
createSource(network, derivationStyleProvider)
|
||||
createSource(
|
||||
networkId = network.networkId,
|
||||
contractAddress = network.contractAddress,
|
||||
decimals = network.decimalCount?.toInt() ?: return@mapNotNull null,
|
||||
derivationStyleProvider = derivationStyleProvider,
|
||||
)
|
||||
},
|
||||
addedIn = findAddedInNetworks(coinResponse.id, tokensResponse, derivationStyleProvider),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createSource(
|
||||
networkResponse: CoinsResponse.Coin.Network,
|
||||
networkId: String,
|
||||
contractAddress: String?,
|
||||
decimals: Int,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
extraDerivationPath: String? = null,
|
||||
): SourceNetwork? {
|
||||
val blockchain = Blockchain.fromNetworkId(networkResponse.networkId)
|
||||
val blockchain = Blockchain.fromNetworkId(networkId)
|
||||
?.takeIf { it.isSupportedInApp() }
|
||||
?: return null
|
||||
|
||||
val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null
|
||||
val contractAddress = networkResponse.contractAddress
|
||||
|
||||
return if (contractAddress.isNullOrBlank()) {
|
||||
SourceNetwork.Main(
|
||||
|
|
@ -127,7 +167,7 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
} else {
|
||||
SourceNetwork.Default(
|
||||
network = network,
|
||||
decimals = requireNotNull(networkResponse.decimalCount?.toInt()),
|
||||
decimals = decimals,
|
||||
contractAddress = contractAddress,
|
||||
)
|
||||
}
|
||||
|
|
@ -157,7 +197,7 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getIconUrl(id: String, imageHost: String?): String {
|
||||
private fun getIconUrl(id: String, imageHost: String? = DEFAULT_IMAGE_HOST): String {
|
||||
return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,25 +3,30 @@ package com.tangem.data.staking
|
|||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
internal class DefaultStakingPendingTransactionRepository : StakingPendingTransactionRepository {
|
||||
|
||||
private val pendingTransactions = CopyOnWriteArrayList<PendingTransaction>()
|
||||
private val pendingTransactionsMap = ConcurrentHashMap<UserWalletId, CopyOnWriteArrayList<PendingTransaction>>()
|
||||
|
||||
override fun saveTransaction(transaction: PendingTransaction) {
|
||||
pendingTransactions.add(transaction)
|
||||
override fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction) {
|
||||
val transactions = pendingTransactionsMap.computeIfAbsent(userWalletId) { CopyOnWriteArrayList() }
|
||||
transactions.add(transaction)
|
||||
}
|
||||
|
||||
override fun removeTransactions(transactions: Set<PendingTransaction>) {
|
||||
pendingTransactions.removeAll(transactions)
|
||||
override fun removeTransactions(userWalletId: UserWalletId, transactions: Set<PendingTransaction>) {
|
||||
pendingTransactionsMap[userWalletId]?.removeAll(transactions)
|
||||
}
|
||||
|
||||
override fun getTransactionsWithBalanceItems(): List<Pair<PendingTransaction, BalanceItem>> {
|
||||
return pendingTransactions.mapNotNull { pendingTransaction ->
|
||||
override fun getTransactionsWithBalanceItems(
|
||||
userWalletId: UserWalletId,
|
||||
): List<Pair<PendingTransaction, BalanceItem>> {
|
||||
return pendingTransactionsMap[userWalletId]?.mapNotNull { pendingTransaction: PendingTransaction ->
|
||||
PendingTransactionItemConverter.convert(pendingTransaction)?.let { balanceItem ->
|
||||
pendingTransaction to balanceItem
|
||||
}
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ internal class DefaultStakingRepository(
|
|||
key = YIELDS_STORE_KEY,
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val stakingTokensWithYields = stakeKitApi.getMultipleYields(preferredValidatorsOnly = true)
|
||||
val stakingTokensWithYields = stakeKitApi.getMultipleYields(preferredValidatorsOnly = false)
|
||||
.getOrThrow()
|
||||
|
||||
stakingYieldsStore.store(stakingTokensWithYields.data)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ internal object PendingTransactionItemConverter : Converter<PendingTransaction,
|
|||
override fun convert(value: PendingTransaction): BalanceItem? {
|
||||
return BalanceItem(
|
||||
groupId = value.groupId ?: return null,
|
||||
type = value.type ?: return null,
|
||||
amount = value.amount ?: return null,
|
||||
type = value.type,
|
||||
amount = value.amount,
|
||||
rawCurrencyId = value.rawCurrencyId,
|
||||
validatorAddress = value.validator?.address,
|
||||
date = DateTime.now(),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.data.staking.converters
|
|||
import com.tangem.data.staking.converters.action.PendingActionConverter
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class YieldBalanceConverter : Converter<YieldBalanceWrapperDTO, YieldBalance> {
|
||||
|
|
@ -27,9 +26,7 @@ internal class YieldBalanceConverter : Converter<YieldBalanceWrapperDTO, YieldBa
|
|||
validatorAddress = item.validatorAddress ?: item.validatorAddresses?.get(0),
|
||||
date = item.date?.toDateTime(),
|
||||
pendingActions = pendingActionConverter
|
||||
.convertList(item.pendingActions)
|
||||
// temporarily exclude VOTE_LOCKED, it will be implemented in future iterations
|
||||
.filterNot { it.type == StakingActionType.VOTE_LOCKED },
|
||||
.convertList(item.pendingActions),
|
||||
isPending = false,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class YieldConverter(
|
|||
metadata = convertMetadata(value.metadata),
|
||||
validators = value.validators
|
||||
.asSequence()
|
||||
.filter { it.preferred && it.status == ValidatorStatusDTO.ACTIVE }
|
||||
.filter { it.status == ValidatorStatusDTO.ACTIVE }
|
||||
.sortedByDescending { it.apr }
|
||||
.map { convertValidator(it) }
|
||||
.toImmutableList(),
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ android {
|
|||
|
||||
dependencies {
|
||||
api(projects.domain.staking.models)
|
||||
|
||||
api(projects.domain.core)
|
||||
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.jodatime)
|
||||
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ import java.math.BigDecimal
|
|||
|
||||
data class PendingTransaction(
|
||||
val groupId: String?,
|
||||
val type: BalanceType?,
|
||||
val amount: BigDecimal?,
|
||||
val type: BalanceType,
|
||||
val amount: BigDecimal,
|
||||
val rawCurrencyId: String?,
|
||||
val validator: Yield.Validator?,
|
||||
val balancesId: Int,
|
||||
)
|
||||
|
|
@ -1,14 +1,7 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class SubmitHashData(
|
||||
val transactionHash: String,
|
||||
val transactionId: String,
|
||||
val validator: Yield.Validator?,
|
||||
val amount: BigDecimal?,
|
||||
val balanceType: BalanceType?,
|
||||
val rawCurrencyId: String?,
|
||||
val pendingTransaction: PendingTransaction,
|
||||
)
|
||||
|
|
@ -32,6 +32,14 @@ sealed class YieldBalance {
|
|||
.distinctBy { it.validatorAddress }
|
||||
.size
|
||||
}
|
||||
|
||||
fun getBalancesUniqueId(): Int {
|
||||
// need to exclude rewards because their amount may change frequently
|
||||
return balance.items
|
||||
.filter { it.type != BalanceType.REWARDS }
|
||||
.map { it.amount.toString() + it.type.toString() + it.groupId }
|
||||
.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
data object Empty : YieldBalance()
|
||||
|
|
|
|||
|
|
@ -5,18 +5,19 @@ import com.tangem.domain.staking.model.stakekit.BalanceItem
|
|||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Use case for getting saved staking pending transactions.
|
||||
*/
|
||||
class GetPendingTransactionsUseCase(
|
||||
class GetStakingPendingTransactionsUseCase(
|
||||
private val stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(): Either<StakingError, List<BalanceItem>> {
|
||||
operator fun invoke(userWalletId: UserWalletId): Either<StakingError, List<BalanceItem>> {
|
||||
return Either.catch {
|
||||
stakingPendingTransactionRepository.getTransactionsWithBalanceItems().map { it.second }
|
||||
stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId).map { it.second }
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
|
|
@ -3,52 +3,74 @@ package com.tangem.domain.staking
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import java.util.UUID
|
||||
|
||||
class InvalidatePendingTransactionsUseCase(
|
||||
private val stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(yieldBalance: YieldBalance): Either<StakingError, List<BalanceItem>> {
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
balanceItems: List<BalanceItem>,
|
||||
balancesId: Int,
|
||||
): Either<StakingError, List<BalanceItem>> {
|
||||
return Either.catch {
|
||||
if (yieldBalance is YieldBalance.Data) {
|
||||
val (balancesToDisplay, transactionsToRemove) = mergeRealAndPendingTransactions(
|
||||
real = yieldBalance.balance.items,
|
||||
pending = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(),
|
||||
)
|
||||
val (balancesToDisplay, transactionsToRemove) = mergeRealAndPendingTransactions(
|
||||
realData = balanceItems,
|
||||
newBalancesId = balancesId,
|
||||
pendingData = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId),
|
||||
)
|
||||
|
||||
stakingPendingTransactionRepository.removeTransactions(transactionsToRemove.toSet())
|
||||
stakingPendingTransactionRepository.removeTransactions(userWalletId, transactionsToRemove.toSet())
|
||||
|
||||
balancesToDisplay
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
balancesToDisplay
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeRealAndPendingTransactions(
|
||||
real: List<BalanceItem>,
|
||||
pending: List<Pair<PendingTransaction, BalanceItem>>,
|
||||
realData: List<BalanceItem>,
|
||||
newBalancesId: Int,
|
||||
pendingData: List<Pair<PendingTransaction, BalanceItem>>,
|
||||
): Pair<List<BalanceItem>, List<PendingTransaction>> {
|
||||
val map = real.associateBy { Triple(it.groupId, it.type, it.amount) }.toMutableMap()
|
||||
val balances = realData.associateBy { Triple(it.groupId, it.type, it.amount) }.toMutableMap()
|
||||
|
||||
val toRemove = mutableListOf<PendingTransaction>()
|
||||
val transactionsToRemove = mutableListOf<PendingTransaction>()
|
||||
|
||||
pending.forEach { (pendingTransaction, balanceItem) ->
|
||||
pendingData.forEach { (pendingTransaction, balanceItem) ->
|
||||
val key = Triple(balanceItem.groupId, balanceItem.type, balanceItem.amount)
|
||||
if (map.containsKey(key)) {
|
||||
map[key] = balanceItem
|
||||
} else {
|
||||
toRemove.add(pendingTransaction)
|
||||
val oldBalancesId = pendingTransaction.balancesId
|
||||
|
||||
when {
|
||||
newBalancesId != oldBalancesId -> {
|
||||
transactionsToRemove.add(pendingTransaction)
|
||||
}
|
||||
balances.containsKey(key) -> {
|
||||
balances[key] = balanceItem
|
||||
}
|
||||
else -> {
|
||||
val groupId = UUID.randomUUID().toString()
|
||||
balances[Triple(groupId, BalanceType.STAKED, pendingTransaction.amount)] = BalanceItem(
|
||||
groupId = groupId,
|
||||
type = pendingTransaction.type,
|
||||
amount = pendingTransaction.amount,
|
||||
rawCurrencyId = pendingTransaction.rawCurrencyId,
|
||||
validatorAddress = pendingTransaction.validator?.address,
|
||||
date = null,
|
||||
pendingActions = emptyList(),
|
||||
isPending = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map.values.toList() to toRemove
|
||||
return balances.values.toList() to transactionsToRemove
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.staking.model.PendingTransaction
|
|||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Use case for saving hash that failed to submit during staking confirmation
|
||||
|
|
@ -14,9 +15,12 @@ class SavePendingTransactionUseCase(
|
|||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(pendingTransaction: PendingTransaction): Either<StakingError, Unit> {
|
||||
operator fun invoke(userWalletId: UserWalletId, transaction: PendingTransaction): Either<StakingError, Unit> {
|
||||
return Either.catch {
|
||||
stakingPendingTransactionRepository.saveTransaction(pendingTransaction)
|
||||
stakingPendingTransactionRepository.saveTransaction(
|
||||
userWalletId = userWalletId,
|
||||
transaction = transaction,
|
||||
)
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ package com.tangem.domain.staking.repositories
|
|||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface StakingPendingTransactionRepository {
|
||||
|
||||
fun getTransactionsWithBalanceItems(): List<Pair<PendingTransaction, BalanceItem>>
|
||||
fun getTransactionsWithBalanceItems(userWalletId: UserWalletId): List<Pair<PendingTransaction, BalanceItem>>
|
||||
|
||||
fun saveTransaction(transaction: PendingTransaction)
|
||||
fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction)
|
||||
|
||||
fun removeTransactions(transactions: Set<PendingTransaction>)
|
||||
fun removeTransactions(userWalletId: UserWalletId, transactions: Set<PendingTransaction>)
|
||||
}
|
||||
|
|
@ -85,17 +85,12 @@ internal object StakingNotification {
|
|||
),
|
||||
)
|
||||
|
||||
data class PendingAction(
|
||||
data class Ordinary(
|
||||
val title: TextReference,
|
||||
val text: TextReference,
|
||||
) : StakingNotification.Info(
|
||||
title = title,
|
||||
subtitle = text,
|
||||
)
|
||||
|
||||
data object TronRevote : StakingNotification.Info(
|
||||
title = resourceReference(R.string.staking_revote),
|
||||
subtitle = resourceReference(R.string.staking_notifications_revote_tron_text),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
|
|||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
|
||||
|
|
@ -98,7 +99,7 @@ internal sealed class StakingStates {
|
|||
val transactionDoneState: TransactionDoneState,
|
||||
val isApprovalNeeded: Boolean,
|
||||
val reduceAmountBy: BigDecimal?,
|
||||
val balanceState: BalanceState?,
|
||||
val possiblePendingTransaction: PendingTransaction?,
|
||||
) : ConfirmationState()
|
||||
|
||||
data class Empty(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.staking.model.stakekit.BalanceItem
|
|||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType.Companion.isClickable
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
|
|
@ -83,7 +84,11 @@ internal class BalanceItemConverter(
|
|||
private fun getSubtitle(balance: BalanceItem) = when (balance.type) {
|
||||
BalanceType.UNSTAKING -> getUnbondingDate(balance.date)
|
||||
BalanceType.UNSTAKED -> resourceReference(R.string.staking_tap_to_withdraw)
|
||||
BalanceType.LOCKED -> resourceReference(R.string.staking_tap_to_unlock)
|
||||
BalanceType.LOCKED -> if (balance.pendingActions.any { it.type == StakingActionType.VOTE_LOCKED }) {
|
||||
resourceReference(R.string.staking_tap_to_unlock_or_vote)
|
||||
} else {
|
||||
resourceReference(R.string.staking_tap_to_unlock)
|
||||
}
|
||||
BalanceType.PREPARING -> {
|
||||
val warmupPeriod = yield.metadata.warmupPeriod.days
|
||||
combinedReference(
|
||||
|
|
|
|||
|
|
@ -28,12 +28,17 @@ internal class YieldBalancesConverter(
|
|||
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
val balanceToShowItems = balancesToShowProvider()
|
||||
|
||||
return if (yieldBalance is YieldBalance.Data) {
|
||||
val cryptoRewardsValue = yieldBalance.getRewardStakingBalance()
|
||||
val fiatRewardsValue = cryptoCurrencyStatus.value.fiatRate?.times(cryptoRewardsValue)
|
||||
return if (yieldBalance is YieldBalance.Data || balanceToShowItems.any { it.isPending }) {
|
||||
val cryptoRewardsValue = (yieldBalance as? YieldBalance.Data)?.getRewardStakingBalance()
|
||||
|
||||
val balanceToShowItems = balancesToShowProvider()
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
|
||||
fiatRate.times(cryptoRewardsValue)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
InnerYieldBalanceState.Data(
|
||||
rewardsCrypto = BigDecimalFormatter.formatCryptoAmount(
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.SubmitHashData
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
|
|
@ -35,6 +32,7 @@ import kotlinx.coroutines.awaitAll
|
|||
import kotlinx.coroutines.coroutineScope
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class StakingTransactionSender @AssistedInject constructor(
|
||||
|
|
@ -91,12 +89,16 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
sendStakingTransaction(
|
||||
fullTransactionsData = fullTransactionsData,
|
||||
balanceState = confirmationState.balanceState,
|
||||
possiblePendingTransaction = composePendingTransaction(confirmationState),
|
||||
onSendSuccess = onSendSuccess,
|
||||
onSendError = onSendError,
|
||||
)
|
||||
}
|
||||
|
||||
private fun composePendingTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction {
|
||||
return confirmationState.possiblePendingTransaction ?: composeStakeTransaction(confirmationState)
|
||||
}
|
||||
|
||||
private suspend fun getStakingTransactions(
|
||||
state: StakingUiState,
|
||||
confirmationState: StakingStates.ConfirmationState.Data,
|
||||
|
|
@ -204,7 +206,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
private suspend fun sendStakingTransaction(
|
||||
fullTransactionsData: List<FullTransactionData>,
|
||||
balanceState: BalanceState?,
|
||||
possiblePendingTransaction: PendingTransaction,
|
||||
onSendSuccess: (txUrl: String) -> Unit,
|
||||
onSendError: (SendTransactionError?) -> Unit,
|
||||
) {
|
||||
|
|
@ -218,13 +220,9 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
},
|
||||
ifRight = { transactionHashes ->
|
||||
submitHash(
|
||||
transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id },
|
||||
transactions = fullTransactionsData.map { it.stakeKitTransaction },
|
||||
transactionHashes = transactionHashes,
|
||||
groupId = balanceState?.groupId,
|
||||
validator = balanceState?.validator,
|
||||
amount = balanceState?.cryptoDecimal,
|
||||
balanceType = balanceState?.type,
|
||||
rawCurrencyId = balanceState?.rawCurrencyId,
|
||||
pendingTransaction = possiblePendingTransaction,
|
||||
)
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = transactionHashes.last(),
|
||||
|
|
@ -238,25 +236,18 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private suspend fun submitHash(
|
||||
transactionIds: List<String>,
|
||||
transactions: List<StakingTransaction>,
|
||||
transactionHashes: List<String>,
|
||||
groupId: String?,
|
||||
validator: Yield.Validator?,
|
||||
amount: BigDecimal?,
|
||||
balanceType: BalanceType?,
|
||||
rawCurrencyId: String?,
|
||||
pendingTransaction: PendingTransaction,
|
||||
) {
|
||||
transactionIds
|
||||
transactions
|
||||
.zip(transactionHashes)
|
||||
.forEach { (transactionId, transactionHash) ->
|
||||
.forEach { (transaction, transactionHash) ->
|
||||
submitHashUseCase(
|
||||
SubmitHashData(
|
||||
transactionId = transactionId,
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
validator = validator,
|
||||
amount = amount,
|
||||
balanceType = balanceType,
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
pendingTransaction = pendingTransaction,
|
||||
),
|
||||
)
|
||||
.onLeft { error ->
|
||||
|
|
@ -267,20 +258,14 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
transactionId = transactionId,
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
savePendingTransactionUseCase.invoke(
|
||||
PendingTransaction(
|
||||
groupId = groupId,
|
||||
type = balanceType,
|
||||
amount = amount,
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
validator = validator,
|
||||
),
|
||||
)
|
||||
if (transaction.type != StakingTransactionType.FREEZE_ENERGY) {
|
||||
savePendingTransactionUseCase.invoke(userWallet.walletId, pendingTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -299,6 +284,19 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun composeStakeTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction {
|
||||
val state = stateController.value
|
||||
|
||||
return PendingTransaction(
|
||||
groupId = UUID.randomUUID().toString(),
|
||||
type = BalanceType.STAKED,
|
||||
amount = (state.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO,
|
||||
rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId,
|
||||
validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator,
|
||||
balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0,
|
||||
)
|
||||
}
|
||||
|
||||
private data class FullTransactionData(
|
||||
val stakeKitTransaction: StakingTransaction,
|
||||
val tangemTransaction: TransactionData.Compiled,
|
||||
|
|
|
|||
|
|
@ -93,6 +93,6 @@ internal object ConfirmationStatePreviewData {
|
|||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
pendingActions = null,
|
||||
balanceState = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -273,17 +273,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addEnterInfoNotifications() {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
|
||||
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
|
||||
?.items?.any {
|
||||
it.type == BalanceType.PREPARING ||
|
||||
it.type == BalanceType.STAKED ||
|
||||
it.type == BalanceType.LOCKED
|
||||
} == true
|
||||
if (hasStakedBalance && isTron) {
|
||||
add(StakingNotification.Info.TronRevote)
|
||||
}
|
||||
addTronRevoteNotification()
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addPendingInfoNotifications(prevState: StakingUiState) {
|
||||
|
|
@ -319,16 +309,39 @@ internal class AddStakingNotificationsTransformer(
|
|||
null to null
|
||||
}
|
||||
}
|
||||
StakingActionType.VOTE_LOCKED -> {
|
||||
resourceReference(R.string.staking_revote) to
|
||||
resourceReference(R.string.staking_notifications_revote_tron_text)
|
||||
}
|
||||
else -> null to null
|
||||
}
|
||||
|
||||
if (titleReference != null && textReference != null) {
|
||||
add(
|
||||
StakingNotification.Info.PendingAction(
|
||||
StakingNotification.Info.Ordinary(
|
||||
title = titleReference,
|
||||
text = textReference,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTronRevoteNotification() {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
|
||||
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
|
||||
?.items?.any {
|
||||
it.type == BalanceType.PREPARING ||
|
||||
it.type == BalanceType.STAKED ||
|
||||
it.type == BalanceType.LOCKED
|
||||
} == true
|
||||
if (isTron && hasStakedBalance) {
|
||||
add(
|
||||
StakingNotification.Info.Ordinary(
|
||||
title = resourceReference(R.string.staking_revote),
|
||||
text = resourceReference(R.string.staking_notifications_revote_tron_text),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SetBalanceStateTransformer(
|
||||
private val balanceState: BalanceState? = null,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
?: return prevState
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = possibleConfirmationState.copy(balanceState = balanceState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -144,9 +144,19 @@ internal class SetButtonsStateTransformer(
|
|||
clickIntents.onAmountValueChange("") // reset amount state
|
||||
clickIntents.onNextClick(StakingActionCommonType.ENTER)
|
||||
}
|
||||
StakingStep.Validators,
|
||||
StakingStep.Amount,
|
||||
-> clickIntents.onNextClick()
|
||||
StakingStep.Validators -> {
|
||||
if (confirmationState is StakingStates.ConfirmationState.Data) {
|
||||
clickIntents.onNextClick(
|
||||
pendingActions = confirmationState.pendingActions,
|
||||
pendingAction = confirmationState.pendingAction,
|
||||
)
|
||||
} else {
|
||||
clickIntents.onNextClick()
|
||||
}
|
||||
}
|
||||
StakingStep.Amount -> {
|
||||
clickIntents.onNextClick()
|
||||
}
|
||||
StakingStep.Confirmation -> onConfirmationClick()
|
||||
StakingStep.RewardsValidators -> Unit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val filteredValidators = yield.validators.filter {
|
||||
it.preferred
|
||||
}
|
||||
val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val possibleValidatorState = possibleConfirmationState?.validatorState as? ValidatorState.Content
|
||||
val chosenValidator = possibleValidatorState?.chosenValidator ?: yield.validators[0]
|
||||
val chosenValidator = possibleValidatorState?.chosenValidator ?: filteredValidators[0]
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = StakingStates.ConfirmationState.Data(
|
||||
|
|
@ -32,7 +35,7 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
validatorState = ValidatorState.Content(
|
||||
isClickable = true,
|
||||
chosenValidator = chosenValidator,
|
||||
availableValidators = yield.validators,
|
||||
availableValidators = filteredValidators,
|
||||
),
|
||||
notifications = persistentListOf(),
|
||||
footerText = getFooter(prevState, chosenValidator),
|
||||
|
|
@ -41,7 +44,7 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
pendingActions = possibleConfirmationState?.pendingActions,
|
||||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
balanceState = null,
|
||||
possiblePendingTransaction = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers
|
|||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object SetConfirmationStateResetAssentTransformer : Transformer<StakingUiState> {
|
||||
|
|
@ -13,7 +14,7 @@ internal object SetConfirmationStateResetAssentTransformer : Transformer<Staking
|
|||
confirmationState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
validatorState = confirmationState.validatorState.copySealed(isClickable = true),
|
||||
validatorState = ValidatorState.Loading,
|
||||
)
|
||||
} else {
|
||||
confirmationState
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ internal class SetInitialDataStateTransformer(
|
|||
pendingActions = null,
|
||||
isApprovalNeeded = isApprovalNeeded,
|
||||
reduceAmountBy = null,
|
||||
balanceState = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SetPossiblePendingTransactionTransformer(
|
||||
private val balanceState: BalanceState,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val possibleConfirmationState =
|
||||
prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
|
||||
|
||||
val balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = possibleConfirmationState.copy(
|
||||
possiblePendingTransaction = PendingTransaction(
|
||||
groupId = balanceState.groupId,
|
||||
type = balanceState.type,
|
||||
amount = balanceState.cryptoDecimal,
|
||||
rawCurrencyId = balanceState.rawCurrencyId,
|
||||
validator = balanceState.validator,
|
||||
balancesId = balancesId,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
|
|
@ -39,6 +40,7 @@ internal fun StakingConfirmationContent(
|
|||
) {
|
||||
if (state !is StakingStates.ConfirmationState.Data) return
|
||||
val isEnterAction = type == StakingActionCommonType.ENTER
|
||||
val showValidatorBlock = isEnterAction || state.pendingAction?.type == StakingActionType.VOTE_LOCKED
|
||||
val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -63,7 +65,7 @@ internal fun StakingConfirmationContent(
|
|||
isEditingDisabled = !isEnterAction && state.innerState != InnerConfirmationStakingState.COMPLETED,
|
||||
onClick = clickIntents::onPrevClick,
|
||||
)
|
||||
if (isEnterAction) {
|
||||
if (showValidatorBlock) {
|
||||
ValidatorBlock(validatorState = state.validatorState, onClick = clickIntents::openValidators)
|
||||
}
|
||||
StakingFeeBlock(feeState = state.feeState, isTransactionSent = isTransactionSent)
|
||||
|
|
|
|||
|
|
@ -136,9 +136,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
private val balancesToShow: List<BalanceItem>
|
||||
get() {
|
||||
return cryptoCurrencyStatus.value.yieldBalance?.let {
|
||||
invalidatePendingTransactionsUseCase(it).getOrElse { emptyList() }
|
||||
} ?: emptyList()
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
return invalidatePendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
|
||||
balancesId = yieldBalance?.getBalancesUniqueId() ?: 0,
|
||||
).getOrElse { emptyList() }
|
||||
}
|
||||
|
||||
private var isInitialInfoAnalyticSent: Boolean = false
|
||||
|
|
@ -218,13 +221,31 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingStateRouter.onNextClick()
|
||||
when {
|
||||
isInitState() -> {
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency))
|
||||
stateController.update(SetBalanceStateTransformer(balanceState))
|
||||
if (balanceState != null) {
|
||||
stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus))
|
||||
}
|
||||
|
||||
stateController.update(
|
||||
transformer = SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingViewModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = true,
|
||||
isApprovalNeeded = stakingApproval is StakingApproval.Needed,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
)
|
||||
onRefreshSwipe(isRefreshing = false)
|
||||
}
|
||||
isAssentState() -> {
|
||||
getFee(pendingAction, pendingActions)
|
||||
stateController.update(SetBalanceStateTransformer(balanceState))
|
||||
if (balanceState != null) {
|
||||
stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus))
|
||||
}
|
||||
val amountState = value.amountState as? AmountState.Data
|
||||
if (amountState?.amountTextField?.isWarning == true) {
|
||||
stateController.update(
|
||||
|
|
@ -328,6 +349,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
if (value.currentStep == StakingStep.Confirmation) {
|
||||
when ((value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState) {
|
||||
InnerConfirmationStakingState.ASSENT -> {
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stakingStateRouter.onPrevClick()
|
||||
}
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.RewardBlockType
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
|
|
@ -37,6 +38,7 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val stakingEntryInfoProvider: Provider<StakingEntryInfo?>,
|
||||
private val pendingBalancesProvider: Provider<List<BalanceItem>>,
|
||||
private val symbol: String,
|
||||
private val decimals: Int,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
|
|
@ -143,16 +145,28 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
|
||||
val stakingEntryInfo = stakingEntryInfoProvider.invoke()
|
||||
val iconState = state.tokenInfoBlockState.iconState
|
||||
val pendingBalances = pendingBalancesProvider.invoke()
|
||||
val fiatRate = status.value.fiatRate
|
||||
|
||||
return when {
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> {
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isEmpty() -> {
|
||||
getStakeAvailableState(stakingEntryInfo, iconState)
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isNotEmpty() -> {
|
||||
val pendingBalancesCryptoAmount = pendingBalances.sumOf { it.amount }
|
||||
|
||||
val stakingFiatAmount = fiatRate?.multiply(pendingBalancesCryptoAmount)
|
||||
getStakedState(
|
||||
status = status,
|
||||
stakingCryptoAmount = pendingBalancesCryptoAmount,
|
||||
stakingFiatAmount = stakingFiatAmount,
|
||||
stakingRewardAmount = null,
|
||||
)
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> {
|
||||
StakingBlockUM.Error(iconState = iconState)
|
||||
}
|
||||
else -> {
|
||||
val fiatRate = status.value.fiatRate
|
||||
val stakingRewardAmount = yieldBalance?.getRewardStakingBalance()?.let { fiatRate?.multiply(it) }
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { fiatRate?.multiply(it) }
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.domain.card.NetworkHasDerivationUseCase
|
|||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.*
|
||||
|
|
@ -52,6 +53,7 @@ internal class TokenDetailsStateFactory(
|
|||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val stakingEntryInfoProvider: Provider<StakingEntryInfo?>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
private val pendingBalancesProvider: Provider<List<BalanceItem>>,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val featureToggles: TokenDetailsFeatureToggles,
|
||||
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
|
||||
|
|
@ -81,6 +83,7 @@ internal class TokenDetailsStateFactory(
|
|||
currentStateProvider = currentStateProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
stakingEntryInfoProvider = stakingEntryInfoProvider,
|
||||
pendingBalancesProvider = pendingBalancesProvider,
|
||||
symbol = symbol,
|
||||
decimals = decimals,
|
||||
clickIntents = clickIntents,
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.staking.GetStakingPendingTransactionsUseCase
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.GetYieldUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
|
|
@ -119,6 +121,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val associateAssetUseCase: AssociateAssetUseCase,
|
||||
private val getStakingPendingTransactionsUseCase: GetStakingPendingTransactionsUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val vibratorHapticManager: VibratorHapticManager,
|
||||
|
|
@ -148,6 +151,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val warningsJobHolder = JobHolder()
|
||||
private val swapTxJobHolder = JobHolder()
|
||||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
private val stakingPendingBalances: List<BalanceItem>
|
||||
get() = getStakingPendingTransactionsUseCase(userWalletId).getOrElse { emptyList() }
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var stakingEntryInfo: StakingEntryInfo? = null
|
||||
|
|
@ -158,6 +163,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
stakingEntryInfoProvider = Provider { stakingEntryInfo },
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
pendingBalancesProvider = Provider { stakingPendingBalances },
|
||||
clickIntents = this,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ markdownComposeView = "0.5.4"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "release-app_5.16-803"
|
||||
tangemBlockchainSdk = "release-app_5.16-807"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "release-app_5.16-386"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
|
|||
"sei-network" -> Blockchain.Sei
|
||||
"sei-network/test" -> Blockchain.SeiTestnet
|
||||
"internet-computer" -> Blockchain.InternetComputer
|
||||
"sui" -> Blockchain.Sui
|
||||
"sui/test" -> Blockchain.SuiTestnet
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
@ -253,6 +255,8 @@ fun Blockchain.toNetworkId(): String {
|
|||
Blockchain.Sei -> "sei-network"
|
||||
Blockchain.SeiTestnet -> "sei-network/test"
|
||||
Blockchain.InternetComputer -> "internet-computer"
|
||||
Blockchain.Sui -> "sui"
|
||||
Blockchain.SuiTestnet -> "sui/test"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,6 +341,7 @@ fun Blockchain.toCoinId(): String {
|
|||
Blockchain.Cyber, Blockchain.CyberTestnet -> "cyber-ethereum"
|
||||
Blockchain.Sei, Blockchain.SeiTestnet -> "sei-network"
|
||||
Blockchain.InternetComputer -> "internet-computer"
|
||||
Blockchain.Sui, Blockchain.SuiTestnet -> "sui"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue