Updated on 2026-08-14
This commit is contained in:
commit
5e42ff6b85
20 changed files with 243 additions and 25 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 5fc86d29bc2c0dc7c057ab0242b2cfa3e9f48daf
|
||||
Subproject commit 78be297b0f09bc2be1e9b0fbab9384f17bf94c37
|
||||
|
|
@ -51,6 +51,8 @@ class TokenItemStateConverter(
|
|||
},
|
||||
private val onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)? = null,
|
||||
private val onYieldPromoCloseClick: (() -> Unit)? = null,
|
||||
private val onYieldPromoShown: ((cryptoCurrency: CryptoCurrency) -> Unit)? = null,
|
||||
private val onYieldPromoClicked: ((cryptoCurrency: CryptoCurrency) -> Unit)? = null,
|
||||
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = { currencyStatus ->
|
||||
createTitleState(
|
||||
currencyStatus = currencyStatus,
|
||||
|
|
@ -75,6 +77,8 @@ class TokenItemStateConverter(
|
|||
yieldSupplyPromoBannerKey = yieldSupplyPromoBannerKey,
|
||||
onApyLabelClick = onApyLabelClick,
|
||||
onYieldPromoCloseClick = onYieldPromoCloseClick,
|
||||
onYieldPromoShown = onYieldPromoShown,
|
||||
onYieldPromoClicked = onYieldPromoClicked,
|
||||
)
|
||||
},
|
||||
private val onItemClick: ((TokenItemState, CryptoCurrencyStatus) -> Unit)? = null,
|
||||
|
|
@ -393,12 +397,15 @@ class TokenItemStateConverter(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private fun createPromoBannerState(
|
||||
status: CryptoCurrencyStatus,
|
||||
yieldModuleApyMap: Map<String, BigDecimal>,
|
||||
yieldSupplyPromoBannerKey: String?,
|
||||
onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)?,
|
||||
onYieldPromoCloseClick: (() -> Unit)?,
|
||||
onYieldPromoShown: ((cryptoCurrency: CryptoCurrency) -> Unit)?,
|
||||
onYieldPromoClicked: ((cryptoCurrency: CryptoCurrency) -> Unit)?,
|
||||
): TokenItemState.PromoBannerState {
|
||||
val token = status.currency as? CryptoCurrency.Token ?: return TokenItemState.PromoBannerState.Empty
|
||||
if (status.value !is CryptoCurrencyStatus.Loaded) {
|
||||
|
|
@ -418,11 +425,15 @@ class TokenItemStateConverter(
|
|||
wrappedList(yieldSupplyApy),
|
||||
),
|
||||
onPromoBannerClick = {
|
||||
onYieldPromoClicked?.invoke(status.currency)
|
||||
onApyLabelClick?.invoke(status, ApySource.YIELD_SUPPLY, yieldSupplyApy.toString())
|
||||
},
|
||||
onCloseClick = {
|
||||
onYieldPromoCloseClick?.invoke()
|
||||
},
|
||||
onPromoShown = {
|
||||
onYieldPromoShown?.invoke(status.currency)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -134,6 +134,28 @@ sealed class MainScreenAnalyticsEvent(
|
|||
STATE to state,
|
||||
),
|
||||
)
|
||||
|
||||
data class YieldPromo(
|
||||
val token: String,
|
||||
val blockchain: String,
|
||||
) : MainScreenAnalyticsEvent(
|
||||
event = "Yield Promo",
|
||||
params = mapOf(
|
||||
TOKEN_PARAM to token,
|
||||
BLOCKCHAIN to blockchain,
|
||||
),
|
||||
)
|
||||
|
||||
data class YieldPromoClicked(
|
||||
val token: String,
|
||||
val blockchain: String,
|
||||
) : MainScreenAnalyticsEvent(
|
||||
event = "Yield Promo Clicked",
|
||||
params = mapOf(
|
||||
TOKEN_PARAM to token,
|
||||
BLOCKCHAIN to blockchain,
|
||||
),
|
||||
)
|
||||
// endregion
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -32,8 +32,16 @@ class NetworkLogsSaveInterceptor(
|
|||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val host = request.url.host
|
||||
val path = request.url.encodedPath
|
||||
val isRestrictedUrl = restrictedForLogURLs.contains(host + path)
|
||||
val isRestrictedHost = restrictedForLogHosts.any { host.contains(it) }
|
||||
|
||||
logRequestMessage(chain, request)
|
||||
if (isRestrictedUrl || isRestrictedHost) {
|
||||
logEmptyRequestMessage(chain, request)
|
||||
} else {
|
||||
logRequestMessage(chain, request)
|
||||
}
|
||||
|
||||
val startNs = System.nanoTime()
|
||||
val response: Response
|
||||
|
|
@ -44,11 +52,6 @@ class NetworkLogsSaveInterceptor(
|
|||
throw e
|
||||
}
|
||||
|
||||
val host = request.url.host
|
||||
val path = request.url.encodedPath
|
||||
val isRestrictedUrl = restrictedForLogURLs.contains(host + path)
|
||||
val isRestrictedHost = restrictedForLogHosts.any { host.contains(it) }
|
||||
|
||||
if (isRestrictedUrl || isRestrictedHost) {
|
||||
logResponseWithEmptyMessage(response, startNs)
|
||||
} else {
|
||||
|
|
@ -58,6 +61,13 @@ class NetworkLogsSaveInterceptor(
|
|||
return response
|
||||
}
|
||||
|
||||
private fun logEmptyRequestMessage(chain: Interceptor.Chain, request: Request) {
|
||||
val connection = chain.connection()
|
||||
val connectionProtocol = if (connection != null) " ${connection.protocol()}" else ""
|
||||
|
||||
saveLogMessage("--> ${request.method} ${request.url}$connectionProtocol\n")
|
||||
}
|
||||
|
||||
private fun logRequestMessage(chain: Interceptor.Chain, request: Request) {
|
||||
val connection = chain.connection()
|
||||
val connectionProtocol = if (connection != null) " ${connection.protocol()}" else ""
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<string name="access_code_alert_skip_ok">Trotzdem überspringen</string>
|
||||
<string name="access_code_alert_skip_title">Zugangscode nicht festgelegt</string>
|
||||
<string name="access_code_alert_validation_cancel">Code ändern</string>
|
||||
<string name="access_code_alert_validation_description">Dein Zugangscode dient zum Entsperren Deiner Wallet und zum Schutz des Zugriffs auf Deine Vermögenswerte.</string>
|
||||
<string name="access_code_alert_validation_description">Dein Zugangscode entsperrt und schützt den Zugriff auf Deine Geldbörse.</string>
|
||||
<string name="access_code_alert_validation_ok">Trotzdem verwenden</string>
|
||||
<string name="access_code_alert_validation_title">Dieser Zugangscode kann leicht erraten werden</string>
|
||||
<string name="access_code_check_title">Zugangscode eingeben</string>
|
||||
|
|
@ -49,6 +49,7 @@
|
|||
<string name="account_form_placeholder_new_account">Neues Konto</string>
|
||||
<string name="account_form_title_create">Konto hinzufügen</string>
|
||||
<string name="account_form_title_edit">Konto bearbeiten</string>
|
||||
<string name="account_generic_error_dialog_message">Bitte versuche es später erneut. Sollte das Problem weiterhin bestehen, kontaktiere bitte unseren Support. Wir helfen Dir gerne bei der Lösung.</string>
|
||||
<string name="account_label_tokens_info">%1$s in %2$s</string>
|
||||
<string name="account_main_account_title">Hauptkonto</string>
|
||||
<string name="account_recover_limit_dialog_description">Du hast das Limit von %1$s aktiven Konten bereits überschritten. Archiviere eines zur Wiederherstellung</string>
|
||||
|
|
@ -96,7 +97,7 @@
|
|||
<string name="alert_manage_tokens_unsupported_message">Tokens im %1$s -Netzwerk werden von dieser Karte oder Ring aufgrund einer Firmware-Einschränkung nicht unterstützt.</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">Hast du Probleme beim Scannen deiner Karte oder Ring?</string>
|
||||
<string name="alert_unsupported_card">Diese Karte oder Ring ist für die Zusammenarbeit mit Tangem nicht geeignet</string>
|
||||
<string name="app_settings_access_code_warning">Lege zunächst einen Zugangscode fest, um die Biometrie zu aktivieren.</string>
|
||||
<string name="app_settings_access_code_warning">Lege einen Zugangscode fest, um die Biometrie zu aktivieren.</string>
|
||||
<string name="app_settings_biometrics_footer">Verwende die %1$s um Deine Wallet zu entsperren und sensible Aktionen wie das Signieren von Transaktionen zu bestätigen. Bei Hardware-Wallets ist zum Signieren weiterhin eine Karte oder ein Ring erforderlich.</string>
|
||||
<string name="app_settings_default_fee">Standardgebühr</string>
|
||||
<string name="app_settings_default_fee_footer">Aktiviere die Option Standardgebühr, um die Transaktionsgebühren automatisch festzulegen und die Gebührenseite beim Senden von Geldern zu überspringen. Du kannst bei Bedarf jederzeit zu dieser Seite zurückkehren.</string>
|
||||
|
|
@ -145,10 +146,10 @@
|
|||
<string name="balance_hidden_title">Guthaben sind ausgeblendet</string>
|
||||
<string name="beta_mode_warning_message">Laut den Blockchain-Entwicklern befinden sich der Kaspa-Token derzeit in der Betaphase. Bleibe dran für Updates!</string>
|
||||
<string name="beta_mode_warning_title">Beta-Phase</string>
|
||||
<string name="biometric_disabled_warning_description">Die biometrischen Daten sind auf Deinem Gerät deaktiviert, sodass Du sie nicht zum Entsperren Deine Wallet verwenden kannst. Aktiviere die Biometrie in den Einstellungen Deines Geräts, um diese Methode wieder zu verwenden.</string>
|
||||
<string name="biometric_disabled_warning_description">Die Biometrie ist auf Deinem Gerät deaktiviert, daher kannst Du sie nicht zum Entsperren Deiner Wallets verwenden. Aktiviere die Biometrie in den Geräteeinstellungen, um diese Methode wieder nutzen zu können.</string>
|
||||
<string name="biometric_disabled_warning_title">Biometrische Authentifizierung deaktiviert</string>
|
||||
<string name="biometric_lockout_permanent_warning_description">Bitte Karte oder Ring scannen</string>
|
||||
<string name="biometric_lockout_permanent_warning_description_2">Du hast das Limit an biometrischen Entsperrversuchen erreicht. Bitte entsperren Deine Wallet durch Antippen Deines Geräts oder gib den Zugangscode ein.</string>
|
||||
<string name="biometric_lockout_permanent_warning_description_2">Du hast das Limit an biometrischen Entsperrversuchen erreicht. Bitte entsperre Deine Wallet mit einer Karte/einem Ring oder gib Deinen Zugangscode ein.</string>
|
||||
<string name="biometric_lockout_permanent_warning_title">Biometrische Authentifizierung gesperrt</string>
|
||||
<string name="biometric_lockout_warning_description">Bitte versuche es in 30 Sekunden erneut oder scanne die Karte oder Ring</string>
|
||||
<string name="biometric_lockout_warning_description_2">Die biometrische Anmeldung ist vorübergehend gesperrt. Bitte versuche es in 30 Sekunden erneut oder entsperre Deine Wallet durch Antippen Deines Geräts oder mit einem Zugangscode.</string>
|
||||
|
|
@ -374,6 +375,7 @@
|
|||
<string name="common_terms_and_conditions">Allgemeine Geschäftsbedingungen</string>
|
||||
<string name="common_terms_of_use">Nutzungsbedingungen</string>
|
||||
<string name="common_to">An</string>
|
||||
<string name="common_to_wallet_name">Zu %s</string>
|
||||
<string name="common_today">Heute</string>
|
||||
<plurals name="common_tokens_count">
|
||||
<item quantity="one">%d Token</item>
|
||||
|
|
@ -385,6 +387,7 @@
|
|||
<string name="common_transfer">Überweisung</string>
|
||||
<string name="common_unable_to_load">Die Daten konnten nicht geladen werden…</string>
|
||||
<string name="common_understand">Ich verstehe</string>
|
||||
<string name="common_understand_continue">Ich verstehe, fahre bitte fort.</string>
|
||||
<string name="common_unknown_error">Es ist ein Fehler aufgetreten. Bitte versuche es erneut.</string>
|
||||
<string name="common_unreachable">Nicht erreichbar</string>
|
||||
<string name="common_unstake">Staking beenden</string>
|
||||
|
|
@ -398,6 +401,7 @@
|
|||
<string name="currency_subtitle_expanded">Verfügbare Netzwerke</string>
|
||||
<string name="custom_token_another_account_dialog_description">Die Ableitung Deines Tokens entspricht der Ableitung von %1$s. Dein Token wird diesem Konto gutgeschrieben.</string>
|
||||
<string name="custom_token_another_account_dialog_title">Die Herleitung stammt aus einem anderen Bericht.</string>
|
||||
<string name="custom_token_another_account_snackbar_text">Token hinzugefügt %1$s Konto</string>
|
||||
<string name="custom_token_contract_address_input_title">Vertragsadresse</string>
|
||||
<string name="custom_token_creation_error_invalid_contract_address">Vertragsadresse ist ungültig</string>
|
||||
<string name="custom_token_creation_error_network_not_selected">Bitte wähle das Netzwerk</string>
|
||||
|
|
@ -725,6 +729,7 @@
|
|||
<string name="markets_sort_by_top_gainers_title">Top-Gewinner</string>
|
||||
<string name="markets_sort_by_top_losers_title">Top-Verlierer</string>
|
||||
<string name="markets_sort_by_trending_title">Beliebt</string>
|
||||
<string name="markets_sort_by_yield_mode_title">Ertragsmodus</string>
|
||||
<string name="markets_staking_banner_description_placeholder">Staking ist der einfachste Weg, um Belohnungen für Deine Kryptowährung zu erhalten. %s</string>
|
||||
<string name="markets_staking_banner_title">Verdiene bis zu %s APY</string>
|
||||
<string name="markets_token_added">Token hinzugefügt</string>
|
||||
|
|
@ -800,8 +805,17 @@
|
|||
<string name="mobile_wallet_requires_min_os_warning_title">Mobile Wallet erfordert %1$s oder später</string>
|
||||
<string name="news_all_news">Alle Neuigkeiten</string>
|
||||
<string name="news_like">Gefällt mir</string>
|
||||
<plurals name="news_published_hours_ago">
|
||||
<item quantity="one">%dStunde her</item>
|
||||
<item quantity="other">%dStunden her</item>
|
||||
</plurals>
|
||||
<plurals name="news_published_minutes_ago">
|
||||
<item quantity="one">%dMinute her</item>
|
||||
<item quantity="other">%dMinuten her</item>
|
||||
</plurals>
|
||||
<string name="news_quick_recap">Kurze Zusammenfassung</string>
|
||||
<string name="news_related_news">Verwandte Nachrichten</string>
|
||||
<string name="news_related_tokens">Verwandte Token</string>
|
||||
<string name="news_sources">Quellen</string>
|
||||
<string name="news_stay_in_the_loop">Auf dem Laufenden bleiben</string>
|
||||
<string name="nfc_error_unavailable">NFC ist auf deinem Gerät nicht verfügbar</string>
|
||||
|
|
@ -978,6 +992,7 @@
|
|||
<string name="onramp_currency_other">Andere Währungen</string>
|
||||
<string name="onramp_currency_popular">Beliebte Fiats</string>
|
||||
<string name="onramp_currency_search">Suche nach Währung</string>
|
||||
<string name="onramp_error_transaction_already_processed">Diese Transaktion wurde bereits verarbeitet. Es sind keine weiteren Maßnahmen erforderlich.</string>
|
||||
<string name="onramp_fetching_best_rates">Die besten Preise erzielen...</string>
|
||||
<string name="onramp_instant_status">Sofort</string>
|
||||
<string name="onramp_legal">Durch die Nutzung der Onramp-Funktionalität stimmst Du den %1$s und %2$s des Anbieters zu.</string>
|
||||
|
|
@ -1074,6 +1089,7 @@
|
|||
<string name="reset_card_to_factory_button_title">Karte oder Ring 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 oder Ring nicht verwenden kann, um meinen Zugangscode auf den anderen Karten oder Ringe der aktuellen Wallet wiederherzustellen</string>
|
||||
<string name="reset_card_to_factory_condition_3">Mir ist bewusst, dass ich den Zugang zu meiner Tangem Pay Karte und allen darauf befindlichen Geldern vollständig verliere, ohne die Möglichkeit der Wiederherstellung</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 oder Ring gelöscht. Du kannst die aktuelle Wallet nicht wiederherstellen oder die Karte oder Ring verwenden, um den Zugangscode wiederherzustellen.</string>
|
||||
<string name="reset_card_without_backup_to_factory_message">Beim Zurücksetzen auf die Werkseinstellungen wird die Wallet der ausgewählten Karte oder Ring vollständig gelöscht und aus der App entfernt. Es ist nicht möglich, die aktuelle Wallet wiederherzustellen.</string>
|
||||
<string name="reset_cards_dialog_complete_description">Alle Tangem-Geräte wurden zurückgesetzt.</string>
|
||||
|
|
@ -1227,6 +1243,8 @@
|
|||
<string name="staking_account_initialization_footer">Eine Netzwerkgebühr ist eine kleine Zahlung, die erforderlich ist, um Deine Transaktion auf der Blockchain zu verarbeiten und zu bestätigen.</string>
|
||||
<string name="staking_account_initialization_message">Um mit dem Staking zu beginnen, muss Dein TON-Konto mit einer Transaktion von 1 TON aktiviert werden. Das Guthaben verbleibt auf Deinem Konto, dieser Schritt dient lediglichder aktivierung für das Staking.</string>
|
||||
<string name="staking_account_initialization_title">Kontoaktivierung</string>
|
||||
<string name="staking_alert_network_fee_updated_message">Die Netzwerkgebühr hat sich geändert. Bitte überprüfe den neuen Betrag, bevor Du fortfährst.</string>
|
||||
<string name="staking_alert_network_fee_updated_title">Netzwerkgebühr aktualisiert</string>
|
||||
<string name="staking_amount_requirement_error">Die Anzahl der zu stakenden Krypros muss mindesten %s betragen</string>
|
||||
<string name="staking_amount_tron_integer_error">Der Stakingbetrag wird aufgrund der Netzwerkregeln auf %1$s TRX aufgerundet.</string>
|
||||
<string name="staking_amount_tron_integer_error_unstaking">Der Betrag der unstaked wird, wird aufgrund der Netzwerkregeln auf %1$s TRX gerundet.</string>
|
||||
|
|
@ -1265,6 +1283,7 @@
|
|||
<string name="staking_give_permission_fee_footer">Das Netzwerk erhebt eine Token-Genehmigungsgebühr, um zu überprüfen, ob Du die Verwendung Deines Tokens für das Staking genehmigst.</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_max_amount_requirement_error">Höchstbetrag: %s</string>
|
||||
<string name="staking_migrate">Migrieren</string>
|
||||
<string name="staking_native">Natives Staking</string>
|
||||
<string name="staking_no_validators_error_message">Derzeit sind keine aktiven Validatoren für das Staking verfügbar. Bitte versuchen Sie es später erneut.</string>
|
||||
|
|
@ -1417,6 +1436,7 @@
|
|||
<string name="tangem_pay_freeze_card_success">Ihre Karte ist eingefroren.</string>
|
||||
<string name="tangem_pay_get_help">Hilfe erhalten</string>
|
||||
<string name="tangem_pay_other">Andere</string>
|
||||
<string name="tangem_pay_rooted_device_subtitle">Nicht nutzbar auf gerooteten Geräten</string>
|
||||
<string name="tangem_pay_status_completed">Abgeschlossen</string>
|
||||
<string name="tangem_pay_status_declined">Abgelehnt</string>
|
||||
<string name="tangem_pay_status_pending">Ausstehend</string>
|
||||
|
|
@ -1429,6 +1449,7 @@
|
|||
<string name="tangem_pay_unfreeze_card_failed">Entsperren der Karte fehlgeschlagen. Versuchen Sie es später erneut.</string>
|
||||
<string name="tangem_pay_unfreeze_card_success">Ihre Karte ist entsperrt.</string>
|
||||
<string name="tangem_pay_withdrawal">Abhebung</string>
|
||||
<string name="tangempay_account_unable_to_use_rooted">Auf gerooteten Geräten nicht nutzbar.</string>
|
||||
<string name="tangempay_cancel_kyc">KYC abbrechen</string>
|
||||
<string name="tangempay_card_details_add_funds">Guthaben hinzufügen</string>
|
||||
<string name="tangempay_card_details_add_funds_subtitle">Aufladeoptionen</string>
|
||||
|
|
@ -1466,6 +1487,7 @@
|
|||
<string name="tangempay_card_details_swap_description">Tausche beliebige Vermögenswerte in Deinem Portfolio gegen eine Karte.</string>
|
||||
<string name="tangempay_card_details_title">Kartendetails</string>
|
||||
<string name="tangempay_card_details_unfreeze_card">Karte entsperren</string>
|
||||
<string name="tangempay_card_details_view_pin_code_description">Komm zurück zur App, falls du es vergisst.</string>
|
||||
<string name="tangempay_card_details_view_pin_code_title">Dein PIN-Code</string>
|
||||
<string name="tangempay_card_details_withdraw">Auszahlung</string>
|
||||
<string name="tangempay_card_details_withdraw_error_title">Auszahlung derzeit nicht möglich</string>
|
||||
|
|
@ -1473,6 +1495,7 @@
|
|||
<string name="tangempay_card_details_withdraw_in_progress_title">Auszahlung läuft</string>
|
||||
<string name="tangempay_change_pin_code">PIN-Code ändern</string>
|
||||
<string name="tangempay_come_back_if_forget_pin">Kehren Sie zur App zurück, falls Sie ihn vergessen.</string>
|
||||
<string name="tangempay_factory_settings_warning_title">Mir ist bewusst, dass ich den Zugriff auf meine Tangem Pay Card und alle darauf befindlichen Guthaben vollständig und ohne Möglichkeit der Wiederherstellung verliere.</string>
|
||||
<string name="tangempay_failed_to_issue_card">Kartenausstellung fehlgeschlagen</string>
|
||||
<string name="tangempay_failed_to_issue_card_retry_description">Ein technischer Fehler ist aufgetreten, bitte versuchen Sie es erneut, indem Sie auf die Schaltfläche unten klicken</string>
|
||||
<string name="tangempay_failed_to_issue_card_support_description">Ein technischer Fehler ist aufgetreten, bitte kontaktieren Sie den Support</string>
|
||||
|
|
@ -1491,6 +1514,7 @@
|
|||
<string name="tangempay_kyc_in_progress">KYC in Bearbeitung</string>
|
||||
<string name="tangempay_kyc_in_progress_notification_button">Status anzeigen</string>
|
||||
<string name="tangempay_kyc_in_progress_notification_title">KYC für Tangem Pay in Arbeit</string>
|
||||
<string name="tangempay_kyc_in_progress_popup_description">Über die Schaltflächen unten kannst Du Deinen aktuellen KYC-Status einsehen oder ihn abbrechen.</string>
|
||||
<string name="tangempay_onboarding_banner_description">Holen Sie sich Ihre kostenlose virtuelle Tangem Visa-Karte</string>
|
||||
<string name="tangempay_onboarding_banner_title">Nutzen Sie USDC für alltägliche Zahlungen</string>
|
||||
<string name="tangempay_onboarding_get_card_button_text">Karte erhalten</string>
|
||||
|
|
@ -1507,9 +1531,11 @@
|
|||
<string name="tangempay_service_unavailable_description">Wir beheben ein technisches Problem. Bitte versuchen Sie es später erneut.</string>
|
||||
<string name="tangempay_service_unavailable_title">Service vorübergehend nicht verfügbar</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Daten können derzeit nicht angezeigt werden, Kartenzahlungen funktionieren jedoch weiterhin.</string>
|
||||
<string name="tangempay_sync_needed">Synchronisation erforderlich</string>
|
||||
<string name="tangempay_set_pin_code">Satz \nPIN-Code</string>
|
||||
<string name="tangempay_sync_needed">Nicht synchronisiert</string>
|
||||
<string name="tangempay_sync_needed_restore_access">Zugang wiederherstellen</string>
|
||||
<string name="tangempay_tangem_visa_card">Nutzen Sie USDC für alltägliche Zahlungen</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay ist vorübergehend nicht verfügbar</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay ist vorübergehend nicht erreichbar.</string>
|
||||
<string name="tangempay_title">Tangem Pay</string>
|
||||
<string name="tangempay_use_tangem_device_to_restore_payment_account">Klicken Sie auf die Schaltfläche unten, um den Zugriff wiederherzustellen</string>
|
||||
<string name="tangempay_your_pin_code">Ihr PIN-Code</string>
|
||||
|
|
@ -1976,7 +2002,7 @@
|
|||
<string name="xtz_withdrawal_message_warning">Damit Sie beim nächsten Aufladen Ihrer Brieftasche keine erhöhte Provision zahlen, soll der Betrag um %s XTZ reduziert werden</string>
|
||||
<string name="yield_module_alert_description">Wenn der Yield-Modus aktiviert ist, gehen alle zukünftigen Einzahlungen an diese Adresse an Aave. Du kannst über Dein Guthaben weiterhin frei verfügen.</string>
|
||||
<string name="yield_module_alert_title">Deine %s wird an Aave übermittelt</string>
|
||||
<string name="yield_module_amount_not_transfered_to_aave_title">Die Lieferung von %1$s %2$s an Aave steht noch aus.</string>
|
||||
<string name="yield_module_amount_not_transfered_to_aave_title">Lieferung %1$s %2$s nach Aave</string>
|
||||
<string name="yield_module_approve_needed_notification_cta">Genehmigen</string>
|
||||
<string name="yield_module_approve_needed_notification_description">Die Genehmigung Deines Tokens wurde widerrufen. Erteilen diese erneut, um die Servicefunktionalität fortzusetzen.</string>
|
||||
<string name="yield_module_approve_needed_notification_title">Genehmigung erforderlich</string>
|
||||
|
|
@ -2057,6 +2083,7 @@
|
|||
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Ertragsmodus</string>
|
||||
<string name="yield_module_token_details_earn_notification_processing">Bearbeitung Deiner Einzahlung</string>
|
||||
<string name="yield_module_token_details_earn_notification_title">Ertragsmodus</string>
|
||||
<string name="yield_module_transaction_deploy_contract">Yield-Mode-Vertragsbereitstellung</string>
|
||||
<string name="yield_module_transaction_enter">Ertragsmodus aktivieren</string>
|
||||
<string name="yield_module_transaction_enter_subtitle">%1$s geliefert an Aave</string>
|
||||
<string name="yield_module_transaction_exit">Ertragsmodus deaktiviert</string>
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@
|
|||
<string name="account_unsaved_dialog_message_create">Are you sure you want to discard new account?</string>
|
||||
<string name="account_unsaved_dialog_message_edit">Are you sure you want to discard edits?</string>
|
||||
<string name="account_unsaved_dialog_title">Unsaved Changes</string>
|
||||
<string name="accounts_migration_alert_message">Some custom tokens were moved from “%1$s” to “%2$s” as their derivation belongs to that account.</string>
|
||||
<string name="accounts_migration_alert_title">Some custom tokens were moved</string>
|
||||
<string name="accounts_migration_alert_message">Some custom tokens will be automatically moved from “%1$s” to “%2$s” as their derivation belongs to that account.</string>
|
||||
<string name="accounts_migration_alert_title">Some custom tokens will be automatically moved</string>
|
||||
<string name="action_buttons_buy_empty_search_message">Can’t find your token? Go to the Market section on the main page and add it to your portfolio for purchase</string>
|
||||
<string name="action_buttons_sell_empty_search_message">Can’t find your token? Go to the Market section on the main page and add it to your portfolio for selling.</string>
|
||||
<string name="action_buttons_sell_navigation_bar_title">Sell</string>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
|
|
@ -39,6 +40,9 @@ internal fun YieldSupplyPromoBanner(state: PromoBannerState, modifier: Modifier
|
|||
|
||||
@Composable
|
||||
internal fun YieldSupplyPromoBanner(state: PromoBannerState.Content, modifier: Modifier = Modifier) {
|
||||
LaunchedEffect(state) {
|
||||
state.onPromoShown()
|
||||
}
|
||||
val bgColor = TangemTheme.colors.control.unchecked
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -269,6 +269,7 @@ sealed class TokenItemState {
|
|||
val title: TextReference,
|
||||
val onPromoBannerClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
val onPromoShown: () -> Unit = {},
|
||||
) : PromoBannerState()
|
||||
|
||||
data object Empty : PromoBannerState()
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ sealed class CryptoCurrencyWarning {
|
|||
|
||||
data object MigrationMaticToPol : CryptoCurrencyWarning()
|
||||
|
||||
data object MigrationClore : CryptoCurrencyWarning()
|
||||
|
||||
/**
|
||||
* Shows a warning about an available fee resource for a transaction in several blockchains (ex. Koinos)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -41,16 +41,16 @@ class GetCurrencyWarningsUseCase(
|
|||
|
||||
// don't add here notifications that require async requests
|
||||
return combine(
|
||||
getCoinRelatedWarnings(
|
||||
flow = getCoinRelatedWarnings(
|
||||
userWalletId = userWalletId,
|
||||
networkId = currency.network.id,
|
||||
currencyId = currency.id,
|
||||
derivationPath = derivationPath,
|
||||
isSingleWalletWithTokens = isSingleWalletWithTokens,
|
||||
),
|
||||
flowOf(currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus)),
|
||||
flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)),
|
||||
flowOf(currencyChecksRepository.getFeeResourceAmount(userWalletId, currency.network)),
|
||||
flow2 = flowOf(currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus)),
|
||||
flow3 = flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)),
|
||||
flow4 = flowOf(currencyChecksRepository.getFeeResourceAmount(userWalletId, currency.network)),
|
||||
) { coinRelatedWarnings, maybeRentWarning, maybeEdWarning, maybeFeeResource ->
|
||||
setOfNotNull(
|
||||
maybeRentWarning,
|
||||
|
|
@ -62,6 +62,7 @@ class GetCurrencyWarningsUseCase(
|
|||
getBeaconChainShutdownWarning(rawId = currency.network.id.rawId),
|
||||
getAssetRequirementsWarning(userWalletId = userWalletId, currency = currency),
|
||||
getMigrationFromMaticToPolWarning(currency),
|
||||
getCloreMigrationWarning(currency),
|
||||
)
|
||||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
|
|
@ -261,11 +262,20 @@ class GetCurrencyWarningsUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getCloreMigrationWarning(currency: CryptoCurrency): CryptoCurrencyWarning? {
|
||||
return if (currency.symbol == CLORE_SYMBOL && BlockchainUtils.isClore(currency.network.rawId)) {
|
||||
CryptoCurrencyWarning.MigrationClore
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal?.isZero(): Boolean {
|
||||
return this?.signum() == 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MATIC_SYMBOL = "MATIC"
|
||||
private const val CLORE_SYMBOL = "CLORE"
|
||||
}
|
||||
}
|
||||
|
|
@ -102,14 +102,16 @@ class YieldSupplyGetRewardsBalanceUseCase(
|
|||
}.flowOn(dispatcherProvider.default)
|
||||
|
||||
private fun calculateMinVisibleDecimals(perTickDeltaAbs: BigDecimal, maxDecimals: Int): Int {
|
||||
if (perTickDeltaAbs <= BigDecimal.ZERO) return MIN_DECIMALS
|
||||
val effectiveMin = MIN_DECIMALS.coerceAtMost(maxDecimals)
|
||||
|
||||
if (perTickDeltaAbs <= BigDecimal.ZERO) return effectiveMin
|
||||
|
||||
val perTickAsDouble = perTickDeltaAbs.toDouble()
|
||||
if (perTickAsDouble.isNaN() || perTickAsDouble.isInfinite()) return MIN_DECIMALS
|
||||
if (perTickAsDouble.isNaN() || perTickAsDouble.isInfinite()) return effectiveMin
|
||||
|
||||
val safe = if (perTickAsDouble <= 0.0) EPSILON else perTickAsDouble
|
||||
val raw = ceil(-ln(safe) / LN_10)
|
||||
return raw.toInt().coerceIn(MIN_DECIMALS, maxDecimals)
|
||||
return raw.toInt().coerceIn(effectiveMin, maxDecimals)
|
||||
}
|
||||
|
||||
private fun perTickDelta(amount: BigDecimal, apyFraction: BigDecimal): BigDecimal {
|
||||
|
|
|
|||
|
|
@ -459,4 +459,72 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
|
|||
YieldSupplyGetRewardsBalanceUseCase.FIAT_MAX_DECIMALS,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN token with decimals less than MIN_DECIMALS WHEN invoke THEN does not crash`() = runTest {
|
||||
val network = createNetwork()
|
||||
val tokenId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId(network.rawId),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("low-decimals-token", "0xLowDecimals"),
|
||||
)
|
||||
val tokenWithLowDecimals = CryptoCurrency.Token(
|
||||
id = tokenId,
|
||||
network = network,
|
||||
name = "Low Decimals Token",
|
||||
symbol = "LDT",
|
||||
decimals = 0,
|
||||
iconUrl = null,
|
||||
isCustom = false,
|
||||
contractAddress = "0xLowDecimals",
|
||||
)
|
||||
|
||||
val amount = BigDecimal("100.00")
|
||||
val apy = BigDecimal("10.0")
|
||||
|
||||
val status = CryptoCurrencyStatus(
|
||||
currency = tokenWithLowDecimals,
|
||||
value = CryptoCurrencyStatus.Custom(
|
||||
amount = amount,
|
||||
fiatAmount = null,
|
||||
fiatRate = BigDecimal.ONE,
|
||||
priceChange = null,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = NetworkAddress.Single(
|
||||
NetworkAddress.Address(value = "0xabc", type = NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
),
|
||||
)
|
||||
|
||||
coEvery { repository.getCachedMarkets() } returns listOf(
|
||||
YieldMarketToken(
|
||||
tokenAddress = tokenWithLowDecimals.contractAddress,
|
||||
chainId = 1,
|
||||
apy = apy,
|
||||
isActive = true,
|
||||
maxFeeNative = BigDecimal.ZERO,
|
||||
maxFeeUSD = BigDecimal.ZERO,
|
||||
backendId = "id",
|
||||
),
|
||||
)
|
||||
|
||||
val dispatcherProvider = testDispatcherProvider(this)
|
||||
val useCase = YieldSupplyGetRewardsBalanceUseCase(repository, dispatcherProvider)
|
||||
val appCurrency = AppCurrency.Default
|
||||
|
||||
val deferred = async { useCase(status, appCurrency).take(2).toList() }
|
||||
|
||||
testScheduler.advanceUntilIdle()
|
||||
advanceTimeBy(TICK_MILLIS)
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
val emissions = deferred.await()
|
||||
assertThat(emissions).hasSize(2)
|
||||
assertThat(emissions[0].cryptoBalance).isNotNull()
|
||||
assertThat(emissions[1].cryptoBalance).isNotNull()
|
||||
}
|
||||
}
|
||||
|
|
@ -707,6 +707,11 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
userWalletId: UserWalletId,
|
||||
): Throwable? {
|
||||
val currency = fromToken.currency
|
||||
val blockchain = currency.network.toBlockchain()
|
||||
// Stellar validation removed because swap uses destination = "0" and throws an error
|
||||
if (blockchain == Blockchain.Stellar) {
|
||||
return null
|
||||
}
|
||||
val fee = Fee.Common(
|
||||
amount = Amount(
|
||||
value = when (feeState) {
|
||||
|
|
@ -714,7 +719,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
is TxFeeState.MultipleFeeState -> feeState.normalFee.feeValue
|
||||
is TxFeeState.SingleFeeState -> feeState.fee.feeValue
|
||||
},
|
||||
blockchain = currency.network.toBlockchain(),
|
||||
blockchain = blockchain,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ internal class TokenDetailsNotificationsAnalyticsSender(
|
|||
is TokenDetailsNotification.RequiredTrustlineWarning,
|
||||
is TokenDetailsNotification.KoinosMana,
|
||||
is TokenDetailsNotification.MigrationMaticToPol,
|
||||
is TokenDetailsNotification.MigrationClore,
|
||||
is TokenDetailsNotification.UsedOutdatedData,
|
||||
-> null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,6 +258,11 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
|
|||
subtitle = resourceReference(id = R.string.warning_matic_migration_message),
|
||||
)
|
||||
|
||||
data object MigrationClore : Warning(
|
||||
title = resourceReference(id = R.string.warning_clore_migration_title),
|
||||
subtitle = resourceReference(id = R.string.warning_clore_migration_message),
|
||||
)
|
||||
|
||||
data object UsedOutdatedData : TokenDetailsNotification(
|
||||
config = NotificationConfig(
|
||||
subtitle = resourceReference(R.string.warning_some_token_balances_not_updated),
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ internal class TokenDetailsNotificationConverter(
|
|||
},
|
||||
)
|
||||
is CryptoCurrencyWarning.MigrationMaticToPol -> MigrationMaticToPol
|
||||
is CryptoCurrencyWarning.MigrationClore -> MigrationClore
|
||||
is CryptoCurrencyWarning.UsedOutdatedDataWarning -> UsedOutdatedData
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -24,6 +25,7 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
|||
import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplySetShouldShowMainPromoUseCase
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -61,6 +63,10 @@ internal interface WalletContentClickIntents {
|
|||
|
||||
fun onYieldPromoCloseClick()
|
||||
|
||||
fun onYieldPromoShown(cryptoCurrency: CryptoCurrency)
|
||||
|
||||
fun onYieldPromoClicked(cryptoCurrency: CryptoCurrency)
|
||||
|
||||
fun onAccountExpandClick(account: Account)
|
||||
|
||||
fun onAccountCollapseClick(account: Account)
|
||||
|
|
@ -80,7 +86,7 @@ internal interface WalletContentClickIntents {
|
|||
fun onNFTClick(userWallet: UserWallet)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
@ModelScoped
|
||||
internal class WalletContentClickIntentsImplementor @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
|
|
@ -99,6 +105,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyEnterStatusUseCase: YieldSupplyEnterStatusUseCase,
|
||||
private val yieldSupplySetShouldShowMainPromoUseCase: YieldSupplySetShouldShowMainPromoUseCase,
|
||||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
||||
|
||||
override fun onDetailsClick() {
|
||||
|
|
@ -204,6 +211,27 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onYieldPromoShown(cryptoCurrency: CryptoCurrency) {
|
||||
modelScope.launch(dispatchers.io) {
|
||||
tokenListAnalyticsSender.sendYieldPromoShown(
|
||||
userWalletId = stateHolder.getSelectedWalletId(),
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onYieldPromoClicked(cryptoCurrency: CryptoCurrency) {
|
||||
modelScope.launch(dispatchers.io) {
|
||||
analyticsEventHandler.send(
|
||||
MainScreenAnalyticsEvent.YieldPromoClicked(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccountExpandClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountShowTokens())
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
|
|||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.analytics.CheckIsWalletToppedUpUseCase
|
||||
import com.tangem.domain.analytics.model.WalletBalanceState
|
||||
|
|
@ -34,6 +35,7 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
) {
|
||||
|
||||
private val balanceWasSentMap = mutableMapOf<String, Boolean>()
|
||||
private val yieldPromoShownMap = mutableMapOf<String, Boolean>()
|
||||
private val mutex = Mutex()
|
||||
private val loadingTraces = mutableMapOf<UserWalletId, Trace>()
|
||||
|
||||
|
|
@ -234,6 +236,19 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun sendYieldPromoShown(userWalletId: UserWalletId, token: String, blockchain: String) {
|
||||
val key = "${userWalletId.stringValue}_${blockchain}_$token"
|
||||
if (yieldPromoShownMap[key] == true) return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
MainScreenAnalyticsEvent.YieldPromo(
|
||||
token = token,
|
||||
blockchain = blockchain,
|
||||
),
|
||||
)
|
||||
yieldPromoShownMap[key] = true
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val BALANCE_LOADED_TRACE_NAME = "Total_balance_loaded"
|
||||
const val HAS_ERROR = "has_error"
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ internal class TokenListStateConverter(
|
|||
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
|
||||
onApyLabelClick = { status, apySource, apy -> onApyLabelClick(status, apySource, apy) },
|
||||
onYieldPromoCloseClick = clickIntents::onYieldPromoCloseClick,
|
||||
onYieldPromoShown = clickIntents::onYieldPromoShown,
|
||||
onYieldPromoClicked = clickIntents::onYieldPromoClicked,
|
||||
)
|
||||
|
||||
override fun convert(value: WalletTokensListState): WalletTokensListState {
|
||||
|
|
|
|||
|
|
@ -115,6 +115,10 @@ object BlockchainUtils {
|
|||
return blockchain == Blockchain.BSC || blockchain == Blockchain.BSCTestnet
|
||||
}
|
||||
|
||||
fun isClore(blockchainId: String): Boolean {
|
||||
return Blockchain.fromId(blockchainId) == Blockchain.Clore
|
||||
}
|
||||
|
||||
data class BlockchainInfo(
|
||||
val blockchainId: String,
|
||||
val name: String,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue