Updated on 2026-08-14
This commit is contained in:
parent
f8a9cac801
commit
f72c0f448e
13 changed files with 409 additions and 116 deletions
|
|
@ -97,6 +97,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstake
|
|||
import com.tangem.lib.crypto.BlockchainUtils.isTon
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
|
@ -642,6 +643,31 @@ internal class StakingModel @Inject constructor(
|
|||
integration = integration,
|
||||
),
|
||||
)
|
||||
checkSumLimitExceeded()
|
||||
}
|
||||
|
||||
private fun checkSumLimitExceeded() {
|
||||
val maxLimit = (integration as? P2PEthPoolIntegration)
|
||||
?.enterArgs?.amountRequirement?.maximum
|
||||
?.takeIf { it.isPositive() }
|
||||
?: return
|
||||
|
||||
val enteredAmount = (uiState.value.amountState as? AmountState.Data)
|
||||
?.amountTextField?.cryptoAmount?.value
|
||||
?: return
|
||||
|
||||
if (enteredAmount > maxLimit) {
|
||||
// Pass the max limit as a stable crypto-formatted value (e.g. "0.15 ETH"); the event
|
||||
// builds the hardcoded English "Error Message" from it, so the model needs no resources.
|
||||
val formattedMax = maxLimit.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvent.SumLimitError(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
maxAmount = formattedMax,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAmountPasteTriggerDismiss() {
|
||||
|
|
@ -658,6 +684,7 @@ internal class StakingModel @Inject constructor(
|
|||
integration = integration,
|
||||
),
|
||||
)
|
||||
checkSumLimitExceeded()
|
||||
}
|
||||
|
||||
override fun onCurrencyChangeClick(isFiat: Boolean) {
|
||||
|
|
@ -1099,7 +1126,12 @@ internal class StakingModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun showPrimaryClickAlert() {
|
||||
messageSender.send(StakingAlertUM.stakeMoreClickUnavailable(cryptoCurrencyStatus.currency))
|
||||
val message = if (integration is P2PEthPoolIntegration) {
|
||||
StakingAlertUM.stakeMoreClickUnavailableNoTargets()
|
||||
} else {
|
||||
StakingAlertUM.stakeMoreClickUnavailable(cryptoCurrencyStatus.currency)
|
||||
}
|
||||
messageSender.send(message)
|
||||
}
|
||||
|
||||
override fun onOpenLearnMoreAboutApproveClick() {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ internal object StakingAlertUM {
|
|||
),
|
||||
)
|
||||
|
||||
fun stakeMoreClickUnavailableNoTargets(): DialogMessage = DialogMessage(
|
||||
title = null,
|
||||
message = resourceReference(R.string.staking_no_validators_error_message),
|
||||
)
|
||||
|
||||
fun rewardsMinimumRequirementsError(cryptoCurrencyName: String, cryptoAmountValue: String): DialogMessage =
|
||||
DialogMessage(
|
||||
title = null,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import com.tangem.domain.models.account.Account
|
|||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.StakingBalanceEntry
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.staking.model.P2PEthPoolIntegration
|
||||
import com.tangem.domain.staking.model.Period
|
||||
import com.tangem.domain.staking.model.StakingIntegration
|
||||
import com.tangem.domain.staking.model.StakingTarget
|
||||
|
|
@ -262,7 +261,6 @@ internal class SetInitialDataStateTransformer(
|
|||
walletTitle = stringReference(userWalletProvider().name),
|
||||
prefixText = resourceReference(R.string.common_from),
|
||||
).convert(account),
|
||||
isMaxButtonVisible = integration !is P2PEthPoolIntegration,
|
||||
).convert(
|
||||
AmountParameters(
|
||||
title = stringReference(userWalletProvider().name),
|
||||
|
|
|
|||
|
|
@ -96,12 +96,18 @@ internal class AmountRequirementStateTransformer(
|
|||
|
||||
return when (actionType) {
|
||||
is StakingActionCommonType.Enter -> {
|
||||
val enterRequirements = integration.enterArgs?.amountRequirement
|
||||
enterRequirements?.getError(amountDecimal, R.string.staking_amount_requirement_error)
|
||||
integration.enterArgs?.amountRequirement?.getError(
|
||||
amount = amountDecimal,
|
||||
minErrorRes = R.string.staking_amount_requirement_error,
|
||||
maxErrorRes = R.string.staking_max_amount_requirement_error,
|
||||
)
|
||||
}
|
||||
is StakingActionCommonType.Exit -> {
|
||||
val exitRequirements = integration.exitArgs?.amountRequirement
|
||||
exitRequirements?.getError(amountDecimal, R.string.staking_unstake_amount_requirement_error)
|
||||
integration.exitArgs?.amountRequirement?.getError(
|
||||
amount = amountDecimal,
|
||||
minErrorRes = R.string.staking_unstake_amount_requirement_error,
|
||||
maxErrorRes = R.string.staking_max_amount_requirement_error,
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -118,31 +124,25 @@ internal class AmountRequirementStateTransformer(
|
|||
return isEnterOrExit && isTron && !isIntegerOnly
|
||||
}
|
||||
|
||||
private fun StakingAmountRequirement.getError(amount: BigDecimal, @StringRes errorTextRes: Int): TextReference? {
|
||||
private fun StakingAmountRequirement.getError(
|
||||
amount: BigDecimal,
|
||||
@StringRes minErrorRes: Int,
|
||||
@StringRes maxErrorRes: Int,
|
||||
): TextReference? {
|
||||
if (!isRequired) return null
|
||||
|
||||
val isExceedsMinRequirement = minimum?.compareTo(amount) == 1
|
||||
val isExceedsMaxRequirement = if (maximum?.isPositive() == true) {
|
||||
maximum?.compareTo(amount) == -1
|
||||
} else {
|
||||
maxAmount.amount?.compareTo(amount) == -1
|
||||
val effectiveMax = maximum?.takeIf { it.isPositive() } ?: maxAmount.amount
|
||||
val isExceedsMaxRequirement = effectiveMax?.compareTo(amount) == -1
|
||||
|
||||
val (errorRes, boundary) = when {
|
||||
isExceedsMinRequirement -> minErrorRes to minimum
|
||||
isExceedsMaxRequirement -> maxErrorRes to effectiveMax
|
||||
else -> return null
|
||||
}
|
||||
|
||||
val errorText = when {
|
||||
isExceedsMinRequirement -> {
|
||||
minimum.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
}
|
||||
}
|
||||
isExceedsMaxRequirement -> {
|
||||
maximum.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
}
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
return resourceReference(
|
||||
errorTextRes,
|
||||
wrappedList(errorText),
|
||||
).takeIf { isRequired && (isExceedsMinRequirement || isExceedsMaxRequirement) }
|
||||
val formatted = boundary.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
return resourceReference(errorRes, wrappedList(formatted))
|
||||
}
|
||||
|
||||
data class Data(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
package com.tangem.features.staking.impl.presentation.model
|
||||
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.model.ethpool.P2PEthPoolVault
|
||||
import com.tangem.domain.staking.model.ethpool.VaultLimitInfo
|
||||
import com.tangem.domain.tokens.model.Amount
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStep
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Model-level tests for the P2P ETH pool staking integration:
|
||||
* verifies that [StakingAnalyticsEvent.SumLimitError] is sent when the entered amount
|
||||
* exceeds the vault's computed maximum (= limit − totalAssets).
|
||||
*
|
||||
* Fixture:
|
||||
* vault address = "0xabc" totalAssets = 5
|
||||
* limit "0xabc" limit = 10
|
||||
* → maximum = 10 − 5 = 5.0 (scale=1, RoundingMode.FLOOR)
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class StakingModelP2PSumLimitTest : StakingModelTestBase() {
|
||||
|
||||
override val testIntegrationId: StakingIntegrationID = StakingIntegrationID.P2PEthPool
|
||||
|
||||
private val vaultAddress = "0xabc"
|
||||
private val testVault = P2PEthPoolVault(
|
||||
vaultAddress = vaultAddress,
|
||||
displayName = "Test Vault",
|
||||
apy = BigDecimal("4.5"),
|
||||
baseApy = BigDecimal("4.0"),
|
||||
capacity = BigDecimal("1000"),
|
||||
totalAssets = BigDecimal("5"),
|
||||
feePercent = BigDecimal("0.1"),
|
||||
isPrivate = false,
|
||||
isGenesis = false,
|
||||
isSmoothingPool = false,
|
||||
isErc20 = false,
|
||||
tokenName = null,
|
||||
tokenSymbol = null,
|
||||
createdAt = 0L,
|
||||
)
|
||||
|
||||
// maximum = 10 − 5 = 5.0 (FLOOR scale=1)
|
||||
private val testLimits = mapOf(
|
||||
vaultAddress to VaultLimitInfo(limit = BigDecimal("10"), coefficient = null),
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setUpP2P() {
|
||||
coEvery { p2pEthPoolRepository.getVaultsSync() } returns listOf(testVault)
|
||||
coEvery { p2pEthPoolRepository.getVaultLimitsSyncOrNull() } returns testLimits
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: returns a [MutableStateFlow] whose value has [amountState] set to an
|
||||
* [AmountState.Data] mock with the given [cryptoAmountValue].
|
||||
* The flow is also wired to [stateController.uiState].
|
||||
*/
|
||||
private fun stubUiStateWithCryptoAmount(cryptoAmountValue: BigDecimal): MutableStateFlow<StakingUiState> {
|
||||
val amountData = mockk<AmountState.Data>(relaxed = true) {
|
||||
every { amountTextField } returns mockk(relaxed = true) {
|
||||
every { cryptoAmount } returns Amount(
|
||||
currencySymbol = "ETH",
|
||||
value = cryptoAmountValue,
|
||||
decimals = 18,
|
||||
)
|
||||
}
|
||||
}
|
||||
val uiStateFlow = MutableStateFlow(
|
||||
mockk<StakingUiState>(relaxed = true) {
|
||||
every { currentStep } returns StakingStep.InitialInfo
|
||||
every { amountState } returns amountData
|
||||
},
|
||||
)
|
||||
every { stateController.uiState } returns uiStateFlow
|
||||
return uiStateFlow
|
||||
}
|
||||
|
||||
// ----- Test A ---------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun `GIVEN P2P vault max=5 WHEN amount 6 entered THEN SumLimitError analytics sent`() = runTest {
|
||||
stubUiStateWithCryptoAmount(BigDecimal("6"))
|
||||
|
||||
val model = createModel(testScope = this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.onAmountValueChange("6")
|
||||
|
||||
verify {
|
||||
analyticsEventHandler.send(
|
||||
match { it is StakingAnalyticsEvent.SumLimitError }
|
||||
)
|
||||
}
|
||||
|
||||
model.onDestroy()
|
||||
}
|
||||
|
||||
// ----- Test B ---------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun `GIVEN P2P vault max=5 WHEN amount 4 entered THEN SumLimitError analytics NOT sent`() = runTest {
|
||||
stubUiStateWithCryptoAmount(BigDecimal("4"))
|
||||
|
||||
val model = createModel(testScope = this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.onAmountValueChange("4")
|
||||
|
||||
verify(exactly = 0) {
|
||||
analyticsEventHandler.send(
|
||||
match { it is StakingAnalyticsEvent.SumLimitError }
|
||||
)
|
||||
}
|
||||
|
||||
model.onDestroy()
|
||||
}
|
||||
}
|
||||
|
|
@ -57,8 +57,8 @@ internal abstract class StakingModelTestBase {
|
|||
|
||||
protected val testUserWalletId = UserWalletId("1234567890ABCDEF")
|
||||
protected val testCryptoCurrency: CryptoCurrency = mockk(relaxed = true)
|
||||
private val testIntegrationId = StakingIntegrationID.StakeKit.Coin.Solana
|
||||
private val testParams = StakingComponent.Params(
|
||||
protected open val testIntegrationId: StakingIntegrationID = StakingIntegrationID.StakeKit.Coin.Solana
|
||||
private val testParams get() = StakingComponent.Params(
|
||||
userWalletId = testUserWalletId,
|
||||
cryptoCurrency = testCryptoCurrency,
|
||||
integrationId = testIntegrationId,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.events
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.staking.impl.R
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class StakingAlertUMTest {
|
||||
|
||||
@Test
|
||||
fun `noTargets dialog uses no validators string and has no title`() {
|
||||
val message = StakingAlertUM.stakeMoreClickUnavailableNoTargets()
|
||||
|
||||
assertThat(message.title).isNull()
|
||||
assertThat((message.message as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_no_validators_error_message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `default stake more dialog uses stake more unavailability string`() {
|
||||
val currency: CryptoCurrency = mockk(relaxed = true)
|
||||
|
||||
val message = StakingAlertUM.stakeMoreClickUnavailable(currency)
|
||||
|
||||
assertThat(message.title).isNull()
|
||||
assertThat((message.message as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_stake_more_button_unavailability_reason)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers.amount
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.staking.model.StakingIntegration
|
||||
import com.tangem.domain.staking.model.common.StakingActionArgs
|
||||
import com.tangem.domain.staking.model.common.StakingAmountRequirement
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.tokens.model.Amount
|
||||
import com.tangem.features.staking.impl.R
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class AmountRequirementStateTransformerTest {
|
||||
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus = mockk(relaxed = true)
|
||||
|
||||
private fun amountState(enteredCrypto: BigDecimal): AmountState.Data = AmountState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
accountTitleUM = mockk(relaxed = true),
|
||||
availableBalanceCrypto = mockk(relaxed = true),
|
||||
availableBalanceFiat = mockk(relaxed = true),
|
||||
tokenName = mockk(relaxed = true),
|
||||
tokenIconState = mockk(relaxed = true),
|
||||
amountTextField = AmountFieldModel(
|
||||
value = enteredCrypto.toPlainString(),
|
||||
onValueChange = {},
|
||||
keyboardOptions = mockk(relaxed = true),
|
||||
keyboardActions = mockk(relaxed = true),
|
||||
cryptoAmount = Amount(currencySymbol = "ETH", value = enteredCrypto, decimals = 18),
|
||||
fiatAmount = Amount(currencySymbol = "USD", value = BigDecimal.ZERO, decimals = 2),
|
||||
isFiatValue = false,
|
||||
fiatValue = "0",
|
||||
isFiatUnavailable = false,
|
||||
isValuePasted = false,
|
||||
onValuePastedTriggerDismiss = {},
|
||||
isError = false,
|
||||
isWarning = false,
|
||||
error = stringReference(""),
|
||||
),
|
||||
appCurrency = mockk(relaxed = true),
|
||||
)
|
||||
|
||||
private fun enterIntegrationWith(minimum: BigDecimal?, maximum: BigDecimal?): StakingIntegration = mockk {
|
||||
every { enterArgs } returns StakingActionArgs(
|
||||
amountRequirement = StakingAmountRequirement(
|
||||
isRequired = true,
|
||||
minimum = minimum,
|
||||
maximum = maximum,
|
||||
),
|
||||
isPartialAmountDisabled = false,
|
||||
)
|
||||
}
|
||||
|
||||
private fun exitIntegrationWith(minimum: BigDecimal?, maximum: BigDecimal?): StakingIntegration = mockk {
|
||||
every { exitArgs } returns StakingActionArgs(
|
||||
amountRequirement = StakingAmountRequirement(
|
||||
isRequired = true,
|
||||
minimum = minimum,
|
||||
maximum = maximum,
|
||||
),
|
||||
isPartialAmountDisabled = false,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN amount exceeds positive maximum THEN max amount error string is used`() {
|
||||
val transformer = AmountRequirementStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = EnterAmountBoundary(amount = BigDecimal("100"), fiatAmount = null, fiatRate = null),
|
||||
integration = enterIntegrationWith(minimum = BigDecimal("0.01"), maximum = BigDecimal("0.15")),
|
||||
actionType = StakingActionCommonType.Enter(skipEnterAmount = false),
|
||||
)
|
||||
|
||||
val result = transformer.transform(amountState(BigDecimal("0.2"))) as AmountState.Data
|
||||
|
||||
assertThat(result.amountTextField.isError).isTrue()
|
||||
assertThat((result.amountTextField.error as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_max_amount_requirement_error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN amount below minimum THEN min amount error string is used`() {
|
||||
val transformer = AmountRequirementStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = EnterAmountBoundary(amount = BigDecimal("100"), fiatAmount = null, fiatRate = null),
|
||||
integration = enterIntegrationWith(minimum = BigDecimal("0.1"), maximum = null),
|
||||
actionType = StakingActionCommonType.Enter(skipEnterAmount = false),
|
||||
)
|
||||
|
||||
val result = transformer.transform(amountState(BigDecimal("0.05"))) as AmountState.Data
|
||||
|
||||
assertThat(result.amountTextField.isError).isTrue()
|
||||
assertThat((result.amountTextField.error as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_amount_requirement_error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Exit action and amount below exit minimum THEN unstake min error string is used`() {
|
||||
val transformer = AmountRequirementStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = EnterAmountBoundary(amount = BigDecimal("100"), fiatAmount = null, fiatRate = null),
|
||||
integration = exitIntegrationWith(minimum = BigDecimal("0.1"), maximum = null),
|
||||
actionType = StakingActionCommonType.Exit(partiallyUnstakeDisabled = false),
|
||||
)
|
||||
|
||||
val result = transformer.transform(amountState(BigDecimal("0.05"))) as AmountState.Data
|
||||
|
||||
assertThat(result.amountTextField.isError).isTrue()
|
||||
assertThat((result.amountTextField.error as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_unstake_amount_requirement_error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Enter action and maximum is null and amount exceeds balance cap THEN max error string is used`() {
|
||||
val transformer = AmountRequirementStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = EnterAmountBoundary(amount = BigDecimal("0.5"), fiatAmount = null, fiatRate = null),
|
||||
integration = enterIntegrationWith(minimum = BigDecimal("0.01"), maximum = null),
|
||||
actionType = StakingActionCommonType.Enter(skipEnterAmount = false),
|
||||
)
|
||||
|
||||
val result = transformer.transform(amountState(BigDecimal("0.6"))) as AmountState.Data
|
||||
|
||||
assertThat(result.amountTextField.isError).isTrue()
|
||||
assertThat((result.amountTextField.error as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_max_amount_requirement_error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Exit action and amount exceeds staked balance THEN max amount error string is used`() {
|
||||
val transformer = AmountRequirementStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = EnterAmountBoundary(amount = BigDecimal("0.5"), fiatAmount = null, fiatRate = null),
|
||||
integration = exitIntegrationWith(minimum = BigDecimal("0.01"), maximum = null),
|
||||
actionType = StakingActionCommonType.Exit(partiallyUnstakeDisabled = false),
|
||||
)
|
||||
|
||||
val result = transformer.transform(amountState(BigDecimal("0.6"))) as AmountState.Data
|
||||
|
||||
assertThat(result.amountTextField.isError).isTrue()
|
||||
assertThat((result.amountTextField.error as TextReference.Res).id)
|
||||
.isEqualTo(R.string.staking_max_amount_requirement_error)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue