diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt
index 7425a7aa0f..fe9115cfec 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt
@@ -301,6 +301,12 @@ sealed class NotificationUM(val config: NotificationConfig) {
),
subtitle = TextReference.EMPTY,
)
+
+ data object YieldSupplyHighNetworkFee : Info(
+ title = resourceReference(id = R.string.yield_module_high_network_fees_notification_title),
+ subtitle = resourceReference(id = R.string.yield_module_high_network_fees_notification_description),
+ iconTint = NotificationConfig.IconTint.Accent,
+ )
}
sealed interface Cardano {
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index a009fa485f..0dc6af1380 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -849,6 +849,9 @@
Das Zielkonto verfügt nicht über eine Vertrauensstellung für das gesendete Asset.
Sicher Dir 10 $ in BTC mit jeder Wallet greif schnell zu!
Black Friday: bis zu 30% Nachlass
+ Los geht\'s
+ Nur für kurze Zeit!
+ 1+1: Beim Kauf einer Wallet erhältst Du 50% Rabatt
Jetzt beitreten
Teile Deinen Code – verdiene 5 USDT pro Verkauf. Deine Freunde erhalten 10 % Rabatt.
Erhalte BELOHNUNGEN für jeden Freund!
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 94e4382158..82aca73289 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -848,8 +848,8 @@
ウォレットごとに$10相当のBTCをプレゼント\nお早めに!
ブラックフライデー:最大30% オフ
今すぐチェック
- 理想のTangemセットを揃えよう。期間限定。
- 1+1:1つ購入で、2つ目が50%オフ
+ 期間限定!
+ 1+1:ウォレット1つ購入で、2つ目が50%オフ
今すぐ参加
コードを共有すると、販売ごとに5 USDTを獲得できます。お友達は10%割引になります。
友達への紹介で報酬を獲得しよう!
@@ -1391,7 +1391,7 @@
取引を表示
サービス手数料
手数料
- カードを紛失したり盗まれたりしても、資金を安全に保てます。一時停止はいつでも解除できます。
+ 資金を安全に保管します。凍結はいつでも解除できます。
カードを一時停止しますか?
カードを凍結できませんでした。しばらくしてからもう一度お試しください。
一時停止
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index b5587d3945..4b22469076 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -16,8 +16,10 @@
Код доступа
Превышен лимит %1$s аккаунтов. Архивируйте, чтобы создать новый.
Невозможно добавить новый аккаунт.
+ Аккаунт заархивирован
Архивированные аккаунты
Восстановить
+ Вы собираетесь восстановить \"%1$s\".
Восстановить аккаунт
Лимит в 20 активных аккаунтов достигнут. Пожалуйста, зархивируйте один аккаунт, чтобы продолжить.
Невозможно восстановить аккаунт
@@ -26,12 +28,14 @@
Этот аккаунт участвует в реферальной программе
Этот аккаунт не может быть архивирован.
Мы не смогли создать аккаунт. Пожалуйста, попробуйте позже.
+ Аккаунт создан
Архивный аккаунт
Архив
Вы архивируете свой аккаунт, но в любое время можете вернуть его обратно
Архивация...
Аккаунт
Невозможно отредактировать аккаунт
+ Аккаунт сохранен
Аккаунт для наград
Добавить аккаунт
Сохранить
@@ -46,6 +50,8 @@
Основной аккаунт
Лимит %1$s активных аккаунтов превышен. Архивируйте один, чтобы продолжить.
Невозможно восстановить аккаунт
+ Аккаунт восстановлен
+ Нажмите и удерживайте аккаунт, чтобы изменить порядок аккаунтов.
Продолжить
Отменить
Вы уверены, что хотите создание нового аккаунта?
@@ -87,14 +93,17 @@
Токены в сети %1$s не поддерживаются этой картой или кольцом из-за ограничений прошивки.
У вас возникли трудности со сканированием карты или кольца?
Эта карта не предназначена для работы с этим приложением
+ Сначала установите код доступа, чтобы включить биометрию.
Используйте %1$s, чтобы быстро и безопасно разблокировать кошелёк и выполнять чувствительные действия, например, подписывать транзакции. Для аппаратных кошельков всё ещё требуется карта или кольцо для подписи.
Комиссия по-умолчанию
Подключите функцию комиссии по умолчанию и при формировании транзакции на отправку средств комиссия будет выставлена автоматически, а экран комиссии пропущен. Вы всегда сможете на него вернуться.
Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem
Включите биометрическую аутентификацию
Отключение %1$s потребует ввода вашего кода доступа для разблокировки приложения и работы с кошельком.
+ Позже будет необходимо ввести код доступа к вашему кошельку, чтобы мы могли безопасно сохранить его для будущего использования.
Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком.
При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения.
+ Это удалит все сохранённые коды доступа к кошелькам. Любое дальнейшее взаимодействие с кошельком будет требовать ввода кода доступа.
Требуется код доступа
Эта опция отключает использование биометрии для выполнения чувствительных действий. Каждый раз, например при подписании транзакции, вам потребуется вводить код доступа.
Сохранение кода доступа
@@ -144,6 +153,7 @@
Вход по биометрии временно заблокирован. Попробуйте снова через 30 секунд или разблокируйте кошелёк с помощью прикладывания устройства или кода доступа.
Слишком много попыток
Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона.
+ Биометрические данные на вашем устройстве были обновлены. Пожалуйста, выберите свой кошелёк и введите его код доступа, чтобы повторно включить вход по биометрии.
Внимание
При обработке промокода произошла ошибка. Пожалуйста, попробуйте позже.
Ошибка активации
@@ -299,6 +309,7 @@
Позже
Узнать больше
Осталось %1$s
+ Легаси Bitcoin
Заблокирован
Заблокированные кошельки
Основная сеть
@@ -311,6 +322,8 @@
- %d сетей
- %d сети
+ Новый адрес
+ Новости
Далее
NFT
Нет
@@ -363,6 +376,7 @@
Поддержка
Поддерживаемые сети
Обменять
+ Tangem
условия участия
Условиями использования
На
@@ -391,6 +405,7 @@
Адрес контракта скопирован!
Доступные сети
Деривация вашего токена совпадает с деривацией %1$s. Токен будет добавлен к этой учётной записи.
+ Деривация принадлежит другому аккаунту
Токен добавлен в аккаунт %1$s
Адрес контракта
Адрес контракта некорректен
@@ -527,6 +542,8 @@
ID: %s
ID транзакции скопирован
Обменяйте любой актив в вашем портфеле на этот токен
+ Рынок и Новости
+ В тренде
Информация ниже не является обязательной. Вы можете стереть её, если хотите.
Расскажите, каких функций вам не хватает, и мы постараемся вам помочь.
Скажите, пожалуйста, какая у вас карта или кольцо?
@@ -687,6 +704,7 @@
APY %s
Мой портфель
Рынок
+ Зарабатывай с Tangem
Чтобы создать адреса для выбранных сетей, отсканируйте вашу карту или кольцо Tangem кошелька
Потяните вверх или коснитесь поисковой строки, чтобы добавить токен
Данные раздела получены из следующих сетей: %s
@@ -791,6 +809,8 @@
Активировать режим доходности
Обновитесь до версии %1$s, чтобы создать мобильный кошелёк
Мобильный кошелек требует %1$s или новее.
+ Все новости
+ Будьте в курсе
Функция NFC недоступна на вашем устройстве
О NFT
NFT
@@ -1198,6 +1218,7 @@
%1$s — это монета в сети Tron. Чтобы рассчитать комиссию и совершить транзакцию, вам необходимо внести немного Tron (TRX) на свой адрес.
Сумма превышает остаток
Для завершения транзакции на указанный адрес необходим тег назначения (мемо).
+ Требуется Destination Tag
Недопустимая сумма
Комиссия превышает остаток
Отправляемая сумма превышает остаток
@@ -1225,6 +1246,8 @@
Сетевая комиссия — это небольшая плата за обработку и подтверждение вашей транзакции в блокчейне.
Чтобы начать стейкинг, ваш TON-аккаунт должен быть активирован транзакцией самому себе на 1 TON. Средства остаются в вашем кошельке — этот шаг лишь активирует ваш аккаунт для участия в стейкинге.
Активация аккаунта
+ Сетевая комиссия изменилась. Пожалуйста, проверьте новую сумму перед продолжением.
+ Комиссия сети обновлена
Сумма для стейкинга должна быть не менее %s
Согласно правилам сети, сумма стейкинга будет округлена до %1$sTRX.
Сумма для вывода из стейкинга будет округлена до %1$s TRX ввиду особенностей сети.
@@ -1263,6 +1286,7 @@
Комиссия сети за одобрение токена будет взиматься за подтверждение того, что именно вы разрешаете использовать ваш токен для стейкинга.
Пользуясь стейкинг сервисом, вы соглашаетесь с %1$s и %2$s
Заблокировано
+ Максимальная сумму: %s
Переместить
Нативный стейкинг
В данный момент нет доступных активных валидаторов для стейкинга. Пожалуйста, попробуйте позже.
@@ -1598,6 +1622,7 @@
Код доступа
Активация аккаунта
ПИН не принят. Попробуйте ещё раз или введите другой код.
+ Разблокировать
Выберите способ добавления кошелька
Отсканируйте вашу карту или кольцо Tangem, чтобы восстановить её или импортировать из другого кошелька.
Создать аппаратный кошелёк
@@ -1934,6 +1959,7 @@
Политика комиссий за пополнение
Tangem взимает комиссию за обслуживание в размере 15% от полученного дохода.
Ваши средства будут автоматически переведены в Aave, как только комиссия сети снизится или баланс достигнет минимально необходимой суммы.
+ Высокая сетевая комиссия
Историческая доходность
Получай до %1$s APY на свой баланс
Разрешение для вашего токена в режиме доходности было отозвано. Откройте токен, чтобы выдать разрешение снова.
diff --git a/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldSupplyFee.kt b/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldSupplyFee.kt
new file mode 100644
index 0000000000..6763e16cf5
--- /dev/null
+++ b/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldSupplyFee.kt
@@ -0,0 +1,8 @@
+package com.tangem.domain.yield.supply.models
+
+import java.math.BigDecimal
+
+data class YieldSupplyFee(
+ val value: BigDecimal,
+ val isHighFee: Boolean = false,
+)
\ No newline at end of file
diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt
index 460440f4b4..30daafec70 100644
--- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt
+++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt
@@ -2,6 +2,8 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import arrow.core.Either.Companion.catch
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.wallet.UserWalletId
@@ -10,6 +12,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.yield.supply.YieldSupplyConst.YIELD_SUPPLY_EVM_CONSTANT_GAS_LIMIT
import com.tangem.domain.yield.supply.fixFee
+import com.tangem.domain.yield.supply.models.YieldSupplyFee
import java.math.BigDecimal
import java.math.RoundingMode
@@ -25,7 +28,7 @@ class YieldSupplyGetCurrentFeeUseCase(
suspend operator fun invoke(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
- ): Either = catch {
+ ): Either = catch {
val feeWithoutGas = feeRepository.getEthereumFeeWithoutGas(userWalletId, cryptoCurrencyStatus.currency)
val fiatRate = cryptoCurrencyStatus.value.fiatRate ?: error("Fiat rate is missing")
@@ -60,6 +63,23 @@ class YieldSupplyGetCurrentFeeUseCase(
val tokenValue = rateRatio.multiply(nativeGas.amount.value)
- tokenValue.stripTrailingZeros()
+ val isEthereum = cryptoCurrencyStatus.currency
+ .network.id.rawId.value == Blockchain.Ethereum.id
+
+ val isHighFee = if (isEthereum) {
+ val maxFeePerGas = (feeWithoutGas as? Fee.Ethereum.EIP1559)?.maxFeePerGas ?: 0.toBigInteger()
+ maxFeePerGas >= HIGH_ETHEREUM_FEE
+ } else {
+ false
+ }
+
+ YieldSupplyFee(
+ value = tokenValue.stripTrailingZeros(),
+ isHighFee = isHighFee,
+ )
+ }
+
+ companion object {
+ private val HIGH_ETHEREUM_FEE = 400_000_000.toBigInteger()
}
}
\ No newline at end of file
diff --git a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCaseTest.kt b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCaseTest.kt
new file mode 100644
index 0000000000..ee27141e2d
--- /dev/null
+++ b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCaseTest.kt
@@ -0,0 +1,384 @@
+package com.tangem.domain.yield.supply.usecase
+
+import arrow.core.Either
+import com.google.common.truth.Truth.assertThat
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.blockchain.common.transaction.Fee
+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.Network
+import com.tangem.domain.models.network.NetworkAddress
+import com.tangem.domain.models.quote.QuoteStatus
+import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.quotes.QuotesRepository
+import com.tangem.domain.tokens.repository.CurrenciesRepository
+import com.tangem.domain.transaction.FeeRepository
+import com.tangem.domain.utils.convertToSdkAmount
+import io.mockk.coEvery
+import io.mockk.mockk
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.math.RoundingMode
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class YieldSupplyGetCurrentFeeUseCaseTest {
+
+ private val feeRepository: FeeRepository = mockk()
+ private val quotesRepository: QuotesRepository = mockk()
+ private val currenciesRepository: CurrenciesRepository = mockk()
+
+ private lateinit var useCase: YieldSupplyGetCurrentFeeUseCase
+
+ private val userWalletId = UserWalletId("abcdef012345")
+
+ @BeforeEach
+ fun setUp() {
+ useCase = YieldSupplyGetCurrentFeeUseCase(
+ feeRepository = feeRepository,
+ quotesRepository = quotesRepository,
+ currenciesRepository = currenciesRepository,
+ )
+ }
+
+ @Test
+ fun `GIVEN valid inputs on non-ethereum WHEN invoke THEN returns fee value and not high`() = runTest {
+ val rawNetworkId = Blockchain.BSC.id
+ val tokenDecimals = 8
+ val nativeDecimals = 18
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = tokenDecimals)
+ val cryptoStatus = createStatus(token = token, fiatRate = BigDecimal("2.00"))
+ val nativeCoin = createCoin(rawNetworkId = rawNetworkId, decimals = nativeDecimals)
+ val nativeFiatRate = BigDecimal("4.00")
+
+ val maxFeePerGas = BigInteger.valueOf(1_000_000_000L)
+ val feeWithoutGas = Fee.Ethereum.EIP1559(
+ maxFeePerGas = maxFeePerGas,
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
+ )
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns feeWithoutGas
+ coEvery {
+ currenciesRepository.getNetworkCoin(
+ userWalletId = userWalletId,
+ networkId = token.network.id,
+ derivationPath = token.network.derivationPath,
+ )
+ } returns nativeCoin
+ coEvery {
+ quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))
+ } returns setOf(
+ QuoteStatus(
+ rawCurrencyId = nativeCoin.id.rawCurrencyId!!,
+ value = QuoteStatus.Data(
+ source = StatusSource.ACTUAL,
+ fiatRate = nativeFiatRate,
+ priceChange = BigDecimal.ZERO,
+ ),
+ ),
+ )
+
+ val gasLimit = BigInteger.valueOf(350_000)
+ val nativeGasValue = maxFeePerGas.multiply(gasLimit).toBigDecimal().movePointLeft(nativeDecimals)
+ val rateRatio = nativeFiatRate.divide(cryptoStatus.value.fiatRate!!, tokenDecimals, RoundingMode.HALF_UP)
+ val expectedTokenValue = rateRatio.multiply(nativeGasValue).stripTrailingZeros()
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isRight()).isTrue()
+ val fee = (result as Either.Right).value
+ assertThat(fee.value).isEqualTo(expectedTokenValue)
+ assertThat(fee.isHighFee).isFalse()
+ }
+
+ @Test
+ fun `GIVEN ethereum with high gas WHEN invoke THEN returns high fee flag`() = runTest {
+ val rawNetworkId = Blockchain.Ethereum.id
+ val tokenDecimals = 8
+ val nativeDecimals = 18
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = tokenDecimals)
+ val cryptoStatus = createStatus(token = token, fiatRate = BigDecimal("2.00"))
+ val nativeCoin = createCoin(rawNetworkId = rawNetworkId, decimals = nativeDecimals)
+ val nativeFiatRate = BigDecimal("4.00")
+
+ val maxFeePerGas = BigInteger.valueOf(400_000_000L) // threshold
+ val feeWithoutGas = Fee.Ethereum.EIP1559(
+ maxFeePerGas = maxFeePerGas,
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
+ )
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns feeWithoutGas
+ coEvery {
+ currenciesRepository.getNetworkCoin(
+ userWalletId = userWalletId,
+ networkId = token.network.id,
+ derivationPath = token.network.derivationPath,
+ )
+ } returns nativeCoin
+ coEvery {
+ quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))
+ } returns setOf(
+ QuoteStatus(
+ rawCurrencyId = nativeCoin.id.rawCurrencyId!!,
+ value = QuoteStatus.Data(
+ source = StatusSource.ACTUAL,
+ fiatRate = nativeFiatRate,
+ priceChange = BigDecimal.ZERO,
+ ),
+ ),
+ )
+
+ val gasLimit = BigInteger.valueOf(350_000)
+ val nativeGasValue = maxFeePerGas.multiply(gasLimit).toBigDecimal().movePointLeft(nativeDecimals)
+ val rateRatio = nativeFiatRate.divide(cryptoStatus.value.fiatRate!!, tokenDecimals, RoundingMode.HALF_UP)
+ val expectedTokenValue = rateRatio.multiply(nativeGasValue).stripTrailingZeros()
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isRight()).isTrue()
+ val fee = (result as Either.Right).value
+ assertThat(fee.value).isEqualTo(expectedTokenValue)
+ assertThat(fee.isHighFee).isTrue()
+ }
+
+ @Test
+ fun `GIVEN token fiat rate missing WHEN invoke THEN returns error`() = runTest {
+ val rawNetworkId = Blockchain.BSC.id
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = 8)
+ val cryptoStatus = createStatus(token = token, fiatRate = null)
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns Fee.Ethereum.EIP1559(
+ maxFeePerGas = BigInteger.ONE,
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(
+ createStatus(token = token, fiatRate = BigDecimal.ONE),
+ ),
+ )
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isLeft()).isTrue()
+ val error = (result as Either.Left).value
+ assertThat(error).isInstanceOf(IllegalStateException::class.java)
+ assertThat(error.message).isEqualTo("Fiat rate is missing")
+ }
+
+ @Test
+ fun `GIVEN native quotes unavailable WHEN invoke THEN returns error`() = runTest {
+ val rawNetworkId = Blockchain.BSC.id
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = 8)
+ val cryptoStatus = createStatus(token = token, fiatRate = BigDecimal("2.00"))
+ val nativeCoin = createCoin(rawNetworkId = rawNetworkId, decimals = 18)
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns Fee.Ethereum.EIP1559(
+ maxFeePerGas = BigInteger.valueOf(1_000_000_000L),
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
+ )
+ coEvery {
+ currenciesRepository.getNetworkCoin(
+ userWalletId = userWalletId,
+ networkId = token.network.id,
+ derivationPath = token.network.derivationPath,
+ )
+ } returns nativeCoin
+ coEvery { quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!)) } returns null
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isLeft()).isTrue()
+ val error = (result as Either.Left).value
+ assertThat(error).isInstanceOf(IllegalStateException::class.java)
+ assertThat(error.message).isEqualTo("Quotes for native coin are unavailable")
+ }
+
+ @Test
+ fun `GIVEN empty quotes list WHEN invoke THEN returns error`() = runTest {
+ val rawNetworkId = Blockchain.BSC.id
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = 8)
+ val cryptoStatus = createStatus(token = token, fiatRate = BigDecimal("2.00"))
+ val nativeCoin = createCoin(rawNetworkId = rawNetworkId, decimals = 18)
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns Fee.Ethereum.EIP1559(
+ maxFeePerGas = BigInteger.valueOf(1_000_000_000L),
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
+ )
+ coEvery {
+ currenciesRepository.getNetworkCoin(
+ userWalletId = userWalletId,
+ networkId = token.network.id,
+ derivationPath = token.network.derivationPath,
+ )
+ } returns nativeCoin
+ coEvery { quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!)) } returns emptySet()
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isLeft()).isTrue()
+ val error = (result as Either.Left).value
+ assertThat(error).isInstanceOf(IllegalStateException::class.java)
+ assertThat(error.message).isEqualTo("Empty quotes list for native coin")
+ }
+
+ @Test
+ fun `GIVEN native fiat rate non-positive WHEN invoke THEN returns error`() = runTest {
+ val rawNetworkId = Blockchain.BSC.id
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = 8)
+ val cryptoStatus = createStatus(token = token, fiatRate = BigDecimal("2.00"))
+ val nativeCoin = createCoin(rawNetworkId = rawNetworkId, decimals = 18)
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns Fee.Ethereum.EIP1559(
+ maxFeePerGas = BigInteger.valueOf(1_000_000_000L),
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(cryptoStatus),
+ )
+ coEvery {
+ currenciesRepository.getNetworkCoin(
+ userWalletId = userWalletId,
+ networkId = token.network.id,
+ derivationPath = token.network.derivationPath,
+ )
+ } returns nativeCoin
+ coEvery {
+ quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!))
+ } returns setOf(
+ QuoteStatus(
+ rawCurrencyId = nativeCoin.id.rawCurrencyId!!,
+ value = QuoteStatus.Data(
+ source = StatusSource.ACTUAL,
+ fiatRate = BigDecimal.ZERO, // non-positive
+ priceChange = BigDecimal.ZERO,
+ ),
+ ),
+ )
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isLeft()).isTrue()
+ val error = (result as Either.Left).value
+ assertThat(error).isInstanceOf(IllegalArgumentException::class.java)
+ assertThat(error.message).isEqualTo("Native fiat rate must be > 0")
+ }
+
+ @Test
+ fun `GIVEN token fiat rate non-positive WHEN invoke THEN returns error`() = runTest {
+ val rawNetworkId = Blockchain.BSC.id
+ val token = createToken(rawNetworkId = rawNetworkId, decimals = 8)
+ val cryptoStatus = createStatus(token = token, fiatRate = BigDecimal.ZERO) // non-positive
+
+ coEvery { feeRepository.getEthereumFeeWithoutGas(userWalletId, token) } returns Fee.Ethereum.EIP1559(
+ maxFeePerGas = BigInteger.ONE,
+ priorityFee = BigInteger.ONE,
+ gasLimit = BigInteger.ZERO,
+ amount = BigDecimal.ZERO.convertToSdkAmount(
+ createStatus(token = token, fiatRate = BigDecimal.ONE),
+ ),
+ )
+
+ val result = useCase(userWalletId, cryptoStatus)
+
+ assertThat(result.isLeft()).isTrue()
+ val error = (result as Either.Left).value
+ assertThat(error).isInstanceOf(IllegalArgumentException::class.java)
+ assertThat(error.message).isEqualTo("Fiat rate for token must be > 0")
+ }
+
+ private fun createToken(rawNetworkId: String, decimals: Int): CryptoCurrency.Token {
+ val derivationPath = Network.DerivationPath.None
+ val network = Network(
+ id = Network.ID(value = rawNetworkId, derivationPath = derivationPath),
+ backendId = rawNetworkId,
+ name = rawNetworkId,
+ currencySymbol = rawNetworkId.take(3).uppercase(),
+ derivationPath = derivationPath,
+ isTestnet = false,
+ standardType = Network.StandardType.Unspecified("UNSPECIFIED"),
+ hasFiatFeeRate = true,
+ canHandleTokens = true,
+ transactionExtrasType = Network.TransactionExtrasType.NONE,
+ nameResolvingType = Network.NameResolvingType.NONE,
+ )
+
+ return CryptoCurrency.Token(
+ id = CryptoCurrency.ID(
+ prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
+ body = CryptoCurrency.ID.Body.NetworkId(rawNetworkId),
+ suffix = CryptoCurrency.ID.Suffix.RawID(rawNetworkId),
+ ),
+ network = network,
+ name = "TEST_TOKEN",
+ symbol = "TTK",
+ decimals = decimals,
+ iconUrl = null,
+ isCustom = false,
+ contractAddress = "0xToken",
+ )
+ }
+
+ private fun createCoin(rawNetworkId: String, decimals: Int): CryptoCurrency.Coin {
+ val derivationPath = Network.DerivationPath.None
+ val network = Network(
+ id = Network.ID(value = rawNetworkId, derivationPath = derivationPath),
+ backendId = rawNetworkId,
+ name = rawNetworkId,
+ currencySymbol = rawNetworkId.take(3).uppercase(),
+ derivationPath = derivationPath,
+ isTestnet = false,
+ standardType = Network.StandardType.Unspecified("UNSPECIFIED"),
+ hasFiatFeeRate = true,
+ canHandleTokens = true,
+ transactionExtrasType = Network.TransactionExtrasType.NONE,
+ nameResolvingType = Network.NameResolvingType.NONE,
+ )
+
+ return CryptoCurrency.Coin(
+ id = CryptoCurrency.ID(
+ prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
+ body = CryptoCurrency.ID.Body.NetworkId(rawNetworkId),
+ suffix = CryptoCurrency.ID.Suffix.RawID(rawNetworkId),
+ ),
+ network = network,
+ name = "TEST_COIN",
+ symbol = "TCN",
+ decimals = decimals,
+ iconUrl = null,
+ isCustom = false,
+ )
+ }
+
+ private fun createStatus(token: CryptoCurrency.Token, fiatRate: BigDecimal?): CryptoCurrencyStatus {
+ return CryptoCurrencyStatus(
+ currency = token,
+ value = CryptoCurrencyStatus.Custom(
+ amount = BigDecimal.ZERO,
+ fiatAmount = BigDecimal.ZERO,
+ fiatRate = fiatRate,
+ priceChange = BigDecimal.ZERO,
+ yieldBalance = null,
+ yieldSupplyStatus = null,
+ hasCurrentNetworkTransactions = false,
+ pendingTransactions = emptySet(),
+ networkAddress = NetworkAddress.Single(
+ defaultAddress = NetworkAddress.Address(
+ value = "0x0000000000000000000000000000000000000000",
+ type = NetworkAddress.Address.Type.Primary,
+ ),
+ ),
+ sources = CryptoCurrencyStatus.Sources(),
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt
index d468e6d60d..e18a49c84a 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt
@@ -283,7 +283,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
YieldSupplyActiveFeeContentTransformer(
cryptoCurrencyStatus = cryptoStatus,
appCurrency = appCurrency,
- feeValue = currentFee,
+ feeValue = currentFee.value,
maxNetworkFee = maxFee,
analyticsHandler = analyticsHandler,
),
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/YieldSupplyNotificationsComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/YieldSupplyNotificationsComponent.kt
index 366490ead7..0e5ade929b 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/YieldSupplyNotificationsComponent.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/YieldSupplyNotificationsComponent.kt
@@ -43,7 +43,6 @@ internal class YieldSupplyNotificationsComponent(
Modifier.padding(top = 16.dp)
},
containerColor = TangemTheme.colors.background.action,
- iconTint = null,
)
}
}
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/entity/YieldSupplyNotificationData.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/entity/YieldSupplyNotificationData.kt
index 7b9a1e53ef..446047a107 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/entity/YieldSupplyNotificationData.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/entity/YieldSupplyNotificationData.kt
@@ -6,4 +6,5 @@ import java.math.BigDecimal
data class YieldSupplyNotificationData(
val feeValue: BigDecimal?,
val feeError: GetFeeError?,
+ val shouldShowHighFeeNotification: Boolean = false,
)
\ No newline at end of file
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/model/YieldSupplyNotificationsModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/model/YieldSupplyNotificationsModel.kt
index 77907f4839..2e81b51d3f 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/model/YieldSupplyNotificationsModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/notifications/model/YieldSupplyNotificationsModel.kt
@@ -74,6 +74,10 @@ internal class YieldSupplyNotificationsModel @Inject constructor(
dustValue = null,
onReload = params.callback::onFeeReload,
)
+
+ if (data.shouldShowHighFeeNotification) {
+ add(NotificationUM.Info.YieldSupplyHighNetworkFee)
+ }
}
if (notifications.any { it is NotificationUM.Error.TokenExceedsBalance }) {
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt
index a41f836310..6a2d3d2597 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt
@@ -190,7 +190,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
updatedTransactionList = updatedTransactionList,
feeValue = feeSum,
maxNetworkFee = maxFee,
- estimatedFeeValueInTokenCurrency = estimatedFee,
+ estimatedFeeValueInTokenCurrency = estimatedFee.value,
minAmount = minAmount,
),
)
@@ -198,6 +198,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
data = YieldSupplyNotificationData(
feeValue = feeSum,
feeError = null,
+ shouldShowHighFeeNotification = estimatedFee.isHighFee,
),
)
},