Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-30 19:40:14 +05:00
commit ad568e3c32
7 changed files with 65 additions and 36 deletions

View file

@ -118,6 +118,7 @@ internal sealed class StakingStates {
val footerText: TextReference,
val transactionDoneState: TransactionDoneState,
val isApprovalNeeded: Boolean,
val allowance: BigDecimal,
val reduceAmountBy: BigDecimal?,
) : ConfirmationState()

View file

@ -8,7 +8,6 @@ import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.extensions.isZero
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.domain.staking.EstimateGasUseCase
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.stakekit.PendingAction
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.stakekit.Yield
@ -17,7 +16,6 @@ import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.staking.impl.presentation.state.StakingStateController
@ -37,13 +35,11 @@ import java.math.BigDecimal
@Suppress("LongParameterList")
internal class StakingFeeTransactionLoader @AssistedInject constructor(
private val stateController: StakingStateController,
private val getAllowanceUseCase: GetAllowanceUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val estimateGasUseCase: EstimateGasUseCase,
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
@Assisted private val userWallet: UserWallet,
@Assisted private val yield: Yield,
@Assisted private val stakingApproval: StakingApproval,
) {
suspend fun getFee(
@ -66,31 +62,16 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
val validatorAddress = validatorState.chosenValidator.address
val approval = stakingApproval as? StakingApproval.Needed
if (approval != null && state.actionType == StakingActionCommonType.Enter) {
val allowance = getAllowanceUseCase(
userWalletId = userWallet.walletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
spenderAddress = approval.spenderAddress,
).getOrElse { BigDecimal.ZERO }
if (allowance < amount) {
getApproveFee(
amount = amount,
validatorAddress = validatorAddress,
onApprovalFee = onApprovalFee,
onApprovalFeeError = onFeeError,
)
} else {
estimateGas(
pendingAction = pendingAction,
pendingActions = pendingActions,
amount = amount,
validatorAddress = validatorAddress,
onStakingFeeError = onStakingFeeError,
onStakingFee = onStakingFee,
)
}
val isEnter = state.actionType == StakingActionCommonType.Enter
val isApprovalNeeded = confirmationState.isApprovalNeeded
val isAllowanceNotEnough = confirmationState.allowance < amount
if (isEnter && isApprovalNeeded && isAllowanceNotEnough) {
getApproveFee(
amount = amount,
validatorAddress = validatorAddress,
onApprovalFee = onApprovalFee,
onApprovalFeeError = onFeeError,
)
} else {
estimateGas(
pendingAction = pendingAction,
@ -234,7 +215,6 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
cryptoCurrencyStatus: CryptoCurrencyStatus,
userWallet: UserWallet,
yield: Yield,
stakingApproval: StakingApproval,
): StakingFeeTransactionLoader
}
}

View file

@ -45,6 +45,7 @@ internal object ConfirmationStatePreviewData {
transactionDoneState = TransactionDoneState.Empty,
pendingAction = null,
isApprovalNeeded = false,
allowance = BigDecimal.ZERO,
reduceAmountBy = null,
pendingActions = null,
)

View file

@ -1,5 +1,6 @@
package com.tangem.features.staking.impl.presentation.state.transformers
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationButton
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
import com.tangem.core.navigation.url.UrlOpener
@ -9,6 +10,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.utils.getPendingActionTitle
import com.tangem.utils.extensions.orZero
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -116,13 +118,16 @@ internal class SetButtonsStateTransformer(
}
private fun StakingUiState.getConfirmationButtonText(): TextReference {
return if (confirmationState is StakingStates.ConfirmationState.Data) {
val confirmationState = confirmationState as? StakingStates.ConfirmationState.Data
val amountState = amountState as? AmountState.Data
return if (confirmationState != null && amountState != null) {
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) {
resourceReference(R.string.common_close)
} else {
when (actionType) {
StakingActionCommonType.Enter -> {
if (confirmationState.isApprovalNeeded) {
val amount = amountState.amountTextField.cryptoAmount.value.orZero()
if (confirmationState.isApprovalNeeded && confirmationState.allowance < amount) {
resourceReference(R.string.give_permission_title)
} else {
resourceReference(R.string.common_stake)

View file

@ -12,10 +12,12 @@ import com.tangem.features.staking.impl.presentation.state.utils.isTronStakedBal
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
internal class SetConfirmationStateInitTransformer(
private val isEnter: Boolean,
private val stakingApproval: StakingApproval,
private val stakingAllowance: BigDecimal,
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val pendingActions: ImmutableList<PendingAction>? = null,
private val pendingAction: PendingAction? = pendingActions?.firstOrNull(),
@ -60,6 +62,7 @@ internal class SetConfirmationStateInitTransformer(
footerText = TextReference.EMPTY,
transactionDoneState = TransactionDoneState.Empty,
isApprovalNeeded = stakingApproval is StakingApproval.Needed,
allowance = stakingAllowance,
reduceAmountBy = null,
pendingAction = pendingAction,
pendingActions = pendingActions.takeIf { isComposePendingActions },

View file

@ -0,0 +1,17 @@
package com.tangem.features.staking.impl.presentation.state.transformers.confirmation
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.utils.transformer.Transformer
import java.math.BigDecimal
internal class SetUpdatedAllowanceTransformer(
private val allowance: BigDecimal,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
return prevState.copy(
confirmationState = confirmationState?.copy(allowance = allowance) ?: prevState.confirmationState,
)
}
}

View file

@ -26,7 +26,10 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.staking.*
import com.tangem.domain.staking.GetActionsUseCase
import com.tangem.domain.staking.InvalidatePendingTransactionsUseCase
import com.tangem.domain.staking.IsAnyTokenStakedUseCase
import com.tangem.domain.staking.IsApproveNeededUseCase
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.stakekit.BalanceItem
import com.tangem.domain.staking.model.stakekit.PendingAction
@ -63,6 +66,7 @@ import com.tangem.features.staking.impl.presentation.state.helpers.StakingTransa
import com.tangem.features.staking.impl.presentation.state.transformers.*
import com.tangem.features.staking.impl.presentation.state.transformers.amount.*
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.validator.ValidatorSelectChangeTransformer
@ -167,7 +171,6 @@ internal class StakingViewModel @Inject constructor(
cryptoCurrencyStatus = cryptoCurrencyStatus,
userWallet = userWallet,
yield = yield,
stakingApproval = stakingApproval,
)
}
@ -192,6 +195,7 @@ internal class StakingViewModel @Inject constructor(
)
private var stakingApproval: StakingApproval = StakingApproval.Empty
private var stakingAllowance: BigDecimal = BigDecimal.ZERO
private var isAmountSubtractAvailable: Boolean = false
private var isAnyTokenStaked: Boolean = false
private val allowanceTaskScheduler = SingleTaskScheduler<BigDecimal>()
@ -357,6 +361,7 @@ internal class StakingViewModel @Inject constructor(
isEnter = true,
cryptoCurrencyStatus = cryptoCurrencyStatus,
stakingApproval = stakingApproval,
stakingAllowance = stakingAllowance,
),
ValidatorSelectChangeTransformer(
selectedValidator = null,
@ -651,9 +656,11 @@ internal class StakingViewModel @Inject constructor(
}
},
onSuccess = { allowance ->
stakingAllowance = allowance
val amount = (value.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
?: error("No amount provided")
if (allowance >= amount) {
stateController.update(SetUpdatedAllowanceTransformer(allowance))
getFee()
allowanceTaskScheduler.cancelTask()
}
@ -730,8 +737,22 @@ internal class StakingViewModel @Inject constructor(
this.stakingStateRouter = stateRouter
}
private fun setupApprovalNeeded() {
stakingApproval = isApproveNeededUseCase(cryptoCurrencyStatus.currency).getOrElse { StakingApproval.Empty }
private suspend fun setupApprovalNeeded() {
stakingApproval = isApproveNeededUseCase(cryptoCurrencyStatus.currency).fold(
ifRight = { approval ->
if (approval is StakingApproval.Needed) {
stakingAllowance = getAllowanceUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
spenderAddress = approval.spenderAddress,
).getOrElse { BigDecimal.ZERO }
}
approval
},
ifLeft = {
StakingApproval.Empty
},
)
}
private suspend fun setupIsAnyTokenStaked() {
@ -886,6 +907,7 @@ internal class StakingViewModel @Inject constructor(
stakingApproval = stakingApproval,
pendingActions = pendingActions,
pendingAction = pendingAction,
stakingAllowance = stakingAllowance,
),
ValidatorSelectChangeTransformer(
selectedValidator = validator,