Skip to content

Commit

Permalink
feat(staking): [LW-7508] add un-delegation functionality (#634)
Browse files Browse the repository at this point in the history
* feat(staking): add un-delegation functionality

* feat(staking): adjust success screen to make sense with undelegation

---------

Co-authored-by: Piotr Czeglik <[email protected]>
  • Loading branch information
xdzurman and pczeglik-iohk authored Feb 29, 2024
1 parent c64342f commit bda9570
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 29 deletions.
2 changes: 1 addition & 1 deletion packages/staking/src/features/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type DraftPortfolioInvalidReason = 'invalid-allocation' | 'slider-zero';
type DraftPortfolioValidity = { valid: true } | { valid: false; reason: DraftPortfolioInvalidReason };

const getDraftPortfolioValidity = (store: DelegationPortfolioStore): DraftPortfolioValidity => {
if (!store.draftPortfolio || store.draftPortfolio.length === 0) return { valid: true }; // throw new Error('Draft portfolio is not defined');
if (!store.draftPortfolio?.length) return { valid: true };
const percentageSum = sumPercentagesSanitized({ items: store.draftPortfolio, key: 'sliderIntegerPercentage' });
if (percentageSum !== PERCENTAGE_SCALE_MAX) {
return { reason: 'invalid-allocation', valid: false };
Expand Down
28 changes: 23 additions & 5 deletions packages/staking/src/features/Drawer/TransactionSuccess.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { WalletType } from '@cardano-sdk/web-extension';
import { Button, PostHogAction } from '@lace/common';
import cn from 'classnames';
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useOutsideHandles } from '../outside-handles-provider';
import { useDelegationPortfolioStore } from '../store';
Expand All @@ -19,6 +19,27 @@ export const TransactionSuccess = ({ popupView }: TransactionSuccessProps): Reac
submittingState: { isRestaking },
analytics,
} = useOutsideHandles();
const draftPortfolio = useDelegationPortfolioStore((store) => store.draftPortfolio);

const { title, description } = useMemo(() => {
// un-delegation case
if (draftPortfolio?.length === 0) {
return {
title: t('drawer.success.modification.title'),
};
}
if (isRestaking) {
return {
description: t('drawer.success.switchedPools.subTitle'),
title: t('drawer.success.switchedPools.title'),
};
}

return {
description: t('drawer.success.subTitle'),
title: t('drawer.success.title'),
};
}, [draftPortfolio, isRestaking, t]);

useEffect(() => {
analytics.sendEventToPostHog(PostHogAction.StakingManageDelegationHurrayView);
Expand All @@ -27,10 +48,7 @@ export const TransactionSuccess = ({ popupView }: TransactionSuccessProps): Reac
return (
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
<div className={cn(styles.container, { [styles.popupView!]: popupView })}>
<ResultMessage
title={isRestaking ? t('drawer.success.switchedPools.title') : t('drawer.success.title')}
description={isRestaking ? t('drawer.success.switchedPools.subTitle') : t('drawer.success.subTitle')}
/>
<ResultMessage title={title} description={description} />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@
}
}
}

.noPoolsText {
color: var(--text-color-secondary);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Icon from '@ant-design/icons';
import { Wallet } from '@lace/cardano';
import { Ellipsis } from '@lace/common';
import { Flex, Text } from '@lace/ui';
import cn from 'classnames';
import { useTranslation } from 'react-i18next';
import { Balance, CurrencyInfo } from '../../../outside-handles-provider';
Expand Down Expand Up @@ -69,22 +70,30 @@ export const StakePoolConfirmationBody = ({
/>
</div>
<Icon style={{ color: '#702BED', fontSize: '24px', margin: '12px 0px' }} component={ArrowDown} />
{stakePools.map((stakePool) => (
<div
key={stakePool.id}
className={cn(styles.item, styles.itemMulti)}
data-testid="sp-confirmation-delegate-to-container"
>
<ItemStatRenderer
img={stakePool.displayData.logo}
text={<EllipsizedPoolName stakePool={stakePool} />}
subText={<span>{stakePool.displayData.ticker}</span>}
/>
<div className={styles.itemData}>
<Ellipsis beforeEllipsis={10} afterEllipsis={8} text={stakePool.id} ellipsisInTheMiddle />
{stakePools.length > 0 ? (
stakePools.map((stakePool) => (
<div
key={stakePool.id}
className={cn(styles.item, styles.itemMulti)}
data-testid="sp-confirmation-delegate-to-container"
>
<ItemStatRenderer
img={stakePool.displayData.logo}
text={<EllipsizedPoolName stakePool={stakePool} />}
subText={<span>{stakePool.displayData.ticker}</span>}
/>
<div className={styles.itemData}>
<Ellipsis beforeEllipsis={10} afterEllipsis={8} text={stakePool.id} ellipsisInTheMiddle />
</div>
</div>
</div>
))}
))
) : (
<Flex justifyContent="center">
<Text.Body.Large className={styles.noPoolsText} weight="$semibold">
{t('drawer.confirmation.noPools')}
</Text.Body.Large>
</Flex>
)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,17 @@ export const StakePoolConfirmationContent = (): React.ReactElement => {

useEffect(() => {
(async () => {
if (draftPortfolio.length === 0 || loading) return;
if (loading) return;
// TODO: move below logic to zustand store
try {
setIsBuildingTx(true);
const txBuilder = inMemoryWallet.createTxBuilder();
const pools = draftPortfolio.map((pool) => ({ id: pool.id, weight: pool.sliderIntegerPercentage }));
const tx = await txBuilder.delegatePortfolio({ pools }).build().inspect();
const tx = await txBuilder
// passing null de-registers all stake keys
.delegatePortfolio(pools.length > 0 ? { pools } : null)
.build()
.inspect();
const implicitCoin = Wallet.Cardano.util.computeImplicitCoin(protocolParameters, tx.body);
const newDelegationTxDeposit = implicitCoin.deposit;
const newDelegationTxReclaim = Wallet.util.calculateDepositReclaim(implicitCoin) || BigInt(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { style } from '@lace/ui';
import { theme } from '../../theme';

export const container = style({
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
});

export const icon = style({
fontSize: theme.spacing.$112,
});

export const text = style({
color: theme.colors.$preferencesDrawerNoPoolsTextColor,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Box, Button, Text } from '@lace/ui';
import { useTranslation } from 'react-i18next';
import * as styles from './NoPoolsSelected.css';
import SadSmiley from './sad-smiley.svg';

export const NoPoolsSelected = ({
onBrowsePoolsButtonClick,
}: {
onBrowsePoolsButtonClick: () => void;
}): React.ReactElement => {
const { t } = useTranslation();
return (
<Box className={styles.container}>
<SadSmiley className={styles.icon} />
<Text.Body.Normal className={styles.text} weight="$semibold">
{t('drawer.preferences.noSelectedPools')}
</Text.Body.Normal>
<Box mt="$40">
<Button.CallToAction label={t('drawer.preferences.browsePools')} onClick={onBrowsePoolsButtonClick} />
</Box>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
sumPercentagesSanitized,
useDelegationPortfolioStore,
} from '../../store';
import { NoPoolsSelected } from './NoPoolsSelected';
import { PoolDetailsCard } from './PoolDetailsCard';
import * as styles from './StepPreferencesContent.css';

Expand Down Expand Up @@ -80,10 +81,6 @@ export const StepPreferencesContent = () => {
data: poolId,
type: 'RemoveStakePool',
});
portfolioMutators.executeCommand({
data: Wallet.Cardano.PoolIdHex(poolId),
type: 'UnselectPoolFromDetails',
});
analytics.sendEventToPostHog(PostHogAction.StakingBrowsePoolsStakePoolDetailUnselectPoolClick);
};
const addPoolButtonDisabled = draftPortfolio.length === MAX_POOLS_COUNT;
Expand Down Expand Up @@ -117,6 +114,11 @@ export const StepPreferencesContent = () => {
/>
</Flex>
<Flex flexDirection="column" gap="$16" pb="$32" alignItems="stretch" data-testid="selected-pools-container">
{displayData.length === 0 && (
<Box pt="$20">
<NoPoolsSelected onBrowsePoolsButtonClick={onAddPoolButtonClick} />
</Box>
)}
{displayData.map(
(
{ color, id, name, stakeValue, onChainPercentage, savedIntegerPercentage, sliderIntegerPercentage },
Expand All @@ -126,7 +128,7 @@ export const StepPreferencesContent = () => {
key={id}
color={color}
name={name}
onRemove={draftPortfolio.length > 1 ? createRemovePoolFromPortfolio(id) : undefined}
onRemove={createRemovePoolFromPortfolio(id)}
actualPercentage={onChainPercentage}
savedPercentage={savedIntegerPercentage}
targetPercentage={sliderIntegerPercentage}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/staking/src/features/i18n/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const en: Translations = {
'drawer.confirmation.chargedDepositAmountInfo': "The amount you'll be charged for registering your stake key.",
'drawer.confirmation.errors.utxoBalanceInsufficient': 'Balance Insufficient',
'drawer.confirmation.errors.utxoFullyDepleted': 'UTxO Fully Depleted',
'drawer.confirmation.noPools': 'No pools',
'drawer.confirmation.reclaimDepositAmountInfo': "The amount you'll be awarded for de-registering your stake key.",
'drawer.confirmation.stakingDeposit': 'Staking deposit',
'drawer.confirmation.subTitle': 'Confirm the amount and the stake pool',
Expand Down Expand Up @@ -110,10 +111,12 @@ export const en: Translations = {
'drawer.failure.subTitle': 'The transaction was not successful. Please try again.',
'drawer.failure.title': 'Oops! Something went wrong...',
'drawer.preferences.addPoolButton': 'Add stake pool',
'drawer.preferences.browsePools': 'Browse pools',
'drawer.preferences.confirmButton': 'Confirm new portfolio',
'drawer.preferences.ctaButtonTooltip.invalidAllocation': 'You need to have a 100% allocation in order to proceed',
'drawer.preferences.ctaButtonTooltip.zeroPercentageSliderError':
'Every portfolio pool requires more than 0% allocation',
'drawer.preferences.noSelectedPools': "You don't have any staking pool selected",
'drawer.preferences.pickMorePools': 'You need to stake at least to one pool.',
'drawer.preferences.poolDetails.actualRatio': 'Actual ratio',
'drawer.preferences.poolDetails.actualRatioTooltip':
Expand All @@ -131,6 +134,7 @@ export const en: Translations = {
'drawer.sign.enterWalletPasswordToConfirmTransaction': 'Enter your wallet password to confirm transaction',
'drawer.sign.error.invalidPassword': 'Wrong password',
'drawer.sign.passwordPlaceholder': 'Password',
'drawer.success.modification.title': 'Hurray! Your modification has been submitted',
'drawer.success.subTitle': "You'll start receiving your staking rewards after two epochs.",
'drawer.success.switchedPools.subTitle':
"You'll start receiving your staking rewards from the new pool after two epochs. Until then you'll continue receiving rewards from the previous one.",
Expand Down
4 changes: 4 additions & 0 deletions packages/staking/src/features/i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ type KeysStructure = {
title: '';
subTitle: '';
};
modification: { title: '' };
};
failure: {
title: '';
Expand Down Expand Up @@ -177,6 +178,7 @@ type KeysStructure = {
title: '';
subTitle: '';
cardanoName: '';
noPools: '';
transactionCost: {
title: '';
};
Expand All @@ -203,7 +205,9 @@ type KeysStructure = {
};
};
preferences: {
browsePools: '';
selectedStakePools: '';
noSelectedPools: '';
addPoolButton: '';
pickMorePools: '';
confirmButton: '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export const atomicStateMutators = {
}),
removePoolFromPreferences: ({ id, state }: { id: Wallet.Cardano.PoolIdHex; state: State }) => {
if (!state.draftPortfolio) throw new Error(missingDraftPortfolioErrorMessage);
if (state.draftPortfolio.length === 1) return {};
return {
draftPortfolio: state.draftPortfolio.filter((pool) => pool.id !== id),
} as const;
Expand Down
3 changes: 3 additions & 0 deletions packages/staking/src/features/theme/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const colorsContract = {
$poolCardProgressBarValue: '',
$poolCardSelectedBorderColor: '',
$poolItemEvenBackground: '',
$preferencesDrawerNoPoolsTextColor: '',
$preferencesPoolCardBorderColor: '',
$preferencesPoolCardDataIconColor: '',
$preferencesPoolCardDataTextColor: '',
Expand Down Expand Up @@ -65,6 +66,7 @@ export const lightThemeColors: typeof colorsContract = {
$poolCardProgressBarValue: lightColorScheme.$primary_grey,
$poolCardSelectedBorderColor: lightColorScheme.$primary_accent_purple,
$poolItemEvenBackground: lightColorScheme.$primary_light_grey_0_56,
$preferencesDrawerNoPoolsTextColor: lightColorScheme.$primary_dark_grey,
$preferencesPoolCardBorderColor: lightColorScheme.$primary_light_grey_plus,
$preferencesPoolCardDataIconColor: lightColorScheme.$primary_grey,
$preferencesPoolCardDataTextColor: lightColorScheme.$primary_dark_grey,
Expand Down Expand Up @@ -107,6 +109,7 @@ export const darkThemeColors: typeof colorsContract = {
$poolCardProgressBarValue: darkColorScheme.$primary_light_grey,
$poolCardSelectedBorderColor: darkColorScheme.$primary_accent_purple,
$poolItemEvenBackground: darkColorScheme.$primary_light_black,
$preferencesDrawerNoPoolsTextColor: darkColorScheme.$primary_light_grey,
$preferencesPoolCardBorderColor: darkColorScheme.$primary_mid_black,
// TODO: use darkColorScheme instead
$preferencesPoolCardDataIconColor: lightColorScheme.$primary_grey,
Expand Down

0 comments on commit bda9570

Please sign in to comment.