diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 328c13826f..d52c8382e9 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 328c13826f7e52ba783db1ba39de5b17b1b79bc5 +Subproject commit d52c8382e9e94b2b44ceecd7cb7bd897e091073c diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt index 0807a628f1..6753cdf12a 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt @@ -152,8 +152,8 @@ internal object StakingDomainModule { fun provideGetPendingTransactionsUseCase( stakingPendingTransactionRepository: StakingPendingTransactionRepository, stakingErrorResolver: StakingErrorResolver, - ): GetPendingTransactionsUseCase { - return GetPendingTransactionsUseCase( + ): GetStakingPendingTransactionsUseCase { + return GetStakingPendingTransactionsUseCase( stakingPendingTransactionRepository = stakingPendingTransactionRepository, stakingErrorResolver = stakingErrorResolver, ) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index cc96519ec8..e4f4e75ca9 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -128,7 +128,7 @@ Keine Adresse Jetzt OK - Primär Karte + Hauptkarte Passphrase Einfügen Datenschutzbestimmungen @@ -275,7 +275,7 @@ Keine Token gefunden. Bitte versuche eine andere Anfrage ID: %s Transaktions-ID kopiert - Wechsel eine Ihrer Währungen in eine andere + Tauschen jeden Vermögenswert in Deinem Portfolio gegen diesen Token Die folgenden Angaben sind freiwillig. Du kannst diese löschen, wenn du sie nicht weitergeben möchtest. Teile uns mit, welche Funktionen du vermisst, und wir werden versuchen, dir zu helfen. Bitte sag uns, welche Karte du hast @@ -345,6 +345,7 @@ Das Wallet unterstützt nicht mehr als ein Netzwerk Um mit dem Kauf, Tausch oder Erhalt dieses Vermögenswerts zu beginnen, füge diesen Token zu mindestens 1 Netzwerk hinzu Dieses Asset ist nicht verfügbar + Dieses Asset ist für dieses Wallet nicht verfügbar Zum Portfolio hinzufügen Hinzufügen Verfügbare Netzwerke @@ -561,7 +562,7 @@ %d Wallet %d Wallets - Zurücksetzen der Karte + Karte zurücksetzen Mir ist bewusst, dass ich nach der Durchführung dieser Aktion keinen Zugriff mehr auf die aktuelle Wallet habe. Mir ist klar, dass ich diese Karte nicht verwenden kann, um meinen Zugangscode auf den anderen Karten der aktuellen Wallet wiederherzustellen 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. @@ -686,7 +687,7 @@ Der Zeitraum, den du nach der Beantragung der Abhebung von Geldern aus dem Staking warten musst, bevor die Token verfügbar werden. Aufwärmphase Die zugewiesene Zeit für die Aktivierung der Teilnahme am Staking. - Indem Du die Staking-Funktionalität nutzen, stimmst Du den %1$s und %2$s des Anbieters zu. + Indem Du die Staking-Funktionalität nutzt, stimmst Du den %1$s und %2$s des Anbieters zu. Gesperrt Migrieren Natives Staking @@ -718,14 +719,14 @@ Neuwahl Automatisch Händisch - Block - Tag + Pro Block + Täglich Täglich - Epoche - Ära - Stunde - Monat - Woche + Pro Epoche + Pro Ära + Stündlich + Monatlich + Wöchentlich Belohnungen Stake gesperrt Mehr staken diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index aeed4388a0..9066c1ebb1 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -342,6 +342,7 @@ ウォレットは複数のネットワークをサポートしていません。 このアセットの購入、交換、受け取りを開始するには、ポートフォリオに追加してください。 このアセットは利用できません + このアセットはこのウォレットでは使用できません。 ポートフォリオに追加 追加 利用可能なネットワーク @@ -799,6 +800,7 @@ オペレーション 送金元: %s 送金先: %s + バリデーター: %s もう一度やり直してください 同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。 間違ったツインカードをスキャンしました。別のカードをお試しください。 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index c7e2221f9d..6f25020e8d 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -434,7 +434,7 @@ Объем торгов (24ч) Общая сумма криптовалюты, которая была продана за последние 24 часа, показывающая уровень активности и ликвидности на рынке. Объем торгов (24ч) - Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из маркета + Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка Добавить токены Функция NFC недоступна на вашем устройстве Вам необходимо установить единый код доступа для защиты всех ваших карт @@ -744,6 +744,7 @@ Застейкать еще Вы стейкаете %1$s и будете получать %2$s ежегодно Нажмите для разблокировки + Нажмите для разблокировки или голосования Нажмите для вывода Застейкать %s Вывести %s diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index ef2696c0e6..611026a7cb 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -141,7 +141,7 @@ Детальніше Отримати Відхилити - Перезавантажити + Оновити Перейменувати Зберегти Зберегти зміни @@ -351,6 +351,7 @@ Гаманець не підтримує більше однієї мережі Щоб почати купувати, обмінювати або отримувати цей актив, додайте цей токен принаймні в 1 мережу Цей актив недоступний + Цей токен недоступний для цього гаманця Додати в портфоліо Додати Доступні мережі @@ -401,6 +402,7 @@ Досвідчені покупці Повн. розведена кап. Загальна теоретична вартість криптовалюти, якщо всі монети, які могли б існувати, перебувають в обігу, включаючи ті, що не перебувають в обігу в даний час + Повністю розведена капіталізація Дата створення Високий Тримачі @@ -415,9 +417,11 @@ Низький Рин. кап. Загальна ринкова вартість криптовалюти, що розраховується шляхом множення поточної ціни монети на загальну кількість монет в обігу + Ринкова капіталізація Рейтинг Позиція в крипторейтингу між усіма монетами на основі ринкової капіталізації Рейтинг + Макс. обсяг Максимальна кількість монет або токенів, яка може коли-небудь існувати для певної криптовалюти Максимальна пропозиція Метрики @@ -713,6 +717,7 @@ Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щотиждня. Стейкайте безпечно та почніть отримувати винагороди щотижня Отримуйте винагороду за стейкінг + Стейкінг тимчасово недоступний через проблеми в мережі. Будь ласка, спробуйте пізніше. Стейкінг в мережі %1$s з новим валідатором автоматично переведе всі раніше застейкані кошти до цього валідатора Реінвестуйте зароблені винагороди у суму стейкінгу, щоб збільшити потенційний прибуток. Розблокуйте свої кошти, щоб вивести їх зі стейкінгу. Розблокування займе %s. @@ -813,12 +818,13 @@ Обміняти контракт: %s У вас ще немає транзакцій - Не вдалося завантажити історію транзакцій.\nНатисніть кнопку перезавантаження, щоб оновити інформацію. + Не вдалося завантажити історію транзакцій.\nНатисніть кнопку нижче, щоб оновити інформацію Кілька адрес Історія транзакцій наразі не підтримується для цього блокчейну. Але не хвилюйтеся, ми працюємо над цим! А поки можете перевірити це в оглядачі. Операція від: %s до: %s + валідатор: %s Спробуйте знову Ви відсканували одну й ту саму картку. Для створення twin-гаманця вам потрібно відсканувати картку з номером %d Ви відсканували не ту twin-картку. Будь ласка, спробуйте відсканувати іншу diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index a75c4fce68..3e97f225bf 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -128,7 +128,7 @@ No address Now OK - Primary Card + Primary card Passphrase Paste Privacy Policy @@ -561,7 +561,7 @@ %d wallet %d wallets - Reset the Card + Reset the card I understand that after performing this action, I will no longer have access to the current wallet I realize that I can\'t use this card to recover my access code on the other cards of the current wallet 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. @@ -577,7 +577,7 @@ Enable biometric authorization Would you like to use biometrics? Note that making a transaction with your funds will still require your card - Scan Card + Scan card 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. Get your card ready! Already included in the entered address @@ -731,6 +731,7 @@ Stake more You stake %1$s and will be receiving %2$s yearly Tap to unlock + Tap to unlock or vote Tap to withdraw Stake %s Unstake %s @@ -831,7 +832,7 @@ This wallet has already been saved, you can add another one The wallet with name %s already exists Wallet name - Rename Wallet + Rename wallet Unlock all Unlock all with %s Blockchain is unreachable. Try later diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index 59fe1fa692..d014d038e0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -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 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_sui_22.xml b/core/ui/src/main/res/drawable/ic_sui_22.xml new file mode 100644 index 0000000000..c6595dd4d7 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_sui_22.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/img_sui_22.xml b/core/ui/src/main/res/drawable/img_sui_22.xml new file mode 100644 index 0000000000..750a7815c8 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_sui_22.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt index 49b58cb102..955401f019 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt @@ -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> = 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, + isFirstBatchFetching: Boolean, + ): BatchFetchResult.Success> { + 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, + ): BatchFetchResult.Success> { + 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" diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt index d5d9009ec3..5433dc0701 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt @@ -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, ) } diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt index 5a5b9fd6eb..ffdbb9e58d 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt @@ -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 { - 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 { + 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 = 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" } diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt index 0b637744ab..008ea7161a 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt @@ -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() + private val pendingTransactionsMap = ConcurrentHashMap>() - 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) { - pendingTransactions.removeAll(transactions) + override fun removeTransactions(userWalletId: UserWalletId, transactions: Set) { + pendingTransactionsMap[userWalletId]?.removeAll(transactions) } - override fun getTransactionsWithBalanceItems(): List> { - return pendingTransactions.mapNotNull { pendingTransaction -> + override fun getTransactionsWithBalanceItems( + userWalletId: UserWalletId, + ): List> { + return pendingTransactionsMap[userWalletId]?.mapNotNull { pendingTransaction: PendingTransaction -> PendingTransactionItemConverter.convert(pendingTransaction)?.let { balanceItem -> pendingTransaction to balanceItem } - } + } ?: emptyList() } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index a1fe27bb29..3be43d56c8 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -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) diff --git a/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt index a18b61f591..6169fc2b96 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt @@ -10,8 +10,8 @@ internal object PendingTransactionItemConverter : Converter { @@ -27,9 +26,7 @@ internal class YieldBalanceConverter : Converter> { + operator fun invoke(userWalletId: UserWalletId): Either> { return Either.catch { - stakingPendingTransactionRepository.getTransactionsWithBalanceItems().map { it.second } + stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId).map { it.second } }.mapLeft { stakingErrorResolver.resolve(it) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt index daddce0ad9..b7c4c597ef 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt @@ -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> { + operator fun invoke( + userWalletId: UserWalletId, + balanceItems: List, + balancesId: Int, + ): Either> { 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, - pending: List>, + realData: List, + newBalancesId: Int, + pendingData: List>, ): Pair, List> { - 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() + val transactionsToRemove = mutableListOf() - 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 } } \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt index 64ce855ae5..ef80e06e64 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt @@ -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 { + operator fun invoke(userWalletId: UserWalletId, transaction: PendingTransaction): Either { return Either.catch { - stakingPendingTransactionRepository.saveTransaction(pendingTransaction) + stakingPendingTransactionRepository.saveTransaction( + userWalletId = userWalletId, + transaction = transaction, + ) }.mapLeft { stakingErrorResolver.resolve(it) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt index 02bb96806b..708b8e1352 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt @@ -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> + fun getTransactionsWithBalanceItems(userWalletId: UserWalletId): List> - fun saveTransaction(transaction: PendingTransaction) + fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction) - fun removeTransactions(transactions: Set) + fun removeTransactions(userWalletId: UserWalletId, transactions: Set) } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt index c989a34d8c..8472172758 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt @@ -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), - ) } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt index c490284116..4bc8b84bda 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt @@ -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( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt index 584fafdd1f..045baf45e0 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt @@ -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( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt index 5c82de46c3..fc1f8a834e 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt @@ -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( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index 57f98b373b..b1f5017c82 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -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, - 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, + transactions: List, transactionHashes: List, - 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, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt index 51b7ec69a2..e68d171b89 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt @@ -93,6 +93,6 @@ internal object ConfirmationStatePreviewData { isApprovalNeeded = false, reduceAmountBy = null, pendingActions = null, - balanceState = null, + possiblePendingTransaction = null, ) } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt index 30877033e3..9317d4fd6d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt @@ -273,17 +273,7 @@ internal class AddStakingNotificationsTransformer( } private fun MutableList.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.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.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), + ), + ) + } + } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt deleted file mode 100644 index d6fae7cd7f..0000000000 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt +++ /dev/null @@ -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 { - - override fun transform(prevState: StakingUiState): StakingUiState { - val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data - ?: return prevState - - return prevState.copy( - confirmationState = possibleConfirmationState.copy(balanceState = balanceState), - ) - } -} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt index d9f2906670..ea4406d986 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt @@ -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 } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt index c2df5db972..60e1fd090c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt @@ -20,9 +20,12 @@ internal class SetConfirmationStateLoadingTransformer( ) : Transformer { 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, ), ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt index ee866db72c..a3b203d9eb 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt @@ -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 { @@ -13,7 +14,7 @@ internal object SetConfirmationStateResetAssentTransformer : Transformer { + + 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, + ), + ), + ) + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt index 8fc799719a..bcf6eb3cc3 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt @@ -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) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index b138f2c300..2c0c1833ed 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -136,9 +136,12 @@ internal class StakingViewModel @Inject constructor( private val balancesToShow: List 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, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index dafdd53e8c..14687af414 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -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, private val appCurrencyProvider: Provider, private val stakingEntryInfoProvider: Provider, + private val pendingBalancesProvider: Provider>, 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) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 934b75a0d1..67a0d59d99 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -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, private val stakingEntryInfoProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val pendingBalancesProvider: Provider>, 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, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 5d65adaf1c..fc41e514a8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -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 = createSelectedAppCurrencyFlow() + private val stakingPendingBalances: List + 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, diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e71e76d7c8..ab6f07950a 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -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 ^ diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt index 6c71737687..ed07736123 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt @@ -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" } }