Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-17 14:05:58 +03:00
commit 3cbc2dadfb
822 changed files with 12838 additions and 5366 deletions

View file

@ -5,7 +5,6 @@
<ID>BooleanPropertyNaming:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer$val showNotification = sendingAmount + feeAmount &gt; balance</ID>
<ID>BooleanPropertyNaming:AmountCurrencyChangeStateTransformer.kt$AmountCurrencyChangeStateTransformer$private val value: Boolean</ID>
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noBalanceState = balanceState == null</ID>
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data</ID>
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingStates.InitialInfoState.Data$val showBanner: Boolean</ID>
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingUiState$val showColdWalletInteractionIcon: Boolean</ID>
<ID>CanBeNonNullable:StakingScreen.kt$bottomSheetConfig: TangemBottomSheetConfig?</ID>
@ -28,14 +27,6 @@
<ID>MultilineLambdaItParameter:StakingTransactionSender.kt$StakingTransactionSender${ onConstructError(it) return emptyList() }</ID>
<ID>NoNameShadowing:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader$amount</ID>
<ID>NoNameShadowing:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader${ if (!it.amount.isZero()) return feeResult }</ID>
<ID>NonBooleanPropertyPrefixedWithIs:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer$private val isAccountInitializedProvider: Provider&lt;Boolean&gt;</ID>
<ID>NonBooleanPropertyPrefixedWithIs:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$val isStakingEnabled = getStakingAvailabilityUseCase.invokeSync( userWalletId = selectedUserWalletId, cryptoCurrency = cryptoCurrency, ).getOrNull()</ID>
<ID>NonBooleanPropertyPrefixedWithIs:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader$private val isFeeApproximateUseCase: IsFeeApproximateUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isAnyTokenStakedUseCase: IsAnyTokenStakedUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isBalanceHiddenFlow: StateFlow&lt;Boolean&gt; field = MutableStateFlow(false)</ID>
<ID>NonBooleanPropertyPrefixedWithIs:StakingTransactionSender.kt$StakingTransactionSender$private val isFeeApproximateUseCase: IsFeeApproximateUseCase</ID>
<ID>NullCheckOnMutableProperty:StakingModel.kt$StakingModel$if (feeCryptoCurrencyStatus != null &amp;&amp; fee != null) { getBalanceNotEnoughForFeeWarningUseCase( fee = fee, userWalletId = userWalletId, tokenStatus = cryptoCurrencyStatus, coinStatus = feeCryptoCurrencyStatus ?: cryptoCurrencyStatus, ).getOrNull() } else { null }</ID>
<ID>NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$networkId</ID>
<ID>NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$tokenId</ID>
@ -50,7 +41,6 @@
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(PRIVACY_POLICY_URL) }</ID>
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(TERMS_OF_USE_URL) }</ID>
<ID>UnsafeCallOnNullableType:StakingModel.kt$StakingModel$tonAccountInitializeTransaction!!</ID>
<ID>UseOrEmpty:StakingModel.kt$StakingModel$yieldBalance?.balance?.items ?: emptyList()</ID>
<ID>UseOrEmpty:StakingTransactionSender.kt$StakingTransactionSender$getExplorerTransactionUrlUseCase( txHash = transactionHashes.last(), networkId = cryptoCurrencyStatus.currency.network.id, ).getOrNull() ?: ""</ID>
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var actionsJobHolder: JobHolder = JobHolder()</ID>
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var approvalJobHolder: JobHolder = JobHolder()</ID>

View file

@ -190,9 +190,9 @@ internal class StakingModel @Inject constructor(
private val balancesToShow: List<BalanceItem>
get() {
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
return invalidatePendingTransactionsUseCase(
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
balanceItems = stakeKitBalance?.balance?.items.orEmpty(),
stakingActions = stakingActions,
token = yield.token,
).getOrElse { emptyList() }
@ -277,10 +277,10 @@ internal class StakingModel @Inject constructor(
modelScope.launch {
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
val noBalanceState = balanceState == null
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
val hasNoYieldBalanceData = cryptoCurrencyStatus.value.stakingBalance !is StakingBalance.Data.StakeKit
when {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && hasNoYieldBalanceData -> {
stakingEventFactory.createStakingValidatorsUnavailableAlert()
return@launch
}
@ -466,7 +466,7 @@ internal class StakingModel @Inject constructor(
}
override fun onInitialInfoBannerClick() {
analyticsEventHandler.send(StakingAnalyticsEvent.WhatIsStaking)
analyticsEventHandler.send(StakingAnalyticsEvent.WhatIsStaking())
innerRouter.openUrl(WHAT_IS_STAKING_ARTICLE_URL)
}
@ -512,7 +512,7 @@ internal class StakingModel @Inject constructor(
}
override fun onMaxValueClick() {
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonMax)
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonMax())
stateController.update(
AmountMaxValueStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
@ -554,7 +554,7 @@ internal class StakingModel @Inject constructor(
}
override fun openRewardsValidators() {
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonRewards)
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonRewards())
val rewardsValidators =
stateController.value.rewardsValidatorsState as? StakingStates.RewardsValidatorsState.Data
@ -879,7 +879,7 @@ internal class StakingModel @Inject constructor(
}
override fun onExploreClick() {
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonExplore)
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonExplore())
val confirmationDataState = uiState.value.confirmationState as? StakingStates.ConfirmationState.Data
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
val txUrl = transactionDoneState?.txUrl
@ -893,7 +893,7 @@ internal class StakingModel @Inject constructor(
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
val txUrl = transactionDoneState?.txUrl
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonShare)
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonShare())
if (txUrl != null) {
vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click)
shareManager.shareText(txUrl)
@ -1117,7 +1117,7 @@ internal class StakingModel @Inject constructor(
private suspend fun onDataLoaded(status: CryptoCurrencyStatus) {
if (!isInitialInfoAnalyticSent) {
isInitialInfoAnalyticSent = true
val balances = status.value.yieldBalance as? YieldBalance.Data
val balances = status.value.stakingBalance as? StakingBalance.Data.StakeKit
paramsInterceptorHolder.addParamsInterceptor(
interceptor = StakingParamsInterceptor(status.currency.symbol),
)

View file

@ -81,12 +81,12 @@ internal class StakingStateRouter(
}
fun showRewardsValidators() {
analyticsEventsHandler.send(StakingAnalyticsEvent.RewardScreenOpened)
analyticsEventsHandler.send(StakingAnalyticsEvent.RewardScreenOpened())
stateController.update { it.copy(currentStep = StakingStep.RewardsValidators) }
}
private fun showAmount() {
analyticsEventsHandler.send(StakingAnalyticsEvent.AmountScreenOpened)
analyticsEventsHandler.send(StakingAnalyticsEvent.AmountScreenOpened())
stateController.update { it.copy(currentStep = StakingStep.Amount) }
}

View file

@ -10,7 +10,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.BalanceType.Companion.isClickable
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.utils.getRewardStakingBalance
@ -77,11 +77,11 @@ internal class BalanceItemConverter(
val isIncludeStakingTotalBalance = BlockchainUtils.isIncludeStakingTotalBalance(
blockchainId = cryptoCurrencyStatus.currency.network.rawId,
)
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
return if (isIncludeStakingTotalBalance) {
amount
} else {
amount - yieldBalance?.getRewardStakingBalance().orZero()
amount - stakeKitBalance?.getRewardStakingBalance().orZero()
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
@ -24,9 +24,9 @@ internal class RewardsValidatorStateConverter(
private val yield: Yield,
) : Converter<Unit, StakingStates.RewardsValidatorsState> {
override fun convert(value: Unit): StakingStates.RewardsValidatorsState {
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
return if (yieldBalance is YieldBalance.Data) {
val balances = yieldBalance.balance.items
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
return if (stakingBalance is StakingBalance.Data.StakeKit) {
val balances = stakingBalance.balance.items
StakingStates.RewardsValidatorsState.Data(
isPrimaryButtonEnabled = true,
rewards = balances
@ -35,6 +35,7 @@ internal class RewardsValidatorStateConverter(
.toPersistentList(),
)
} else {
// TODO p2p
StakingStates.RewardsValidatorsState.Empty()
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceItem
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.RewardBlockType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.utils.getRewardStakingBalance
@ -36,11 +36,11 @@ internal class YieldBalancesConverter(
val appCurrency = appCurrencyProvider()
val cryptoCurrency = cryptoCurrencyStatus.currency
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
val balanceToShowItems = balancesToShowProvider()
return if (yieldBalance != null || balanceToShowItems.any { it.isPending }) {
val cryptoRewardsValue = yieldBalance?.getRewardStakingBalance()
return if (stakeKitBalance != null || balanceToShowItems.any { it.isPending }) {
val cryptoRewardsValue = stakeKitBalance?.getRewardStakingBalance()
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
@ -49,13 +49,13 @@ internal class YieldBalancesConverter(
null
}
val type = getRewardBlockType()
val pendingRewardsConstraints = yieldBalance?.balance?.items
val pendingRewardsConstraints = stakeKitBalance?.balance?.items
?.firstOrNull { it.type == BalanceType.REWARDS }
?.pendingActionsConstraints
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
InnerYieldBalanceState.Data(
integrationId = yieldBalance?.stakingId?.integrationId,
integrationId = stakeKitBalance?.stakingId?.integrationId,
reward = YieldReward(
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
rewardsFiat = fiatRewardsValue.format {
@ -71,6 +71,7 @@ internal class YieldBalancesConverter(
balances = balanceToShowItems.mapBalances(),
)
} else {
// TODO p2p
InnerYieldBalanceState.Empty
}
}
@ -84,8 +85,8 @@ internal class YieldBalancesConverter(
private fun getRewardBlockType(): RewardBlockType {
val blockchainId = cryptoCurrencyStatus.currency.network.rawId
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val rewards = yieldBalance?.balance?.items
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
val rewards = stakeKitBalance?.balance?.items
?.filter { it.type == BalanceType.REWARDS && !it.amount.isZero() }
val isActionable = rewards?.any { it.pendingActions.isNotEmpty() } == true

View file

@ -35,7 +35,7 @@ internal class SetConfirmationStateAssentTransformer(
isFeeApproximate = isFeeApproximate,
),
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
)
} else {

View file

@ -26,7 +26,7 @@ internal class SetConfirmationStateCompletedTransformer(
return if (this is StakingStates.ConfirmationState.Data) {
copy(
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
innerState = InnerConfirmationStakingState.COMPLETED,
footerText = TextReference.EMPTY,

View file

@ -15,7 +15,7 @@ internal class SetConfirmationStateResetAssentTransformer(
confirmationState = if (confirmationState is StakingStates.ConfirmationState.Data) {
confirmationState.copy(
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
innerState = InnerConfirmationStakingState.ASSENT,
)

View file

@ -90,7 +90,7 @@ internal class SetInitialDataStateTransformer(
val status = cryptoCurrencyStatus.value
return StakingStates.InitialInfoState.Data(
isPrimaryButtonEnabled = with(status) {
!amount.isNullOrZero() && sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
!amount.isNullOrZero() && sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
showBanner = !isAnyTokenStaked && yieldBalance == InnerYieldBalanceState.Empty,
infoItems = getInfoItems(),

View file

@ -37,7 +37,7 @@ internal class SetConfirmationStateAssentApprovalTransformer(
isFeeApproximate = false,
),
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
},
isApprovalNeeded = true,
)

View file

@ -127,7 +127,7 @@ internal class AddStakingNotificationsTransformer(
}
val isActualSources = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
}
return prevState.copy(

View file

@ -6,7 +6,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
@ -134,7 +134,7 @@ internal class StakingInfoNotificationsFactory(
private fun MutableList<NotificationUM>.addTronRevoteNotification() {
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val isTron = isTron(cryptoCurrencyStatus.currency.network.rawId)
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
val hasStakedBalance = (cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit)?.balance
?.items?.any {
it.type == BalanceType.PREPARING ||
it.type == BalanceType.STAKED ||