diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 522132b11b..56f11d7388 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -1362,7 +1362,7 @@
Frühzeitiger Zugriff auf neue Funktionen und exklusive Angebote.
Updates zu Funktionen und Neuigkeiten
Möchtest du Push-Benachrichtigungen verwenden?
- Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen \n
+ Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen
Verpasse keine Transaktion
Neues Wallet hinzufügen
Möchtest Du diese Wallet wirklich entfernen?
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index dcce73dd3a..6a2f5f1caf 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -1212,6 +1212,8 @@
Acceso anticipado a nuevas funciones y ofertas exclusivas.
Actualizaciones de características y noticias
¿Quiere utilizar\nnotificaciones push?
+ Active las notificaciones push y le avisaremos al instante cuando le lleguen fondos.
+ No se pierda ninguna transacción
Agregar una nueva billetera
¿Estás seguro de que deseas olvidar esta billetera?
Ha ocurrido un error, por favor escanee su tarjeta o anillo para iniciar sesión
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index 004c7dd622..868ad2f695 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -1185,6 +1185,8 @@
Accès anticipé à de nouvelles fonctionnalités et à des offres exclusives.
Actualités et mises à jour
Souhaitez-vous utiliser les\nnotifications push?
+ Activez les notifications pour recevoir des alertes lorsque des fonds arrivent dans votre portefeuille.
+ Ne manquez aucune transaction
Ajouter un nouveau portefeuille
Êtes-vous sûr de vouloir supprimer ce portefeuille ?
Une erreur s\'est produite, veuillez scanner votre carte ou bague pour vous connecter
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 4a69572db8..282551cd56 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -134,7 +134,7 @@
お使いの携帯電話で生体認証が無効になっているため、アプリにウォレットを保存できません。ウォレットを保存するには、携帯電話の設定で生体認証機能を有効にしてください。
プロモーションコードの処理中にエラーが発生しました。しばらくしてからもう一度お試しください。
アクティベーションエラー
- プロモコードが正常に有効化されました。リワードは14日以内にあなたのアカウントに付与されます。
+ プロモーションコードが正常に有効化されました。報酬は14日以内にビットコインアカウントに入金されます。
プロモーションコードが有効になりました
このプロモーションコードは既に使用されているため、再度使うことはできません。
コードが利用できません
@@ -180,6 +180,7 @@
追加
ポートフォリオに追加
トークンを追加
+ 追加済み
アドレス
すべて
許可する
@@ -291,6 +292,7 @@
%1$s — %2$s
続きを読む
受け取る
+ おすすめ
拒否
リロード
名前を変更
@@ -347,6 +349,8 @@
はい
コントラクトアドレスをコピーしました!
利用可能なネットワーク
+ このトークンの導出パスが%1$sの導出パスと一致しています。トークンはこのアカウントに追加されます。
+ 導出パスは別のアカウントに属します。
コントラクトアドレス
コントラクトアドレスが無効です
ネットワークを選択してください
@@ -464,6 +468,7 @@
プロバイダー
ベストレート
FCA警告リスト
+ お得なレート
FCA警告リストに掲載されたプロバイダー
最大 %s まで使用可能
%s 以上で利用可能
@@ -1361,9 +1366,10 @@
新しい機能や限定オファーへの早期アクセス。
機能およびニュースのアップデート
プッシュ通知を使用しますか?
- プッシュ通知を有効にすると、資金が到着するとすぐに通知されます
+ プッシュ通知を有効にすると、ウォレットに着金したときにアラートを受信できます。
取引を見逃さない
新しいウォレットを追加
+ バックアップせずにこのウォレットを削除すると、資金に永久にアクセスできなくなります。
このウォレットを忘れてもよろしいですか?
エラーが発生しました。カードまたはリングをスキャンしてログインしてください。
このウォレットはすでに保存されています。別のウォレットを追加できます。
@@ -1393,6 +1399,13 @@
ロック解除
カードをスキャンしてアクセスロックを解除する
ロック解除が必要
+ ウォレットの追加方法を選択してください
+ Tangemカードまたはリングをスキャンして復元するか、別のウォレットからインポートしてください。
+ ハードウェアウォレットを作成
+ Tangemウォレットを購入しますか?
+ シードフレーズをインポート
+ スマートフォン上でウォレットを復元するか、他のアプリからインポートできますが、Tangemカードと比べるとセキュリティは劣ります。
+ お選びください
ブロックチェーンにアクセスできません。後でもう一度お試しください。
カードまたはリングをスキャン
このウォレットはすでに有効化されています。\nあなたが行ったのでない場合は、サポートに連絡してください。\nTangemは、事前に生成されたアクセスコードと一緒にウォレットを販売することはありません。
@@ -1432,6 +1445,7 @@
dAppsに接続する
WalletConnect
接続には数秒かかる場合があります
+ Tangemウォレットを作成する
%sから
Tangemウォレットを購入ーこれは、あなたの秘密鍵をオフラインで安全に保管する物理デバイスです。
ハードウェアウォレット
@@ -1657,13 +1671,20 @@
URIはすでに使用されています
WalletConnect
不審な取引
+ すでにTangemをお持ちですか?
+ 数千種類の資産
業界最高水準のハードウェアウォレット
迅速な配送
+ ワンタップで開始
+ シームレスで安全
シンプルな操作
Tangemでハードウェアウォレットを作成しましょう。銀行のカードのようにスリムで、金庫のように安全です。
ソフトウェアウォレットを作成またはインポート
+ スマートフォン上にソフトウェアウォレットを作成するか、インポートしてください。
モバイルウォレットから始める
その他の方法
+ Tangemハードウェアウォレットを使用する
+ 詳細を見る・購入する
破棄
バックアップが中断されました。再開しますか?
はい、再開します
@@ -1688,6 +1709,7 @@
承認を確定する
資産残高に関するテキスト [プレースホルダー]
あなたの%sはAaveに預けられています
+ チャートを読み込めません・・
受け取った金額%1$s %2$sはAaveに入金されませんでした。
%1$s%% を獲得
利用可能
@@ -1705,6 +1727,7 @@
最大手数料
手数料ポリシー
ネットワーク手数料が現在高すぎます。設定した上限を下回るまで待機しています。
+ 過去のリターン
ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー]
トークン承認が必要
ネットワーク接続を確認してください
@@ -1738,7 +1761,8 @@
供給APR
APY
あなたの資産を眠らせない — 残高を運用して利息を得ましょう。
- 保有資産から報酬を得る
+ 利息は自動的に発生します
+ Aaveレンディング
入金の処理中
年間%1$s%%の収益
自動
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 6d0accb1ee..c55cacce85 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -80,7 +80,7 @@
Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона.
При обработке промокода произошла ошибка. Пожалуйста, попробуйте позже.
Ошибка активации
- Ваш промокод был успешно активирован. Награда будет зачислена на ваш счёт в течение 14 дней.
+ Ваш промокод был успешно активирован. Награда будет зачислена на ваш Bitcoin адрес в течение 14 дней.
Промокод активирован
Этот промокод уже был использован и не может быть активирован повторно.
Код недоступен
@@ -1288,6 +1288,8 @@
Ранний доступ к новым функциям и эксклюзивным предложениям.
Новые функции и важные новости
Хотите использовать Push-уведомления?
+ Включите push-уведомления, и мы мгновенно сообщим вам, когда поступят средства.
+ Не пропустите транзакцию
Добавить новый кошелек
Вы уверены, что хотите забыть этот кошелек?
Произошла ошибка, пожалуйста, отсканируйте свою карту или кольцо для входа
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 50216b2d4c..3e4c4ec8a8 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -135,7 +135,7 @@
You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings.
An error occurred while processing your promo code. Please try again later.
Activation error
- Your promo code was successfully activated. A reward will be credited to your account within 14 days.
+ Your promo code was successfully activated. The reward will be credited to your Bitcoin account within 14 days.
Promo Code Activated
This promo code has already been used and cannot be activated again.
Code unavailable
@@ -183,6 +183,7 @@
Add
Add to portfolio
Add token
+ Added
Address
All
Allow
@@ -297,6 +298,7 @@
%1$s — %2$s
Read more
Receive
+ Recommended
Reject
Reload
Rename
@@ -474,6 +476,7 @@
Provider
Best rate
FCA Warning List
+ Great rate
Provider in FCA warning list
Available up to %s
Available from %s
@@ -1386,9 +1389,10 @@
Early access to fresh features and exclusive offers.
Feature and News Updates
Would you like to use\nPush-notifications?
- Enable push notifications and we’ll notify you instantly when funds arrive\n
- Don’t Miss a Transaction
+ Enable push notifications to receive alerts when funds arrive in your wallet.
+ Don\'t Miss a Transaction
Add new wallet
+ If you delete this wallet without a backup, you will permanently lose access to your funds
Are you sure you want to forget this wallet?
An error has occurred, please scan your card or ring to log in
This wallet has already been saved, you can add another one
@@ -1465,6 +1469,13 @@
Unlock
Scan your card to unlock access
Needed unlock
+ Choose how to add your wallet
+ Scan your Tangem card or ring to restore it or import from another wallet.
+ Create Hardware Wallet
+ Want to purchase a Tangem Wallet?
+ Import seed phrase
+ Restore your wallet on your phone or import from another app — convenient, but less secure than a Tangem card.
+ What to choose?
Blockchain is unreachable. Try later
Scan card or ring
This wallet has already been activated earlier.\nIf it was not done by you, please contact support.\nTangem never sells wallets along with pre-generated access codes.
@@ -1504,6 +1515,7 @@
Connect to dApps
WalletConnect
Connecting may take a few seconds
+ Create Tangem Wallet
From %s
Buy Tangem Wallet—a physical device that securely stores your private key offline.
Hardware Wallet
@@ -1785,7 +1797,10 @@
All future %s top-ups will be supplied to Aave automatically, with the transaction fee deducted.
If network fees rise above maximum fee, the transaction won’t go through until they decrease. You can change this limit later.
Maximum fee
+ The transaction fee must stay below 4% of your deposit. Tangem will transfer funds to Aave only once your balance is large enough to meet this condition.
+ Minimal amount
Fee policy
+ Tangem also takes a 3% service fee on the yield earned.
Network fee is too high right now. Waiting until it falls below your limit.
Historical returns
Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]
@@ -1821,7 +1836,8 @@
Supply APR
APY
Make your money work — earn interest on your balance.
- Earning on your balance
+ Interest accrues automatically
+ Aave lending
Processing your deposit
Earn %1$s%% per year
Automatic
diff --git a/domain/account/status/src/test/java/com/tangem/domain/account/status/producer/DefaultMultiAccountStatusListProducerTest.kt b/domain/account/status/src/test/java/com/tangem/domain/account/status/producer/DefaultMultiAccountStatusListProducerTest.kt
deleted file mode 100644
index a3becc8abf..0000000000
--- a/domain/account/status/src/test/java/com/tangem/domain/account/status/producer/DefaultMultiAccountStatusListProducerTest.kt
+++ /dev/null
@@ -1,198 +0,0 @@
-package com.tangem.domain.account.status.producer
-
-import com.google.common.truth.Truth
-import com.tangem.common.test.utils.getEmittedValues
-import com.tangem.domain.account.models.AccountStatusList
-import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
-import com.tangem.domain.common.wallets.UserWalletsListRepository
-import com.tangem.domain.models.wallet.UserWallet
-import com.tangem.domain.models.wallet.UserWalletId
-import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
-import io.mockk.*
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.TestInstance
-
-@Suppress("UnusedFlow")
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-class DefaultMultiAccountStatusListProducerTest {
-
- private val userWalletsListRepository: UserWalletsListRepository = mockk()
- private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier = mockk()
- private val dispatchers = TestingCoroutineDispatcherProvider()
-
- private val userWalletId1 = UserWalletId("001")
- private val userWallet1 = mockk {
- every { walletId } returns userWalletId1
- }
-
- private val userWalletId2 = UserWalletId("002")
- private val userWallet2 = mockk {
- every { walletId } returns userWalletId2
- }
-
- private val producer = DefaultMultiAccountStatusListProducer(
- params = Unit,
- userWalletsListRepository = userWalletsListRepository,
- singleAccountStatusListSupplier = singleAccountStatusListSupplier,
- dispatchers = dispatchers,
- )
-
- @AfterEach
- fun tearDown() {
- clearMocks(userWalletsListRepository, singleAccountStatusListSupplier)
- }
-
- @Test
- fun `produce returns status lists for all user wallets`() = runTest {
- // Arrange
- val wallets = listOf(userWallet1, userWallet2)
- val walletsFlow = MutableStateFlow(wallets)
-
- every { userWalletsListRepository.userWallets } returns walletsFlow
- val accountStatusList1 = mockk()
- val accountStatusList2 = mockk()
-
- every {
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId1),
- )
- } returns flowOf(accountStatusList1)
-
- every {
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId2),
- )
- } returns flowOf(accountStatusList2)
-
- // Act
- val actual = producer.produce().let(::getEmittedValues)
-
- // Assert
- val expected = listOf(accountStatusList1, accountStatusList2)
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- userWalletsListRepository.userWallets
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId1),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId2),
- )
- }
- }
-
- @Test
- fun `produce returns empty flow if userWallets is empty list`() = runTest {
- // Arrange
- val walletsFlow = MutableStateFlow>(emptyList())
- every { userWalletsListRepository.userWallets } returns walletsFlow
-
- // Act
- val actual = producer.produce().let(::getEmittedValues)
-
- // Assert
- Truth.assertThat(actual).isEmpty()
-
- coVerify(ordering = Ordering.SEQUENCE) {
- userWalletsListRepository.userWallets
- }
- }
-
- @Test
- fun `produce returns empty flow if userWallets is null`() = runTest {
- // Arrange
- val walletsFlow = MutableStateFlow?>(null)
- every { userWalletsListRepository.userWallets } returns walletsFlow
-
- // Act
- val actual = producer.produce().let(::getEmittedValues)
-
- // Assert
- Truth.assertThat(actual).isEmpty()
-
- coVerify(ordering = Ordering.SEQUENCE) {
- userWalletsListRepository.userWallets
- }
- }
-
- @Test
- fun `flow will updated if userWallets are updated`() = runTest {
- // Arrange
- val userWalletId3 = UserWalletId("003")
- val userWallet3 = mockk { every { walletId } returns userWalletId3 }
-
- val walletsFlow = MutableStateFlow(listOf(userWallet1, userWallet2))
- every { userWalletsListRepository.userWallets } returns walletsFlow
-
- val accountStatusList1 = mockk()
- val accountStatusList2 = mockk()
- val accountStatusList3 = mockk()
-
- every {
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId1),
- )
- } returns flowOf(accountStatusList1)
-
- every {
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId2),
- )
- } returns flowOf(accountStatusList2)
-
- every {
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId3),
- )
- } returns flowOf(accountStatusList3)
-
- // Act (first emission)
- val actual1 = producer.produce().let(::getEmittedValues)
-
- // Assert (first emission)
- val expected1 = listOf(accountStatusList1, accountStatusList2)
- Truth.assertThat(actual1).containsExactly(expected1)
-
- // Act (second emission)
- walletsFlow.value = listOf(userWallet1, userWallet2, userWallet3)
- val actual2 = producer.produce().let(::getEmittedValues)
-
- // Assert (second emission)
- val expected2 = listOf(accountStatusList1, accountStatusList2, accountStatusList3)
- Truth.assertThat(actual2).containsExactly(expected2)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- userWalletsListRepository.userWallets
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId1),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId2),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId1),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId2),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId3),
- )
- userWalletsListRepository.userWallets
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId1),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId2),
- )
- singleAccountStatusListSupplier(
- params = SingleAccountStatusListProducer.Params(userWalletId3),
- )
- }
- }
-}
\ No newline at end of file
diff --git a/domain/account/status/src/test/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt b/domain/account/status/src/test/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt
deleted file mode 100644
index ca151e1478..0000000000
--- a/domain/account/status/src/test/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt
+++ /dev/null
@@ -1,244 +0,0 @@
-package com.tangem.domain.account.status.producer
-
-import arrow.core.nonEmptyListOf
-import com.google.common.truth.Truth
-import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
-import com.tangem.common.test.utils.getEmittedValues
-import com.tangem.domain.account.models.AccountList
-import com.tangem.domain.account.models.AccountStatusList
-import com.tangem.domain.account.producer.SingleAccountListProducer
-import com.tangem.domain.account.status.utils.CryptoCurrencyStatusesFlowFactory
-import com.tangem.domain.account.supplier.SingleAccountListSupplier
-import com.tangem.domain.core.utils.lceContent
-import com.tangem.domain.core.utils.lceLoading
-import com.tangem.domain.models.StatusSource
-import com.tangem.domain.models.TokensSortType
-import com.tangem.domain.models.TotalFiatBalance
-import com.tangem.domain.models.account.AccountStatus
-import com.tangem.domain.models.currency.CryptoCurrencyStatus
-import com.tangem.domain.models.quote.PriceChange
-import com.tangem.domain.models.tokenlist.TokenList
-import com.tangem.domain.models.wallet.UserWallet
-import com.tangem.domain.models.wallet.UserWalletId
-import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
-import io.mockk.*
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.TestInstance
-import java.math.BigDecimal
-
-/**
-[REDACTED_AUTHOR]
- */
-@Suppress("UnusedFlow")
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-class DefaultSingleAccountStatusListProducerTest {
-
- private val singleAccountListSupplier: SingleAccountListSupplier = mockk()
- private val cryptoCurrencyStatusesFlowFactory: CryptoCurrencyStatusesFlowFactory = mockk()
-
- private val userWalletId = UserWalletId("011")
- private val userWallet = mockk {
- every { this@mockk.walletId } returns userWalletId
- }
-
- private val producer = DefaultSingleAccountStatusListProducer(
- params = SingleAccountStatusListProducer.Params(userWalletId),
- singleAccountListSupplier = singleAccountListSupplier,
- cryptoCurrencyStatusesFlowFactory = cryptoCurrencyStatusesFlowFactory,
- dispatchers = TestingCoroutineDispatcherProvider(),
- )
-
- @AfterEach
- fun tearDown() {
- clearMocks(singleAccountListSupplier, cryptoCurrencyStatusesFlowFactory)
- }
-
- @Test
- fun `flow is mapped for user wallet id from params`() = runTest {
- // Arrange
- val accountList = AccountList.empty(userWallet)
-
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns flowOf(accountList)
-
- // Act
- val actual = producer.produce().let(::getEmittedValues)
-
- // Assert
- val expected = AccountStatusList(
- userWallet = userWallet,
- accountStatuses = setOf(
- AccountStatus.CryptoPortfolio(
- account = accountList.mainAccount,
- tokenList = TokenList.Empty,
- priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
- ),
- ),
- totalAccounts = 1,
- totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
- )
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- }
- }
-
- @Test
- fun `flow will updated if balances are updated`() = runTest {
- // Arrange
- val accountList = AccountList.empty(userWallet)
- val updatedAccountList = AccountList.empty(userWallet = userWallet, sortType = TokensSortType.BALANCE)
-
- val accountListFlow = MutableStateFlow(value = accountList)
-
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns accountListFlow
-
- // Act (first emission)
- val actual1 = producer.produce().let(::getEmittedValues)
-
- // Assert (first emission)
- val expected = AccountStatusList(
- userWallet = userWallet,
- accountStatuses = setOf(
- AccountStatus.CryptoPortfolio(
- account = accountList.mainAccount,
- tokenList = TokenList.Empty,
- priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
- ),
- ),
- totalAccounts = 1,
- totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
- )
- Truth.assertThat(actual1).containsExactly(expected)
-
- // Act (second emission)
- accountListFlow.value = updatedAccountList
- val actual2 = producer.produce().let(::getEmittedValues)
-
- // Assert (second emission)
- val expected2 = AccountStatusList(
- userWallet = userWallet,
- accountStatuses = setOf(
- AccountStatus.CryptoPortfolio(
- account = updatedAccountList.mainAccount,
- tokenList = TokenList.Empty,
- priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
- ),
- ),
- totalAccounts = 1,
- totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
- )
- Truth.assertThat(actual2).containsExactly(expected2)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- }
- }
-
- @Test
- fun `flow is filtered the same balance`() = runTest {
- // Arrange
- val accountList = AccountList.empty(userWallet)
- val accountListFlow = MutableStateFlow(value = accountList)
-
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns accountListFlow
-
- val expected = AccountStatusList(
- userWallet = userWallet,
- accountStatuses = setOf(
- AccountStatus.CryptoPortfolio(
- account = accountList.mainAccount,
- tokenList = TokenList.Empty,
- priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
- ),
- ),
- totalAccounts = 1,
- totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
- )
-
- // Act (first emission)
- val actual1 = producer.produce().let(::getEmittedValues)
-
- // Assert (first emission)
- Truth.assertThat(actual1).containsExactly(expected)
-
- // Act (second emission)
- accountListFlow.value = accountList
- val actual2 = producer.produce().let(::getEmittedValues)
-
- // Assert (second emission)
- Truth.assertThat(actual2).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- }
- }
-
- @Test
- fun `flow is produced for account with non empty crypto currencies`() = runTest {
- // Arrange
- val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
- val accountList = AccountList.empty(
- userWallet = userWallet,
- cryptoCurrencies = cryptoCurrencyFactory.ethereumAndStellar.toSet(),
- )
-
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns flowOf(accountList)
-
- val ethereumStatus = CryptoCurrencyStatus(
- currency = cryptoCurrencyFactory.ethereum,
- value = CryptoCurrencyStatus.Loading,
- )
- every {
- cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = cryptoCurrencyFactory.ethereum)
- } returns flowOf(ethereumStatus)
-
- val stellarStatus = CryptoCurrencyStatus(
- currency = cryptoCurrencyFactory.stellar,
- value = CryptoCurrencyStatus.MissedDerivation(priceChange = null, fiatRate = null),
- )
- every {
- cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = cryptoCurrencyFactory.stellar)
- } returns flowOf(stellarStatus)
-
- // Act
- val actual = producer.produce().let(::getEmittedValues)
-
- // Assert
- val expected = AccountStatusList(
- userWallet = userWallet,
- accountStatuses = setOf(
- AccountStatus.CryptoPortfolio(
- account = accountList.mainAccount,
- tokenList = TokenList.Ungrouped(
- totalFiatBalance = TotalFiatBalance.Loading,
- sortedBy = TokensSortType.NONE,
- currencies = nonEmptyListOf(ethereumStatus, stellarStatus),
- ),
- priceChangeLce = lceLoading(),
- ),
- ),
- totalAccounts = 1,
- totalFiatBalance = TotalFiatBalance.Loading,
- )
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- }
- }
-}
\ No newline at end of file
diff --git a/domain/account/status/src/test/java/com/tangem/domain/account/status/utils/CryptoCurrencyStatusesFlowFactoryTest.kt b/domain/account/status/src/test/java/com/tangem/domain/account/status/utils/CryptoCurrencyStatusesFlowFactoryTest.kt
deleted file mode 100644
index 0036579daf..0000000000
--- a/domain/account/status/src/test/java/com/tangem/domain/account/status/utils/CryptoCurrencyStatusesFlowFactoryTest.kt
+++ /dev/null
@@ -1,308 +0,0 @@
-package com.tangem.domain.account.status.utils
-
-import arrow.core.left
-import arrow.core.right
-import com.google.common.truth.Truth
-import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
-import com.tangem.common.test.utils.getEmittedValues
-import com.tangem.domain.models.StatusSource
-import com.tangem.domain.models.currency.CryptoCurrency
-import com.tangem.domain.models.currency.CryptoCurrencyStatus
-import com.tangem.domain.models.network.NetworkAddress
-import com.tangem.domain.models.network.NetworkStatus
-import com.tangem.domain.models.quote.QuoteStatus
-import com.tangem.domain.models.staking.StakingID
-import com.tangem.domain.models.staking.YieldBalance
-import com.tangem.domain.models.wallet.UserWallet
-import com.tangem.domain.models.wallet.UserWalletId
-import com.tangem.domain.models.wallet.isMultiCurrency
-import com.tangem.domain.networks.single.SingleNetworkStatusProducer
-import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
-import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
-import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
-import com.tangem.domain.staking.StakingIdFactory
-import com.tangem.domain.staking.single.SingleYieldBalanceProducer
-import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
-import io.mockk.*
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.TestInstance
-import java.math.BigDecimal
-
-/**
-[REDACTED_AUTHOR]
- */
-@Suppress("UnusedFlow")
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-class CryptoCurrencyStatusesFlowFactoryTest {
-
- private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier = mockk()
- private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier = mockk()
- private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier = mockk()
- private val stakingIdFactory: StakingIdFactory = mockk()
-
- private val factory = CryptoCurrencyStatusesFlowFactory(
- singleNetworkStatusSupplier = singleNetworkStatusSupplier,
- singleQuoteStatusSupplier = singleQuoteStatusSupplier,
- singleYieldBalanceSupplier = singleYieldBalanceSupplier,
- stakingIdFactory = stakingIdFactory,
- )
-
- private val userWalletId = UserWalletId("011")
- private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
-
- private val networkAddress = NetworkAddress.Single(
- defaultAddress = NetworkAddress.Address(value = "0x1", type = NetworkAddress.Address.Type.Primary),
- )
-
- @AfterEach
- fun tearDown() {
- clearMocks(
- singleNetworkStatusSupplier,
- singleQuoteStatusSupplier,
- singleYieldBalanceSupplier,
- stakingIdFactory,
- )
- }
-
- @Test
- fun `if rawCurrencyId is null, there will be no subscription to the quote status`() = runTest {
- // Arrange
- val userWallet = mockk {
- every { this@mockk.walletId } returns userWalletId
- every { this@mockk.isMultiCurrency } returns true
- }
-
- val currency = cryptoCurrencyFactory.ethereum.copy(
- id = cryptoCurrencyFactory.ethereum.id.copy(
- suffix = CryptoCurrency.ID.Suffix.ContractAddress(contractAddress = "0x12345"),
- ),
- )
-
- val networkStatus = NetworkStatus(
- network = currency.network,
- value = NetworkStatus.Unreachable(address = networkAddress),
- )
- val networkStatusFlow = flowOf(networkStatus)
- every {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- } returns networkStatusFlow
-
- val stakingId = StakingID(integrationId = "id", address = networkAddress.defaultAddress.value)
- coEvery {
- stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
- } returns stakingId.right()
-
- val yieldBalance = YieldBalance.Empty(stakingId = stakingId, source = StatusSource.ACTUAL)
- val yieldBalanceFlow = flowOf(yieldBalance)
- every {
- singleYieldBalanceSupplier(
- params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
- )
- } returns yieldBalanceFlow
-
- // Act
- val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
-
- // Assert
- val expected = CryptoCurrencyStatus(
- currency = currency,
- value = CryptoCurrencyStatus.Unreachable(
- priceChange = null,
- fiatRate = null,
- networkAddress = networkAddress,
- ),
- )
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
- singleYieldBalanceSupplier(params = SingleYieldBalanceProducer.Params(userWalletId, stakingId))
- }
- }
-
- @Test
- fun `if userWallet is not multi-currency, there will be no subscription to the yield balance`() = runTest {
- // Arrange
- val userWallet = mockk {
- every { this@mockk.walletId } returns userWalletId
- every { this@mockk.isMultiCurrency } returns false
- }
-
- val currency = cryptoCurrencyFactory.ethereum
-
- val networkStatus = NetworkStatus(
- network = currency.network,
- value = NetworkStatus.Unreachable(address = networkAddress),
- )
- val networkStatusFlow = flowOf(networkStatus)
- every {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- } returns networkStatusFlow
-
- val quoteStatus = QuoteStatus(
- rawCurrencyId = currency.id.rawCurrencyId!!,
- value = QuoteStatus.Data(
- source = StatusSource.ACTUAL,
- fiatRate = BigDecimal.ONE,
- priceChange = BigDecimal.ONE,
- ),
- )
- val quoteStatusFlow = flowOf(quoteStatus)
- every {
- singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
- } returns quoteStatusFlow
-
- // Act
- val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
-
- // Assert
- val expected = CryptoCurrencyStatus(
- currency = currency,
- value = CryptoCurrencyStatus.Unreachable(
- priceChange = BigDecimal.ONE,
- fiatRate = BigDecimal.ONE,
- networkAddress = networkAddress,
- ),
- )
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
- }
- }
-
- @Test
- fun `no subscription to the quote status and yield balance`() = runTest {
- // Arrange
- val userWallet = mockk {
- every { this@mockk.walletId } returns userWalletId
- every { this@mockk.isMultiCurrency } returns false
- }
-
- val currency = cryptoCurrencyFactory.ethereum.copy(
- id = cryptoCurrencyFactory.ethereum.id.copy(
- suffix = CryptoCurrency.ID.Suffix.ContractAddress(contractAddress = "0x12345"),
- ),
- )
-
- val networkStatus = NetworkStatus(
- network = currency.network,
- value = NetworkStatus.Unreachable(address = networkAddress),
- )
- val networkStatusFlow = flowOf(networkStatus)
- every {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- } returns networkStatusFlow
-
- // Act
- val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
-
- // Assert
- val expected = CryptoCurrencyStatus(
- currency = currency,
- value = CryptoCurrencyStatus.Unreachable(
- priceChange = null,
- fiatRate = null,
- networkAddress = networkAddress,
- ),
- )
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- }
- }
-
- @Test
- fun `if stakingId is not supported, yield balance will be null`() = runTest {
- // Arrange
- val userWallet = mockk {
- every { this@mockk.walletId } returns userWalletId
- every { this@mockk.isMultiCurrency } returns true
- }
-
- val currency = cryptoCurrencyFactory.ethereum
-
- val networkStatus = NetworkStatus(
- network = currency.network,
- value = NetworkStatus.Unreachable(address = networkAddress),
- )
- val networkStatusFlow = flowOf(networkStatus)
- every {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- } returns networkStatusFlow
-
- val quoteStatus = QuoteStatus(
- rawCurrencyId = currency.id.rawCurrencyId!!,
- value = QuoteStatus.Data(
- source = StatusSource.ACTUAL,
- fiatRate = BigDecimal.ONE,
- priceChange = BigDecimal.ONE,
- ),
- )
- val quoteStatusFlow = flowOf(quoteStatus)
- every {
- singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
- } returns quoteStatusFlow
-
- coEvery {
- stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
- } returns StakingIdFactory.Error.UnsupportedCurrency.left()
-
- // Act
- val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
-
- // Assert
- val expected = CryptoCurrencyStatus(
- currency = currency,
- value = CryptoCurrencyStatus.Unreachable(
- priceChange = BigDecimal.ONE,
- fiatRate = BigDecimal.ONE,
- networkAddress = networkAddress,
- ),
- )
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
- }
- }
-
- @Test
- fun `all sources are empty`() = runTest {
- // Arrange
- val userWallet = mockk {
- every { this@mockk.walletId } returns userWalletId
- every { this@mockk.isMultiCurrency } returns true
- }
-
- val currency = cryptoCurrencyFactory.ethereum
-
- every {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- } returns emptyFlow()
-
- every {
- singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
- } returns emptyFlow()
-
- // Act
- val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
-
- // Assert
- val expected = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
- Truth.assertThat(actual).containsExactly(expected)
-
- coVerify(ordering = Ordering.SEQUENCE) {
- singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
- singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
- }
- }
-}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingClickIntents.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingClickIntents.kt
index 28507abd9f..97e447a0f5 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingClickIntents.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingClickIntents.kt
@@ -69,4 +69,8 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
fun showPrimaryClickAlert()
fun onOpenLearnMoreAboutApproveClick()
+
+ fun onActivateTonAccountNotificationClick()
+
+ fun onActivateTonAccountClick()
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt
index b4c31ec116..f32aabd0e3 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt
@@ -2,6 +2,7 @@ package com.tangem.features.staking.impl.presentation.model
import androidx.compose.runtime.Stable
import arrow.core.getOrElse
+import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
@@ -48,8 +49,11 @@ import com.tangem.domain.staking.utils.getValidatorsCount
import com.tangem.domain.tokens.*
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
+import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
+import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
+import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.staking.api.StakingComponent
import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor
@@ -69,10 +73,15 @@ import com.tangem.features.staking.impl.presentation.state.transformers.approval
import com.tangem.features.staking.impl.presentation.state.transformers.confirmation.SetUpdatedAllowanceTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.notifications.AddStakingNotificationsTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.notifications.DismissStakingNotificationsStateTransformer
+import com.tangem.features.staking.impl.presentation.state.transformers.ton.CompleteInitializeBottomSheetTransformer
+import com.tangem.features.staking.impl.presentation.state.transformers.ton.SetFeeToTonInitializeBottomSheetTransformer
+import com.tangem.features.staking.impl.presentation.state.transformers.ton.ShowTonInitializeBottomSheetTransformer
+import com.tangem.features.staking.impl.presentation.state.transformers.ton.SetFeeErrorToTonInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.validator.ValidatorSelectChangeTransformer
import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalculateSubtractedAmount
import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction
import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction
+import com.tangem.lib.crypto.BlockchainUtils.isTon
import com.tangem.utils.Provider
import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
import com.tangem.utils.coroutines.*
@@ -122,6 +131,9 @@ internal class StakingModel @Inject constructor(
private val getActionsUseCase: GetActionsUseCase,
private val getYieldUseCase: GetYieldUseCase,
private val checkAccountInitializedUseCase: CheckAccountInitializedUseCase,
+ private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
+ private val getFeeUseCase: GetFeeUseCase,
+ private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase,
private val getActionRequirementAmountUseCase: GetActionRequirementAmountUseCase,
private val paramsInterceptorHolder: ParamsInterceptorHolder,
private val shareManager: ShareManager,
@@ -150,11 +162,14 @@ internal class StakingModel @Inject constructor(
}
}
+ private var isAccountInitialized: Boolean = true
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
private var stakingActions: List = emptyList()
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
private var minimumTransactionAmount: EnterAmountBoundary? = null
+ private var tonAccountInitializeTransaction: TransactionData.Uncompiled? = null
+
private val userWallet by lazy {
requireNotNull(
getUserWalletUseCase(userWalletId).getOrNull(),
@@ -250,26 +265,12 @@ internal class StakingModel @Inject constructor(
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
val noBalanceState = balanceState == null
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
- val isAccountInitialized = checkAccountInitializedUseCase.invoke(
- userWalletId = userWalletId,
- network = cryptoCurrencyStatus.currency.network,
- ).getOrElse {
- Timber.e(it)
- false
- }
when {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
stakingEventFactory.createStakingValidatorsUnavailableAlert()
return@launch
}
- isInitialInfoStep && noBalanceState && !isAccountInitialized -> {
- analyticsEventHandler.send(StakingAnalyticsEvent.UnitializedAddress(
- token = cryptoCurrencyStatus.currency.symbol,
- ))
- stakingEventFactory.createInitializeAccountAlert()
- return@launch
- }
isInitialInfoStep && noBalanceState -> {
val list = buildList {
SetConfirmationStateInitTransformer(
@@ -763,6 +764,7 @@ internal class StakingModel @Inject constructor(
AddStakingNotificationsTransformer(
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
appCurrencyProvider = Provider { appCurrency },
+ isAccountInitializedProvider = Provider { isAccountInitialized },
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
currencyWarning = currencyWarning,
currencyCheck = currencyStatus,
@@ -934,6 +936,85 @@ internal class StakingModel @Inject constructor(
urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP)
}
+ override fun onActivateTonAccountNotificationClick() {
+ modelScope.launch {
+ stateController.update(
+ ShowTonInitializeBottomSheetTransformer(
+ onDismiss = { stateController.update(DismissBottomSheetStateTransformer) },
+ ),
+ )
+
+ val tonAmount = BigDecimal(1)
+ val destination = getNetworkAddressesUseCase.invokeSync(
+ userWalletId = userWalletId,
+ network = cryptoCurrencyStatus.currency.network,
+ ).getOrNull(0)?.address ?: return@launch
+
+ val transaction = createTransferTransactionUseCase(
+ amount = tonAmount.convertToSdkAmount(cryptoCurrencyStatus),
+ destination = destination,
+ userWalletId = userWalletId,
+ network = cryptoCurrencyStatus.currency.network,
+ memo = null,
+ )
+ tonAccountInitializeTransaction = transaction.getOrElse {
+ stateController.update(
+ SetFeeErrorToTonInitializeBottomSheetTransformer(),
+ )
+ return@launch
+ }
+
+ val transactionFee = getFeeUseCase(
+ userWallet = userWallet,
+ network = cryptoCurrencyStatus.currency.network,
+ transactionData = tonAccountInitializeTransaction!!,
+ )
+
+ transactionFee.fold(
+ ifLeft = {
+ stateController.update(
+ SetFeeErrorToTonInitializeBottomSheetTransformer(),
+ )
+ },
+ ifRight = {
+ stateController.update(
+ SetFeeToTonInitializeBottomSheetTransformer(
+ appCurrencyProvider = Provider { appCurrency },
+ feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
+ fee = it.normal,
+ isFeeApproximate = false,
+ ),
+ )
+ },
+ )
+ }
+ }
+
+ override fun onActivateTonAccountClick() {
+ modelScope.launch {
+ tonAccountInitializeTransaction?.let { transaction ->
+ sendTransactionUseCase.invoke(
+ txData = transaction,
+ userWallet = userWallet,
+ network = cryptoCurrencyStatus.currency.network,
+ ).fold(
+ ifLeft = {
+ stateController.update(DismissBottomSheetStateTransformer)
+ },
+ ifRight = {
+ stateController.update(CompleteInitializeBottomSheetTransformer(
+ cryptoCurrencyStatus = cryptoCurrencyStatus,
+ minimumTransactionAmount = minimumTransactionAmount,
+ ))
+
+ balanceUpdater.partialUpdateWithDelay()
+ stateController.update(DismissBottomSheetStateTransformer)
+ },
+ )
+ }
+ }
+ }
+
private suspend fun setupApprovalNeeded() {
val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval
?: StakingApproval.Empty
@@ -961,7 +1042,9 @@ internal class StakingModel @Inject constructor(
)
.conflate()
.distinctUntilChanged()
- .filter { value.currentStep == StakingStep.InitialInfo }
+ .filter {
+ value.currentStep == StakingStep.InitialInfo || isCaseWithUnitializedTonAccount()
+ }
.onEach { maybeStatus ->
maybeStatus.fold(
ifRight = { status ->
@@ -989,6 +1072,19 @@ internal class StakingModel @Inject constructor(
}
cryptoCurrencyStatus = status
+ val isAccountInitializedNew = checkAccountInitializedUseCase.invoke(
+ userWalletId = userWalletId,
+ network = cryptoCurrencyStatus.currency.network,
+ ).getOrElse {
+ Timber.e(it)
+ false
+ }
+ if (isAccountInitializedNew && !isAccountInitialized) {
+ isAccountInitialized = true
+ updateNotifications()
+ }
+ isAccountInitialized = isAccountInitializedNew
+
setupApprovalNeeded()
setupIsAnyTokenStaked()
checkIfSubtractAvailable()
@@ -1150,6 +1246,12 @@ internal class StakingModel @Inject constructor(
.getOrElse { false }
}
+ private fun isCaseWithUnitializedTonAccount(): Boolean {
+ return value.currentStep == StakingStep.Confirmation &&
+ isTon(cryptoCurrencyStatus.currency.network.rawId) &&
+ !isAccountInitialized
+ }
+
private companion object {
const val WHAT_IS_STAKING_ARTICLE_URL = "https://tangem.com/en/blog/post/how-to-stake-cryptocurrency/"
const val ALLOWANCE_UPDATE_DELAY = 10_000L
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 abbe28cfb5..b5485b47ba 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
@@ -60,6 +60,15 @@ internal object StakingNotification {
title = resourceReference(R.string.staking_notification_low_staked_balance_title),
subtitle = resourceReference(R.string.staking_notification_low_staked_balance_text),
)
+
+ data class InitializeTonAccount(private val onInitializeClick: () -> Unit) : Info(
+ title = resourceReference(R.string.staking_notification_ton_account_initialization_title),
+ subtitle = resourceReference(R.string.staking_notification_ton_account_initialization_message),
+ buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
+ text = resourceReference(R.string.common_activate),
+ onClick = onInitializeClick,
+ ),
+ )
}
sealed class Info(
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/TonInitializeAccountBottomSheetConfig.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/TonInitializeAccountBottomSheetConfig.kt
new file mode 100644
index 0000000000..f941d083ec
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/TonInitializeAccountBottomSheetConfig.kt
@@ -0,0 +1,15 @@
+package com.tangem.features.staking.impl.presentation.state.bottomsheet
+
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.features.staking.impl.presentation.state.FeeState
+
+internal data class TonInitializeAccountBottomSheetConfig(
+ val title: TextReference,
+ val message: TextReference,
+ val footer: TextReference,
+ val onButtonClick: () -> Unit,
+ val isButtonEnabled: Boolean,
+ val isButtonLoading: Boolean,
+ val feeState: FeeState,
+) : TangemBottomSheetConfigContent
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingAlertUM.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingAlertUM.kt
index d2c18a8581..af6931c29f 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingAlertUM.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingAlertUM.kt
@@ -62,13 +62,6 @@ internal sealed class StakingAlertUM : AlertUM {
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
- data object InitializeAccount : StakingAlertUM() {
- override val onConfirmClick: (() -> Unit)? = null
- override val title: TextReference = resourceReference(id = R.string.staking_error_no_validators_title)
- override val message: TextReference = resourceReference(id = R.string.staking_notification_ton_activate_account)
- override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
- }
-
data class RewardsMinimumRequirementsError(
val cryptoCurrencyName: String,
val cryptoAmountValue: String,
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingEventFactory.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingEventFactory.kt
index bb1b446118..465bd1c024 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingEventFactory.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/events/StakingEventFactory.kt
@@ -47,11 +47,6 @@ internal class StakingEventFactory(
stateController.updateEvent(alert)
}
- fun createInitializeAccountAlert() {
- val alert = StakingEvent.ShowAlert(alert = StakingAlertUM.InitializeAccount)
- stateController.updateEvent(alert)
- }
-
fun createStakingRewardsMinimumRequirementsErrorAlert(cryptoCurrencyName: String, cryptoAmountValue: String) {
stateController.updateEvent(
StakingEvent.ShowAlert(
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt
index 3defda6f74..977585131e 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt
@@ -76,6 +76,11 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
}
}
+ suspend fun partialUpdateWithDelay() {
+ delay(BALANCE_UPDATE_DELAY)
+ partialUpdate()
+ }
+
private suspend fun fetchCurrencyStatus(delayMillis: Long = 0L) {
delay(delayMillis)
fetchCurrencyStatusUseCase(
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/stub/StakingClickIntentsStub.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/stub/StakingClickIntentsStub.kt
index d1ed5d5c98..d7fac733a7 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/stub/StakingClickIntentsStub.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/stub/StakingClickIntentsStub.kt
@@ -78,4 +78,8 @@ internal object StakingClickIntentsStub : StakingClickIntents {
override fun showPrimaryClickAlert() {}
override fun onOpenLearnMoreAboutApproveClick() {}
+
+ override fun onActivateTonAccountNotificationClick() {}
+
+ override fun onActivateTonAccountClick() {}
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt
index 0dbf76f8ad..1681808734 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt
@@ -40,6 +40,7 @@ import java.math.BigDecimal
internal class AddStakingNotificationsTransformer(
private val cryptoCurrencyStatusProvider: Provider,
private val appCurrencyProvider: Provider,
+ private val isAccountInitializedProvider: Provider,
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
private val currencyWarning: CryptoCurrencyWarning?,
private val feeError: GetFeeError?,
@@ -90,34 +91,40 @@ internal class AddStakingNotificationsTransformer(
BigDecimal.ZERO
}
- val notifications = buildList {
- // errors
- addErrorNotifications(
- prevState = prevState,
- feeError = feeError,
- sendingAmount = sendingAmount,
- onReload = prevState.clickIntents::getFee,
- feeValue = feeValue,
- )
- addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee)
- // warnings
- addWarningNotifications(
- prevState = prevState,
- amountState = amountState,
- feeState = feeState,
- sendingAmount = sendingAmount,
- isFeeCoverage = isFeeCoverage && isEnterAction && !sendingAmount.equals(minimumRequirement),
- )
+ val notifications = if (isAccountInitializedProvider.invoke()) {
+ buildList {
+ // errors
+ addErrorNotifications(
+ prevState = prevState,
+ feeError = feeError,
+ sendingAmount = sendingAmount,
+ onReload = prevState.clickIntents::getFee,
+ feeValue = feeValue,
+ )
+ addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee)
+ // warnings
+ addWarningNotifications(
+ prevState = prevState,
+ amountState = amountState,
+ feeState = feeState,
+ sendingAmount = sendingAmount,
+ isFeeCoverage = isFeeCoverage && isEnterAction && !sendingAmount.equals(minimumRequirement),
+ )
- stakingInfoNotificationsFactory.addInfoNotifications(
- notifications = this,
- prevState = prevState,
- sendingAmount = sendingAmount,
- actionAmount = amountValue,
- feeValue = feeValue,
- tonBalanceExtraFeeThreshold = TON_BALANCE_EXTRA_FEE_THRESHOLD,
- )
- }.toImmutableList()
+ stakingInfoNotificationsFactory.addInfoNotifications(
+ notifications = this,
+ prevState = prevState,
+ sendingAmount = sendingAmount,
+ actionAmount = amountValue,
+ feeValue = feeValue,
+ tonBalanceExtraFeeThreshold = TON_BALANCE_EXTRA_FEE_THRESHOLD,
+ )
+ }.toImmutableList()
+ } else {
+ buildList {
+ addTonInitializeAccountNotification(prevState)
+ }.toImmutableList()
+ }
val isActualSources = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
@@ -130,7 +137,8 @@ internal class AddStakingNotificationsTransformer(
it is StakingNotification.Error ||
it is NotificationUM.Error ||
it is NotificationUM.Warning.NetworkFeeUnreachable ||
- it is StakingNotification.Warning.TransactionInProgress
+ it is StakingNotification.Warning.TransactionInProgress ||
+ it is StakingNotification.Warning.InitializeTonAccount
} && isActualSources,
),
)
@@ -219,7 +227,6 @@ internal class AddStakingNotificationsTransformer(
) {
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val appCurrency = appCurrencyProvider()
- cryptoCurrencyStatus.currency
addRentExemptionNotification(
rentWarning = currencyCheck.rentWarning,
@@ -282,6 +289,19 @@ internal class AddStakingNotificationsTransformer(
}
}
+ private fun MutableList.addTonInitializeAccountNotification(prevState: StakingUiState) {
+ val isAccountInitialized = isAccountInitializedProvider.invoke()
+ val cryptoCurrencyNetworkIdValue = cryptoCurrencyStatusProvider().currency.network.rawId
+
+ if (isTon(cryptoCurrencyNetworkIdValue) && !isAccountInitialized) {
+ add(
+ StakingNotification.Warning.InitializeTonAccount(
+ onInitializeClick = prevState.clickIntents::onActivateTonAccountNotificationClick,
+ ),
+ )
+ }
+ }
+
private companion object {
val TON_BALANCE_EXTRA_FEE_THRESHOLD = BigDecimal(0.2)
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/CompleteInitializeBottomSheetTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/CompleteInitializeBottomSheetTransformer.kt
new file mode 100644
index 0000000000..7a9bc02057
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/CompleteInitializeBottomSheetTransformer.kt
@@ -0,0 +1,53 @@
+package com.tangem.features.staking.impl.presentation.state.transformers.ton
+
+import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
+import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
+import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.features.staking.impl.presentation.state.FeeState
+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.bottomsheet.TonInitializeAccountBottomSheetConfig
+import com.tangem.utils.transformer.Transformer
+import java.math.BigDecimal
+
+internal class CompleteInitializeBottomSheetTransformer(
+ private val cryptoCurrencyStatus: CryptoCurrencyStatus,
+ private val minimumTransactionAmount: EnterAmountBoundary?,
+) : Transformer {
+
+ override fun transform(prevState: StakingUiState): StakingUiState {
+ val prevBottomSheetConfig = prevState.bottomSheetConfig ?: return prevState
+ val prevBottomSheetConfigContent =
+ prevBottomSheetConfig.content as? TonInitializeAccountBottomSheetConfig ?: return prevState
+
+ val feeAmount = (prevBottomSheetConfigContent.feeState as? FeeState.Content)
+ ?.fee?.amount?.value?.multiply(BigDecimal(FEE_MULTIPLIER)) ?: return prevState
+
+ val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
+
+ return prevState.copy(
+ amountState = AmountReduceByTransformer(
+ cryptoCurrencyStatus = cryptoCurrencyStatus,
+ minimumTransactionAmount = minimumTransactionAmount,
+ value = ReduceByData(
+ feeAmount,
+ feeAmount,
+ ),
+ ).transform(prevState.amountState),
+ confirmationState = confirmationState.copy(
+ reduceAmountBy = feeAmount,
+ ),
+ bottomSheetConfig = prevBottomSheetConfig.copy(
+ content = prevBottomSheetConfigContent.copy(
+ isButtonLoading = true,
+ isButtonEnabled = false,
+ ),
+ ),
+ )
+ }
+
+ private companion object {
+ const val FEE_MULTIPLIER = 1.1
+ }
+}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/SetFeeErrorToTonInitializeBottomSheetTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/SetFeeErrorToTonInitializeBottomSheetTransformer.kt
new file mode 100644
index 0000000000..644e344be8
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/SetFeeErrorToTonInitializeBottomSheetTransformer.kt
@@ -0,0 +1,25 @@
+package com.tangem.features.staking.impl.presentation.state.transformers.ton
+
+import com.tangem.features.staking.impl.presentation.state.FeeState
+import com.tangem.features.staking.impl.presentation.state.StakingUiState
+import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
+import com.tangem.utils.transformer.Transformer
+
+internal class SetFeeErrorToTonInitializeBottomSheetTransformer : Transformer {
+
+ override fun transform(prevState: StakingUiState): StakingUiState {
+ val prevBottomSheetConfig = prevState.bottomSheetConfig ?: return prevState
+ val prevBottomSheetConfigContent =
+ prevBottomSheetConfig.content as? TonInitializeAccountBottomSheetConfig ?: return prevState
+
+ return prevState.copy(
+ bottomSheetConfig = prevBottomSheetConfig.copy(
+ content = prevBottomSheetConfigContent.copy(
+ isButtonEnabled = false,
+ feeState = FeeState.Error,
+ isButtonLoading = false,
+ ),
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/SetFeeToTonInitializeBottomSheetTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/SetFeeToTonInitializeBottomSheetTransformer.kt
new file mode 100644
index 0000000000..659bc1e3ab
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/SetFeeToTonInitializeBottomSheetTransformer.kt
@@ -0,0 +1,41 @@
+package com.tangem.features.staking.impl.presentation.state.transformers.ton
+
+import com.tangem.blockchain.common.transaction.Fee
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.features.staking.impl.presentation.state.FeeState
+import com.tangem.features.staking.impl.presentation.state.StakingUiState
+import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
+import com.tangem.utils.Provider
+import com.tangem.utils.transformer.Transformer
+
+internal class SetFeeToTonInitializeBottomSheetTransformer(
+ private val appCurrencyProvider: Provider,
+ private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
+ private val fee: Fee,
+ private val isFeeApproximate: Boolean,
+) : Transformer {
+
+ override fun transform(prevState: StakingUiState): StakingUiState {
+ val prevBottomSheetConfig = prevState.bottomSheetConfig ?: return prevState
+ val prevBottomSheetConfigContent =
+ prevBottomSheetConfig.content as? TonInitializeAccountBottomSheetConfig ?: return prevState
+
+ val isFeeConvertibleToFiat = feeCryptoCurrencyStatus?.currency?.network?.hasFiatFeeRate == true
+ return prevState.copy(
+ bottomSheetConfig = prevBottomSheetConfig.copy(
+ content = prevBottomSheetConfigContent.copy(
+ isButtonEnabled = true,
+ isButtonLoading = false,
+ feeState = FeeState.Content(
+ fee = fee,
+ rate = feeCryptoCurrencyStatus?.value?.fiatRate,
+ isFeeConvertibleToFiat = isFeeConvertibleToFiat,
+ appCurrency = appCurrencyProvider(),
+ isFeeApproximate = isFeeApproximate,
+ ),
+ ),
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/ShowTonInitializeBottomSheetTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/ShowTonInitializeBottomSheetTransformer.kt
new file mode 100644
index 0000000000..27e17d0ca7
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ton/ShowTonInitializeBottomSheetTransformer.kt
@@ -0,0 +1,32 @@
+package com.tangem.features.staking.impl.presentation.state.transformers.ton
+
+import com.tangem.common.ui.R
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.features.staking.impl.presentation.state.FeeState
+import com.tangem.features.staking.impl.presentation.state.StakingUiState
+import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
+import com.tangem.utils.transformer.Transformer
+
+internal class ShowTonInitializeBottomSheetTransformer(
+ private val onDismiss: () -> Unit,
+) : Transformer {
+
+ override fun transform(prevState: StakingUiState): StakingUiState {
+ return prevState.copy(
+ bottomSheetConfig = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = onDismiss,
+ content = TonInitializeAccountBottomSheetConfig(
+ title = resourceReference(R.string.staking_account_initialization_title),
+ message = resourceReference(R.string.staking_account_initialization_message),
+ footer = resourceReference(R.string.staking_account_initialization_footer),
+ onButtonClick = prevState.clickIntents::onActivateTonAccountClick,
+ isButtonEnabled = false,
+ feeState = FeeState.Loading,
+ isButtonLoading = false,
+ ),
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt
index 5170756b32..c8f15d868e 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt
@@ -28,8 +28,10 @@ import com.tangem.features.staking.impl.presentation.state.StakingStep
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingActionSelectionBottomSheetConfig
import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingInfoBottomSheetConfig
+import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingActionSelectorBottomSheet
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingInfoBottomSheet
+import com.tangem.features.staking.impl.presentation.ui.bottomsheet.TonInitializeAccountBottomSheet
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
@@ -83,6 +85,7 @@ fun StakingBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
is StakingInfoBottomSheetConfig -> StakingInfoBottomSheet(bottomSheetConfig)
is GiveTxPermissionBottomSheetConfig -> GiveTxPermissionBottomSheet(bottomSheetConfig)
is StakingActionSelectionBottomSheetConfig -> StakingActionSelectorBottomSheet(bottomSheetConfig)
+ is TonInitializeAccountBottomSheetConfig -> TonInitializeAccountBottomSheet(bottomSheetConfig)
}
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/TonInitializeAccountBottomSheet.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/TonInitializeAccountBottomSheet.kt
new file mode 100644
index 0000000000..21be5002e8
--- /dev/null
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/TonInitializeAccountBottomSheet.kt
@@ -0,0 +1,158 @@
+package com.tangem.features.staking.impl.presentation.ui.bottomsheet
+
+import android.content.res.Configuration
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.tangem.blockchain.common.Amount
+import com.tangem.blockchain.common.AmountType
+import com.tangem.blockchain.common.transaction.Fee
+import com.tangem.common.ui.R
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
+import com.tangem.core.ui.components.buttons.common.TangemButton
+import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
+import com.tangem.core.ui.components.buttons.common.TangemButtonSize
+import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.features.staking.impl.presentation.state.FeeState
+import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
+import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock
+import java.math.BigDecimal
+
+@Composable
+internal fun TonInitializeAccountBottomSheet(config: TangemBottomSheetConfig) {
+ TangemBottomSheet(
+ config = config,
+ containerColor = TangemTheme.colors.background.tertiary,
+ ) { content ->
+ Column(
+ modifier = Modifier
+ .padding(
+ start = TangemTheme.dimens.spacing16,
+ end = TangemTheme.dimens.spacing16,
+ bottom = TangemTheme.dimens.spacing16,
+ )
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ SpacerH(height = 16.dp)
+ Image(
+ painter = painterResource(id = R.drawable.img_ton_22),
+ modifier = Modifier.size(56.dp),
+ contentDescription = null,
+ )
+
+ SpacerH(height = 24.dp)
+
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ text = content.title.resolveReference(),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(height = 16.dp)
+
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ text = content.message.resolveReference(),
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.secondary,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(height = 24.dp)
+
+ StakingFeeBlock(content.feeState)
+
+ SpacerH(height = 12.dp)
+
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ text = content.footer.resolveReference(),
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.tertiary,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(height = 20.dp)
+
+ TangemButton(
+ text = stringResourceSafe(R.string.common_activate),
+ icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
+ onClick = content.onButtonClick,
+ colors = TangemButtonsDefaults.primaryButtonColors,
+ showProgress = content.feeState is FeeState.Loading || content.isButtonLoading,
+ enabled = content.isButtonEnabled,
+ size = TangemButtonSize.WideAction,
+ textStyle = TangemTheme.typography.subtitle1,
+ modifier = Modifier.fillMaxWidth(),
+ )
+
+ SpacerH(height = 8.dp)
+ }
+ }
+}
+
+// region Preview
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun Preview_TonInitializeAccountBottomSheetContent() {
+ TangemThemePreview {
+ TonInitializeAccountBottomSheet(
+ config = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = {},
+ content = TonInitializeAccountBottomSheetConfig(
+ title = resourceReference(R.string.staking_account_initialization_title),
+ message = resourceReference(R.string.staking_account_initialization_message),
+ footer = resourceReference(R.string.staking_account_initialization_footer),
+ onButtonClick = {},
+ isButtonEnabled = true,
+ isButtonLoading = false,
+ feeState = FeeState.Content(
+ fee = Fee.Common(
+ amount = Amount(
+ currencySymbol = "MATIC",
+ value = BigDecimal(0.159806),
+ decimals = 18,
+ type = AmountType.Coin,
+ ),
+ ),
+ rate = BigDecimal.ONE,
+ appCurrency = AppCurrency.Default,
+ isFeeApproximate = false,
+ isFeeConvertibleToFiat = true,
+ ),
+ ),
+
+ ),
+ )
+ }
+}
+// endregion Preview
\ No newline at end of file