Updated on 2026-08-14
This commit is contained in:
parent
b2189b3895
commit
4f3d1f10c7
11 changed files with 268 additions and 80 deletions
|
|
@ -1,10 +1,8 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.FeeState
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
|
|
@ -17,31 +15,23 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
internal class SetConfirmationStateAssentTransformer(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
private val stakingGasEstimate: StakingGasEstimate,
|
||||
private val fee: Fee,
|
||||
private val pendingActionList: ImmutableList<PendingAction>,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
return prevState.copy(
|
||||
confirmationState = prevState.confirmationState.copyWrapped(stakingGasEstimate),
|
||||
confirmationState = prevState.confirmationState.copyWrapped(fee),
|
||||
)
|
||||
}
|
||||
|
||||
private fun StakingStates.ConfirmationState.copyWrapped(
|
||||
gasEstimate: StakingGasEstimate,
|
||||
): StakingStates.ConfirmationState {
|
||||
private fun StakingStates.ConfirmationState.copyWrapped(fee: Fee): StakingStates.ConfirmationState {
|
||||
if (this is StakingStates.ConfirmationState.Data) {
|
||||
val isFeeConvertibleToFiat = feeCryptoCurrencyStatus?.currency?.network?.hasFiatFeeRate == true
|
||||
return copy(
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
feeState = FeeState.Content(
|
||||
fee = Fee.Common(
|
||||
Amount(
|
||||
currencySymbol = gasEstimate.token.symbol,
|
||||
value = gasEstimate.amount,
|
||||
decimals = gasEstimate.token.decimals,
|
||||
),
|
||||
),
|
||||
fee = fee,
|
||||
rate = feeCryptoCurrencyStatus?.value?.fiatRate,
|
||||
isFeeConvertibleToFiat = isFeeConvertibleToFiat,
|
||||
appCurrency = appCurrencyProvider(),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
|
|
@ -15,32 +13,24 @@ import com.tangem.utils.transformer.Transformer
|
|||
internal class SetConfirmationStateCompletedTransformer(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
private val stakingGasEstimate: StakingGasEstimate,
|
||||
private val fee: Fee,
|
||||
private val txUrl: String,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
return prevState.copy(
|
||||
confirmationState = prevState.confirmationState.copyWrapped(stakingGasEstimate),
|
||||
confirmationState = prevState.confirmationState.copyWrapped(fee),
|
||||
)
|
||||
}
|
||||
|
||||
private fun StakingStates.ConfirmationState.copyWrapped(
|
||||
gasEstimate: StakingGasEstimate,
|
||||
): StakingStates.ConfirmationState {
|
||||
private fun StakingStates.ConfirmationState.copyWrapped(fee: Fee): StakingStates.ConfirmationState {
|
||||
if (this is StakingStates.ConfirmationState.Data) {
|
||||
val isFeeConvertibleToFiat = feeCryptoCurrencyStatus?.currency?.network?.hasFiatFeeRate == true
|
||||
return copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
innerState = InnerConfirmationStakingState.COMPLETED,
|
||||
feeState = FeeState.Content(
|
||||
fee = Fee.Common(
|
||||
Amount(
|
||||
currencySymbol = gasEstimate.token.symbol,
|
||||
value = gasEstimate.amount,
|
||||
decimals = gasEstimate.token.decimals,
|
||||
),
|
||||
),
|
||||
fee = fee,
|
||||
rate = feeCryptoCurrencyStatus?.value?.fiatRate,
|
||||
isFeeConvertibleToFiat = isFeeConvertibleToFiat,
|
||||
appCurrency = appCurrencyProvider(),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import androidx.lifecycle.SavedStateHandle
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
|
|
@ -28,7 +30,6 @@ import com.tangem.domain.staking.model.stakekit.PendingAction
|
|||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
|
||||
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
|
||||
|
|
@ -37,10 +38,7 @@ import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
|
|||
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
|
||||
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.transaction.usecase.*
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
|
|
@ -70,6 +68,7 @@ import kotlinx.coroutines.*
|
|||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
|
@ -87,6 +86,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val getConstructedStakingTransactionUseCase: GetConstructedStakingTransactionUseCase,
|
||||
private val estimateGasUseCase: EstimateGasUseCase,
|
||||
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||
private val sendMultipleTransactionUseCase: SendMultipleTransactionUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
|
||||
private val submitHashUseCase: SubmitHashUseCase,
|
||||
|
|
@ -143,7 +143,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private var stakingApproval: StakingApproval = StakingApproval.Empty
|
||||
private val allowanceTaskScheduler = SingleTaskScheduler<BigDecimal>()
|
||||
|
||||
private var transactionInProgress: StakingTransaction? = null
|
||||
private val transactionsInProgress: CopyOnWriteArrayList<StakingTransaction> = CopyOnWriteArrayList()
|
||||
|
||||
private var approvalJobHolder: JobHolder = JobHolder()
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
val defaultAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
|
||||
?: error("No available address")
|
||||
|
||||
val stakingTransaction = getStakingTransactionUseCase(
|
||||
val stakingTransactions = getStakingTransactionUseCase(
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
params = ActionParams(
|
||||
|
|
@ -206,9 +206,9 @@ internal class StakingViewModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
stakingTransaction
|
||||
val fullTransactionsData = stakingTransactions
|
||||
.filterNot { it.type == StakingTransactionType.APPROVAL }
|
||||
.forEach { transaction ->
|
||||
.map { transaction ->
|
||||
val (constructedTransaction, transactionData) = getConstructedStakingTransactionUseCase(
|
||||
networkId = cryptoCurrencyStatus.currency.network.id.value,
|
||||
fee = fee,
|
||||
|
|
@ -218,17 +218,20 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingEventFactory.createStakingErrorAlert(it)
|
||||
return@launch
|
||||
}
|
||||
val gasEstimate = constructedTransaction.gasEstimate
|
||||
?: return@launch stakingEventFactory.createGenericErrorAlert("Gas estimate is null")
|
||||
|
||||
transactionInProgress = constructedTransaction
|
||||
sendStakingTransaction(
|
||||
transactionId = constructedTransaction.id,
|
||||
gasEstimate = gasEstimate,
|
||||
txData = transactionData,
|
||||
pendingActionList = confirmationState.pendingActions,
|
||||
FullTransactionData(
|
||||
stakeKitTransaction = constructedTransaction,
|
||||
tangemTransaction = transactionData,
|
||||
)
|
||||
}
|
||||
|
||||
transactionsInProgress.addAll(fullTransactionsData.map { it.stakeKitTransaction })
|
||||
|
||||
sendStakingTransaction(
|
||||
fullTransactionsData = fullTransactionsData,
|
||||
fee = fee,
|
||||
pendingActionList = confirmationState.pendingActions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -291,7 +294,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
validatorAddress: String,
|
||||
) {
|
||||
val pendingAction = pendingActions.firstOrNull()
|
||||
val stakingGasEstimate = estimateGasUseCase(
|
||||
val gasEstimate = estimateGasUseCase(
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
params = ActionParams(
|
||||
|
|
@ -313,7 +316,13 @@ internal class StakingViewModel @Inject constructor(
|
|||
SetConfirmationStateAssentTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
stakingGasEstimate = stakingGasEstimate,
|
||||
fee = Fee.Common(
|
||||
Amount(
|
||||
currencySymbol = gasEstimate.token.symbol,
|
||||
value = gasEstimate.amount,
|
||||
decimals = gasEstimate.token.decimals,
|
||||
),
|
||||
),
|
||||
pendingActionList = pendingActions,
|
||||
),
|
||||
)
|
||||
|
|
@ -555,8 +564,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
val email = FeedbackEmailType.StakingProblem(
|
||||
cardInfo = cardInfo,
|
||||
validatorName = validator?.name,
|
||||
transactionType = transactionInProgress?.type?.name,
|
||||
unsignedTransaction = transactionInProgress?.unsignedTransaction,
|
||||
transactionTypes = transactionsInProgress.map { it.type.name },
|
||||
unsignedTransactions = transactionsInProgress.map { it.unsignedTransaction },
|
||||
)
|
||||
|
||||
feedbackManager.sendEmail(email)
|
||||
|
|
@ -632,13 +641,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun sendStakingTransaction(
|
||||
transactionId: String,
|
||||
gasEstimate: StakingGasEstimate,
|
||||
txData: TransactionData,
|
||||
fullTransactionsData: List<FullTransactionData>,
|
||||
fee: Fee,
|
||||
pendingActionList: ImmutableList<PendingAction>,
|
||||
) {
|
||||
sendTransactionUseCase(
|
||||
txData = txData,
|
||||
sendMultipleTransactionUseCase(
|
||||
txsData = fullTransactionsData.map { it.tangemTransaction },
|
||||
userWallet = userWallet,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
).fold(
|
||||
|
|
@ -648,18 +656,21 @@ internal class StakingViewModel @Inject constructor(
|
|||
SetConfirmationStateAssentTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
stakingGasEstimate = gasEstimate,
|
||||
fee = fee,
|
||||
pendingActionList = pendingActionList,
|
||||
),
|
||||
)
|
||||
stakingEventFactory.createSendTransactionErrorAlert(error)
|
||||
},
|
||||
ifRight = { txHash ->
|
||||
transactionInProgress = null
|
||||
submitHash(transactionId, txHash)
|
||||
ifRight = { transactionHashes ->
|
||||
transactionsInProgress.clear()
|
||||
submitHash(
|
||||
transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id },
|
||||
transactionHashes = transactionHashes,
|
||||
)
|
||||
scheduleUpdates()
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = txHash,
|
||||
txHash = transactionHashes.last(),
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
).getOrElse { "" }
|
||||
|
||||
|
|
@ -667,7 +678,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
SetConfirmationStateCompletedTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
stakingGasEstimate = gasEstimate,
|
||||
fee = fee,
|
||||
txUrl = txUrl,
|
||||
),
|
||||
)
|
||||
|
|
@ -675,18 +686,22 @@ internal class StakingViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun submitHash(transactionId: String, transactionHash: String) {
|
||||
submitHashUseCase.submitHash(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
.onLeft {
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
private suspend fun submitHash(transactionIds: List<String>, transactionHashes: List<String>) {
|
||||
transactionIds
|
||||
.zip(transactionHashes)
|
||||
.forEach { (transactionId, transactionHash) ->
|
||||
submitHashUseCase.submitHash(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
.onLeft {
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -750,6 +765,11 @@ internal class StakingViewModel @Inject constructor(
|
|||
InnerConfirmationStakingState.ASSENT
|
||||
}
|
||||
|
||||
private data class FullTransactionData(
|
||||
val stakeKitTransaction: StakingTransaction,
|
||||
val tangemTransaction: TransactionData.Compiled,
|
||||
)
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue