Skip to content

Commit

Permalink
Warning when voting power too high (#1895)
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 authored Nov 1, 2024
1 parent fe88afd commit e0070a5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Button } from '@penumbra-zone/ui/components/ui/button';
import { Dialog, DialogContent, DialogHeader } from '@penumbra-zone/ui/components/ui/dialog';
import {
Dialog,
DialogClose,
DialogContent,
DialogHeader,
} from '@penumbra-zone/ui/components/ui/dialog';
import { IdentityKeyComponent } from '@penumbra-zone/ui/components/ui/identity-key-component';
import { InputBlock } from '../../../../shared/input-block';
import { Validator } from '@penumbra-zone/protobuf/penumbra/core/component/stake/v1/stake_pb';
Expand All @@ -9,10 +14,22 @@ import { getIdentityKey } from '@penumbra-zone/getters/validator';
import { getFormattedAmtFromValueView } from '@penumbra-zone/types/value-view';
import { BalanceValueView } from '@penumbra-zone/ui/components/ui/balance-value-view';
import { NumberInput } from '../../../../shared/number-input';
import { CircleAlert } from 'lucide-react';
import { useStoreShallow } from '../../../../../utils/use-store-shallow.ts';
import { AllSlices } from '../../../../../state';
import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid';

const getCapitalizedAction = (action: 'delegate' | 'undelegate') =>
action.replace(/^./, firstCharacter => firstCharacter.toLocaleUpperCase());

// If validator has > 5 voting power, show warning to user
const votingPowerSelector =
(validator: Validator, action?: 'delegate' | 'undelegate') => (state: AllSlices) => {
const votingPower =
state.staking.votingPowerByValidatorInfo[bech32mIdentityKey(getIdentityKey(validator))] ?? 0;
return action === 'delegate' && votingPower > 5;
};

/**
* Renders a dialog with a form for delegating to, or undelegating from, a
* validator.
Expand Down Expand Up @@ -51,6 +68,8 @@ export const FormDialog = ({
onClose: () => void;
onSubmit: () => void;
}) => {
const showDelegationWarning = useStoreShallow(votingPowerSelector(validator, action));

const handleOpenChange = (open: boolean) => {
if (!open) {
onClose();
Expand Down Expand Up @@ -87,8 +106,8 @@ export const FormDialog = ({
</div>

{/** @todo: Refactor this block to use `InputToken` (with a new
boolean `showSelectModal` prop) once asset balances are
refactored as `ValueView`s. */}
boolean `showSelectModal` prop) once asset balances are
refactored as `ValueView`s. */}
<InputBlock label={`Amount to ${action}`} className='mb-1' value={amount}>
<NumberInput
variant='transparent'
Expand All @@ -109,14 +128,46 @@ export const FormDialog = ({
)}
</div>
</InputBlock>

<Button type='submit' disabled={amount.length === 0}>
{getCapitalizedAction(action)}
</Button>
{showDelegationWarning ? (
<DelegationVotingPowerWarning amount={amount} />
) : (
<Button type='submit' disabled={amount.length === 0}>
{getCapitalizedAction(action)}
</Button>
)}
</form>
</>
)}
</DialogContent>
</Dialog>
);
};

const DelegationVotingPowerWarning = ({ amount }: { amount: string }) => {
return (
<>
<div className='flex items-center gap-4 text-red'>
<CircleAlert size={50} />
<div>
The validator you’re delegating to has more than 5% of the current voting power. To
promote decentralization, it’s recommended to choose a smaller validator.
</div>
</div>
<div className='flex gap-2'>
<DialogClose className='w-full' asChild>
<Button type='submit' variant='secondary' className='w-1/2'>
Choose another validator
</Button>
</DialogClose>
<Button
type='submit'
disabled={amount.length === 0}
variant='destructive'
className='w-1/2'
>
Delegate anyway
</Button>
</div>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { render } from '@testing-library/react';
import { ValidatorInfo } from '@penumbra-zone/protobuf/penumbra/core/component/stake/v1/stake_pb';
import { ValueView } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb';
import { AllSlices } from '../../../../../state';
import { IdentityKey } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb';

const nonZeroBalance = new ValueView({
valueView: {
Expand All @@ -23,7 +24,16 @@ const zeroBalance = new ValueView({
},
});

const validatorInfo = new ValidatorInfo({ validator: {} });
const validatorInfo = new ValidatorInfo({
validator: {
identityKey: new IdentityKey({
ik: new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4,
5,
]),
}),
},
});

let MOCK_STAKING_TOKENS_AND_FILTER: {
stakingTokens: ValueView | undefined;
Expand All @@ -44,7 +54,7 @@ type RecursivePartial<T> = {
vi.mock('../../../../../utils/use-store-shallow', async () => ({
...(await vi.importActual('../../../../../utils/use-store-shallow')),
useStoreShallow: (selector: (state: RecursivePartial<AllSlices>) => unknown) =>
selector({ staking: { account: 0 } }),
selector({ staking: { account: 0, votingPowerByValidatorInfo: { '': 0 } } }),
}));

describe('<StakingActions />', () => {
Expand Down

0 comments on commit e0070a5

Please sign in to comment.