Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-29 15:59:09 +03:00
parent e5c8b6dc57
commit 427c7aed11
15 changed files with 1 additions and 476 deletions

View file

@ -184,7 +184,6 @@ abstract class BaseTestCase : TestCase(
toggleStates = mapOf(
"SWAP_REDESIGN_ENABLED" to false,
"ACCOUNTS_FEATURE_ENABLED" to true,
"GASLESS_APPROVAL_ENABLED" to true,
"MAIN_SCREEN_QR_SCANNING_ENABLED" to true,
"ADD_AND_MANAGE_TOKENS_ENABLED" to true,
"ASSETS_DISCOVERY_ENABLED" to true,

View file

@ -23,10 +23,6 @@
"name": "APP_REDESIGN_ENABLED",
"version": "6.0"
},
{
"name": "GASLESS_APPROVAL_ENABLED",
"version": "5.37"
},
{
"name": "DYNAMIC_ADDRESSES_ENABLED",
"version": "5.39"

View file

@ -43,7 +43,6 @@ internal class FeatureTogglesNamingConventionTest {
"APP_REDESIGN_ENABLED",
"ASSETS_DISCOVERY_ENABLED",
"DYNAMIC_ADDRESSES_ENABLED",
"GASLESS_APPROVAL_ENABLED",
"HEDERA_ERC20_ENABLED",
"NEW_CARD_SCANNING_ENABLED",
"SOLANA_SCALED_UI_AMOUNT_ENABLED",

View file

@ -1,6 +0,0 @@
package com.tangem.features.approval.api
interface GiveApprovalFeatureToggles {
val isGaslessApprovalEnabled: Boolean
}

View file

@ -1,15 +0,0 @@
package com.tangem.features.approval.impl
import com.tangem.core.configtoggle.FeatureToggles
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import javax.inject.Inject
internal class DefaultGiveApprovalFeatureToggles @Inject constructor(
private val featureToggles: FeatureTogglesManager,
) : GiveApprovalFeatureToggles {
// Remove GiveTxPermissionBottomSheet and all dependencies with this toggle
override val isGaslessApprovalEnabled: Boolean
get() = featureToggles.isFeatureEnabled(FeatureToggles.GASLESS_APPROVAL_ENABLED)
}

View file

@ -4,11 +4,9 @@ import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.approval.api.GiveApprovalComponent
import com.tangem.features.approval.api.GiveApprovalEntryComponent
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import com.tangem.features.approval.api.SelectApprovalTypeComponent
import com.tangem.features.approval.impl.DefaultGiveApprovalComponent
import com.tangem.features.approval.impl.DefaultGiveApprovalEntryComponent
import com.tangem.features.approval.impl.DefaultGiveApprovalFeatureToggles
import com.tangem.features.approval.impl.DefaultSelectApprovalTypeComponent
import com.tangem.features.approval.impl.model.GiveApprovalModel
import com.tangem.features.approval.impl.model.SelectApprovalTypeModel
@ -24,10 +22,6 @@ import javax.inject.Singleton
@Module
internal interface GiveApprovalFeatureModule {
@Singleton
@Binds
fun bindGiveApprovalFeatureToggle(toggles: DefaultGiveApprovalFeatureToggles): GiveApprovalFeatureToggles
@Binds
@Singleton
fun bindComponentFactory(factory: DefaultGiveApprovalComponent.Factory): GiveApprovalComponent.Factory

View file

@ -2,7 +2,6 @@ package com.tangem.features.staking.impl.presentation.model
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.model.StakingTarget
@ -47,10 +46,6 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
fun showApprovalBottomSheet()
fun onApproveTypeChange(approveType: ApproveType)
fun onApprovalClick()
fun onAmountReduceByClick(
reduceAmountBy: BigDecimal,
reduceAmountByDiff: BigDecimal,

View file

@ -7,15 +7,12 @@ import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.TangemBlogUrlBuilder
import com.tangem.common.getValidatorsCount
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.api.ParamsInterceptorHolder
@ -66,7 +63,6 @@ import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.approval.api.GiveApprovalComponent
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import com.tangem.features.staking.api.StakingComponent
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor
@ -125,7 +121,6 @@ internal class StakingModel @Inject constructor(
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase,
private val getAllowanceUseCase: GetAllowanceUseCase,
private val vibratorHapticManager: VibratorHapticManager,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
@ -156,7 +151,6 @@ internal class StakingModel @Inject constructor(
private val coroutineScope: AppCoroutineScope,
private val innerRouter: InnerStakingRouter,
private val messageSender: UiMessageSender,
private val giveApprovalFeatureToggles: GiveApprovalFeatureToggles,
appRouter: AppRouter,
) : Model(), StakingClickIntents {
@ -311,7 +305,6 @@ internal class StakingModel @Inject constructor(
private val transactionsInProgress: CopyOnWriteArrayList<StakingTransaction> = CopyOnWriteArrayList()
private val actionsJobHolder: JobHolder = JobHolder()
private val approvalJobHolder: JobHolder = JobHolder()
private val feeJobHolder: JobHolder = JobHolder()
private val sendTransactionJobHolder = JobHolder()
private val stepChangesJobHolder = JobHolder()
@ -325,7 +318,6 @@ internal class StakingModel @Inject constructor(
override fun onDestroy() {
super.onDestroy()
paramsInterceptorHolder.removeParamsInterceptor(StakingParamsInterceptor.ID)
approvalJobHolder.cancel()
feeJobHolder.cancel()
sendTransactionJobHolder.cancel()
stepChangesJobHolder.cancel()
@ -820,112 +812,7 @@ internal class StakingModel @Inject constructor(
}
override fun showApprovalBottomSheet() {
if (giveApprovalFeatureToggles.isGaslessApprovalEnabled) {
approvalSlotNavigation.activate(Unit)
} else {
stateController.update(
ShowApprovalBottomSheetTransformer(
userWallet = userWallet,
appCurrencyProvider = Provider { currentAppCurrency.value },
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
) {
stateController.update(DismissBottomSheetStateTransformer)
},
)
}
}
override fun onApproveTypeChange(approveType: ApproveType) {
stateController.update(SetApprovalBottomSheetTypeChangeTransformer(approveType))
}
@Suppress("LongMethod")
override fun onApprovalClick() {
modelScope.launch {
stateController.update(
SetApprovalBottomSheetInProgressTransformer {
stateController.update(DismissBottomSheetStateTransformer)
},
)
val tokenCryptoCurrency =
cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: error("No token currency")
val amountValue = (value.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
?: error("No confirmation state")
val fee = (confirmationState.feeState as? FeeState.Content)?.fee ?: error("No fee provided")
val approval = stakingApproval as? StakingApproval.Needed ?: error("No staking approve spender address")
val approvalBottomSheetConfig = value.bottomSheetConfig?.content as? GiveTxPermissionBottomSheetConfig
val isLimitedApproval = approvalBottomSheetConfig?.data?.approveType == ApproveType.LIMITED
val approvalTransaction = createApprovalTransactionUseCase(
amount = amountValue.takeIf { isLimitedApproval },
contractAddress = tokenCryptoCurrency.contractAddress,
spenderAddress = approval.spenderAddress,
fee = fee,
cryptoCurrencyStatus = cryptoCurrencyStatus,
userWalletId = userWalletId,
).fold(
ifLeft = { error ->
TangemLogger.e(error.toString())
analyticsEventHandler.send(
StakingAnalyticsEvent.TransactionError(
errorCode = "CreateApprovalTxError",
),
)
stateController.update(
SetConfirmationStateAssentApprovalTransformer(
appCurrencyProvider = Provider { currentAppCurrency.value },
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
fee = TransactionFee.Single(fee),
cryptoCurrencyStatus = cryptoCurrencyStatus,
),
)
stakingEventFactory.createGenericErrorAlert(error.message ?: error.toString())
stateController.update(
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
)
return@launch
},
ifRight = { it },
)
sendTransactionUseCase(
txData = approvalTransaction,
userWallet = userWallet,
network = tokenCryptoCurrency.network,
).fold(
ifLeft = { error ->
TangemLogger.e(error.toString())
analyticsEventHandler.send(
StakingAnalyticsEvent.TransactionError(
errorCode = error.getAnalyticsDescription(),
),
)
stateController.update(
SetConfirmationStateAssentApprovalTransformer(
appCurrencyProvider = Provider { currentAppCurrency.value },
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
fee = TransactionFee.Single(fee),
cryptoCurrencyStatus = cryptoCurrencyStatus,
),
)
stakingEventFactory.createSendTransactionErrorAlert(error)
stateController.update(
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
)
},
ifRight = {
stakingAnalyticSender.sendTransactionApprovalAnalytics(tokenCryptoCurrency)
stateController.update(SetApprovalInProgressTransformer)
stateController.update(DismissBottomSheetStateTransformer)
awaitForAllowance()
},
)
}.saveIn(approvalJobHolder)
approvalSlotNavigation.activate(Unit)
}
private fun updateNotifications(feeError: GetFeeError? = null, stakingError: StakingError? = null) {

View file

@ -1,6 +1,5 @@
package com.tangem.features.staking.impl.presentation.state.stub
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.model.StakingTarget
@ -46,10 +45,6 @@ internal object StakingClickIntentsStub : StakingClickIntents {
override fun showApprovalBottomSheet() {}
override fun onApproveTypeChange(approveType: ApproveType) {}
override fun onApprovalClick() {}
override fun onExploreClick() {}
override fun onShareClick() {}

View file

@ -1,34 +0,0 @@
package com.tangem.features.staking.impl.presentation.state.transformers.approval
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.utils.transformer.Transformer
internal class SetApprovalBottomSheetInProgressTransformer(
private val onDismiss: () -> Unit,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val approvalBottomSheetConfig = prevState.bottomSheetConfig?.content as? GiveTxPermissionBottomSheetConfig
return prevState.copy(
bottomSheetConfig = prevState.bottomSheetConfig?.copy(
onDismissRequest = onDismiss,
isShown = true,
content = approvalBottomSheetConfig?.let { config ->
config.copy(
data = config.data.copy(
approveButton = config.data.approveButton.copy(
isEnabled = false,
isLoading = true,
),
cancelButton = config.data.cancelButton.copy(
enabled = false,
),
),
onCancel = onDismiss,
)
} as? TangemBottomSheetConfigContent ?: return prevState,
),
)
}
}

View file

@ -1,23 +0,0 @@
package com.tangem.features.staking.impl.presentation.state.transformers.approval
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.utils.transformer.Transformer
internal class SetApprovalBottomSheetTypeChangeTransformer(
private val approveType: ApproveType,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val approvalBottomSheetConfig = prevState.bottomSheetConfig?.content as? GiveTxPermissionBottomSheetConfig
return prevState.copy(
bottomSheetConfig = prevState.bottomSheetConfig?.copy(
content = approvalBottomSheetConfig?.copy(
data = approvalBottomSheetConfig.data.copy(approveType = approveType),
) as? TangemBottomSheetConfigContent ?: return prevState,
),
)
}
}

View file

@ -1,89 +0,0 @@
package com.tangem.features.staking.impl.presentation.state.transformers.approval
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.bottomsheet.permission.state.*
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.staking.impl.R
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.utils.Provider
import com.tangem.utils.transformer.Transformer
internal class ShowApprovalBottomSheetTransformer(
private val userWallet: UserWallet,
private val appCurrencyProvider: Provider<AppCurrency>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
private val onDismiss: () -> Unit,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
val cryptoCurrencyValue = cryptoCurrencyStatusProvider().value
val amountState = prevState.amountState as? AmountState.Data ?: return prevState
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
val validatorState = prevState.validatorState as? StakingStates.ValidatorState.Data ?: return prevState
val feeState = confirmationState.feeState as? FeeState.Content ?: return prevState
val fee = feeState.fee ?: return prevState
val walletAddress = cryptoCurrencyValue.networkAddress?.defaultAddress?.value.orEmpty()
val targetAddress = validatorState.chosenTarget.address
val feeCryptoValue = fee.amount.value.format {
crypto(fee.amount.currencySymbol, fee.amount.decimals)
}
val feeFiatValue = feeCryptoCurrencyStatus?.value?.fiatRate?.multiply(fee.amount.value).format {
fiat(
fiatCurrencyCode = appCurrencyProvider().code,
fiatCurrencySymbol = appCurrencyProvider().symbol,
)
}
return prevState.copy(
bottomSheetConfig = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = GiveTxPermissionBottomSheetConfig(
data = GiveTxPermissionState.ReadyForRequest(
currency = cryptoCurrency.symbol,
amount = amountState.amountTextField.value,
approveType = ApproveType.UNLIMITED,
walletAddress = walletAddress,
spenderAddress = targetAddress,
fee = resourceReference(
R.string.common_crypto_fiat_format,
wrappedList(feeCryptoValue, feeFiatValue),
),
approveButton = ApprovePermissionButton(
isEnabled = true,
isLoading = false,
onClick = prevState.clickIntents::onApprovalClick,
),
cancelButton = CancelPermissionButton(
enabled = true,
),
subtitle = resourceReference(
id = R.string.give_permission_staking_subtitle,
formatArgs = wrappedList(cryptoCurrency.symbol),
),
dialogText = resourceReference(R.string.give_permission_staking_footer),
footerText = resourceReference(R.string.staking_give_permission_fee_footer),
onChangeApproveType = prevState.clickIntents::onApproveTypeChange,
onOpenLearnMoreAboutApproveClick = {},
isResetApproval = false,
),
walletInteractionIcon = walletInterationIcon(userWallet),
onCancel = onDismiss,
),
),
)
}
}

View file

@ -12,8 +12,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import com.tangem.common.ui.amountScreen.AmountScreenContent
import com.tangem.common.ui.bottomsheet.permission.GiveTxPermissionBottomSheet
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
import com.tangem.common.ui.navigationButtons.NavigationButtonsBlock
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
@ -76,7 +74,6 @@ fun StakingBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
if (bottomSheetConfig == null) return
when (bottomSheetConfig.content) {
is StakingInfoBottomSheetConfig -> StakingInfoBottomSheet(bottomSheetConfig)
is GiveTxPermissionBottomSheetConfig -> GiveTxPermissionBottomSheet(bottomSheetConfig)
is StakingActionSelectionBottomSheetConfig -> StakingActionSelectorBottomSheet(bottomSheetConfig)
is TonInitializeAccountBottomSheetConfig -> TonInitializeAccountBottomSheet(bottomSheetConfig)
}

View file

@ -32,7 +32,6 @@ import com.tangem.domain.staking.repositories.P2PEthPoolRepository
import com.tangem.domain.tokens.*
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import com.tangem.features.staking.api.StakingComponent
import com.tangem.features.staking.impl.navigation.InnerStakingRouter
import com.tangem.features.staking.impl.presentation.state.StakingStateController
@ -86,7 +85,6 @@ internal abstract class StakingModelTestBase {
protected val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase = mockk()
protected val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase = mockk()
protected val sendTransactionUseCase: SendTransactionUseCase = mockk()
protected val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase = mockk()
protected val getAllowanceUseCase: GetAllowanceUseCase = mockk()
protected val vibratorHapticManager: VibratorHapticManager = mockk()
protected val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase = mockk()
@ -114,7 +112,6 @@ internal abstract class StakingModelTestBase {
private val coroutineScope: AppCoroutineScope = mockk()
protected val innerRouter: InnerStakingRouter = mockk()
protected val messageSender: UiMessageSender = mockk()
protected val giveApprovalFeatureToggles: GiveApprovalFeatureToggles = mockk()
@BeforeEach
fun setUp() {
@ -176,7 +173,6 @@ internal abstract class StakingModelTestBase {
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
getUserWalletUseCase = getUserWalletUseCase,
sendTransactionUseCase = sendTransactionUseCase,
createApprovalTransactionUseCase = createApprovalTransactionUseCase,
getAllowanceUseCase = getAllowanceUseCase,
vibratorHapticManager = vibratorHapticManager,
getWalletMetaInfoUseCase = getWalletMetaInfoUseCase,
@ -207,7 +203,6 @@ internal abstract class StakingModelTestBase {
coroutineScope = coroutineScope,
innerRouter = innerRouter,
messageSender = messageSender,
giveApprovalFeatureToggles = giveApprovalFeatureToggles,
appRouter = appRouter,
)
}

View file

@ -22,9 +22,6 @@ import com.tangem.features.staking.impl.presentation.state.helpers.StakingTransa
import com.tangem.features.staking.impl.presentation.state.transformers.SetConfirmationStateInProgressTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.SetConfirmationStateLoadingTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.SetConfirmationStateResetAssentTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.approval.SetApprovalBottomSheetInProgressTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.approval.SetApprovalBottomSheetTypeChangeTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.approval.ShowApprovalBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.CompleteInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.SetFeeErrorToTonInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.SetFeeToTonInitializeBottomSheetTransformer
@ -305,168 +302,6 @@ internal class StakingModelTransactionTest : StakingModelTestBase() {
model.onDestroy()
}
@Test
fun `GIVEN gasless approval enabled WHEN showApprovalBottomSheet THEN approvalSlotNavigation activated`() =
runTest {
every { giveApprovalFeatureToggles.isGaslessApprovalEnabled } returns true
val model = createModel(testScope = this)
advanceUntilIdle()
model.showApprovalBottomSheet()
verify(exactly = 0) {
stateController.update(
transformer = match<Transformer<StakingUiState>> { it is ShowApprovalBottomSheetTransformer }
)
}
model.onDestroy()
}
@Test
fun `GIVEN gasless disabled WHEN showApprovalBottomSheet THEN ShowApprovalBottomSheetTransformer applied`() =
runTest {
every { giveApprovalFeatureToggles.isGaslessApprovalEnabled } returns false
val model = createModel(testScope = this)
advanceUntilIdle()
model.showApprovalBottomSheet()
verify {
stateController.update(
transformer = match<Transformer<StakingUiState>> { it is ShowApprovalBottomSheetTransformer }
)
}
model.onDestroy()
}
@Test
fun `WHEN onApproveTypeChange THEN SetApprovalBottomSheetTypeChangeTransformer applied`() = runTest {
val model = createModel(testScope = this)
advanceUntilIdle()
model.onApproveTypeChange(ApproveType.LIMITED)
verify {
stateController.update(
transformer = match<Transformer<StakingUiState>> { it is SetApprovalBottomSheetTypeChangeTransformer },
)
}
model.onDestroy()
}
@Test
fun `GIVEN approval needed WHEN onApprovalClick THEN in progress set and createApprovalTransaction called`() =
runTest {
val spenderAddress = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908"
val expectedNetwork = mockk<Network> {
every { name } returns "KEK"
}
val testToken: CryptoCurrency.Token = mockk(relaxed = true) {
every { network } returns expectedNetwork
}
val testCryptoCurrencyStatus: CryptoCurrencyStatus = mockk(relaxed = true) {
every { currency } returns testToken
}
val testAccountCurrencyStatus = mockk<AccountCryptoCurrencyStatus> {
every { component1() } returns mockk(relaxed = true)
every { component2() } returns testCryptoCurrencyStatus
}
every {
getAccountCurrencyStatusUseCase(testUserWalletId, testCryptoCurrency)
} returns flowOf(testAccountCurrencyStatus)
coEvery {
getFeePaidCryptoCurrencyStatusSyncUseCase(testUserWalletId, testCryptoCurrencyStatus)
} returns Either.Right(mockk(relaxed = true))
coEvery {
getMinimumTransactionAmountSyncUseCase(testUserWalletId, testCryptoCurrencyStatus)
} returns Either.Left(mockk())
// Setup stakingApproval = Needed
mockkObject(StakingIntegrationID.Companion)
every {
StakingIntegrationID.create(any())
} returns mockk {
every { approval } returns StakingApproval.Needed(spenderAddress)
}
coEvery {
getAllowanceUseCase(testUserWalletId, any(), spenderAddress)
} returns Either.Right(BigDecimal.TEN)
every {
stakingOperationsFactory.createFeeLoader(
cryptoCurrencyStatus = any(),
userWallet = any(),
integration = any()
)
} returns mockk<StakingFeeLoader> {
coEvery {
getFee(
onStakingFee = any(),
onStakingFeeError = any(),
onApprovalFee = any(),
onFeeError = any()
)
} just Runs
}
val expectedApprovalTx = Either.Right<TransactionData.Uncompiled>(mockk(relaxed = true))
coEvery {
createApprovalTransactionUseCase.invoke(
cryptoCurrencyStatus = any(),
userWalletId = any(),
amount = any(),
fee = any(),
contractAddress = any(),
spenderAddress = any(),
)
} returns expectedApprovalTx
coEvery {
sendTransactionUseCase(any(), any(), any())
} returns Either.Right("txHash")
every { vibratorHapticManager.performOneTime(any()) } just Runs
val model = createModel(testScope = this)
advanceUntilIdle()
// Now override stateController.value with confirmation state after cryptoCurrencyStatus is initialized
val testFee: Fee.Common = mockk(relaxed = true)
val confirmationState = mockk<StakingStates.ConfirmationState.Data>(relaxed = true) {
every { feeState } returns mockk<FeeState.Content>(relaxed = true) {
every { fee } returns testFee
}
}
val uiState = mockk<StakingUiState>(relaxed = true) {
every { this@mockk.confirmationState } returns confirmationState
every { bottomSheetConfig } returns null
}
every { stateController.value } returns uiState
model.onApprovalClick()
advanceUntilIdle()
verify {
stateController.update(
transformer = match<Transformer<StakingUiState>> {
it is SetApprovalBottomSheetInProgressTransformer
},
)
}
coVerify {
sendTransactionUseCase(
txData = expectedApprovalTx.value,
userWallet = testUserWallet,
network = expectedNetwork,
)
}
model.onDestroy()
unmockkObject(StakingIntegrationID.Companion)
}
@Test
fun `GIVEN approval needed AND amountState data WHEN getApprovalParams THEN returns non-null params`() = runTest {
val spenderAddress = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908"