* fix: dissasociation without vega wallet (806) * fix: removed unused import * chore: removed redundant func prepend0xIfNeeded * Update apps/token/src/routes/staking/disassociate/disassociate-page.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update apps/token/src/routes/staking/disassociate/disassociate-page.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>
This commit is contained in:
parent
aea2a85519
commit
db08a177c4
@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
export enum StakingMethod {
|
export enum StakingMethod {
|
||||||
Contract = 'Contract',
|
Contract = 'Contract',
|
||||||
Wallet = 'Wallet',
|
Wallet = 'Wallet',
|
||||||
|
Unknown = 'Unknown',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StakingMethodRadio = ({
|
export const StakingMethodRadio = ({
|
||||||
|
@ -3,25 +3,8 @@ import React from 'react';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAnimateValue } from '../../hooks/use-animate-value';
|
import { useAnimateValue } from '../../hooks/use-animate-value';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import type { BigNumber } from '../../lib/bignumber';
|
||||||
import { formatNumber } from '../../lib/format-number';
|
import { useNumberParts } from '../../lib/format-number';
|
||||||
|
|
||||||
const useNumberParts = (
|
|
||||||
value: BigNumber | null | undefined,
|
|
||||||
decimals: number
|
|
||||||
) => {
|
|
||||||
return React.useMemo(() => {
|
|
||||||
if (!value) {
|
|
||||||
return ['0', '0'.repeat(decimals)];
|
|
||||||
}
|
|
||||||
// @ts-ignore confident not undefined
|
|
||||||
const separator = BigNumber.config().FORMAT.decimalSeparator as string;
|
|
||||||
const [integers, decimalsPlaces] = formatNumber(value, 18)
|
|
||||||
.toString()
|
|
||||||
.split(separator);
|
|
||||||
return [integers, decimalsPlaces];
|
|
||||||
}, [decimals, value]);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface WalletCardProps {
|
interface WalletCardProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
33
apps/token/src/lib/format-number.spec.ts
Normal file
33
apps/token/src/lib/format-number.spec.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { BigNumber } from './bignumber';
|
||||||
|
import {
|
||||||
|
formatNumber,
|
||||||
|
formatNumberPercentage,
|
||||||
|
toNumberParts,
|
||||||
|
} from './format-number';
|
||||||
|
|
||||||
|
describe('formatNumber and formatNumberPercentage', () => {
|
||||||
|
it.each([
|
||||||
|
{ v: new BigNumber(123), d: 3, o: '123.000' },
|
||||||
|
{ v: new BigNumber(123.123), d: 3, o: '123.123' },
|
||||||
|
{ v: new BigNumber(123.123), d: 6, o: '123.123000' },
|
||||||
|
{ v: new BigNumber(123.123), d: 0, o: '123' },
|
||||||
|
{ v: new BigNumber(123), d: undefined, o: '123.00' }, // it default to 2 decimal places
|
||||||
|
])('formats given number correctly', ({ v, d, o }) => {
|
||||||
|
expect(formatNumber(v, d)).toStrictEqual(o);
|
||||||
|
expect(formatNumberPercentage(v, d)).toStrictEqual(`${o}%`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toNumberParts', () => {
|
||||||
|
it.each([
|
||||||
|
{ v: null, d: 3, o: ['0', '000'] },
|
||||||
|
{ v: undefined, d: 3, o: ['0', '000'] },
|
||||||
|
{ v: new BigNumber(123), d: 3, o: ['123', '000'] },
|
||||||
|
{ v: new BigNumber(123.123), d: 3, o: ['123', '123'] },
|
||||||
|
{ v: new BigNumber(123.123), d: 6, o: ['123', '123000'] },
|
||||||
|
{ v: new BigNumber(123.123), d: 0, o: ['123', ''] },
|
||||||
|
{ v: new BigNumber(123), d: undefined, o: ['123', '000000000000000000'] },
|
||||||
|
])('returns correct tuple given the different arguments', ({ v, d, o }) => {
|
||||||
|
expect(toNumberParts(v, d)).toStrictEqual(o);
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,5 @@
|
|||||||
import type { BigNumber } from './bignumber';
|
import React from 'react';
|
||||||
|
import { BigNumber } from './bignumber';
|
||||||
|
|
||||||
export const formatNumber = (value: BigNumber, decimals?: number) => {
|
export const formatNumber = (value: BigNumber, decimals?: number) => {
|
||||||
const decimalPlaces =
|
const decimalPlaces =
|
||||||
@ -6,8 +7,27 @@ export const formatNumber = (value: BigNumber, decimals?: number) => {
|
|||||||
return value.dp(decimalPlaces).toFormat(decimalPlaces);
|
return value.dp(decimalPlaces).toFormat(decimalPlaces);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatNumberPercentage = (value: BigNumber, decimals?: number) => {
|
export const formatNumberPercentage = (value: BigNumber, decimals?: number) =>
|
||||||
const decimalPlaces =
|
`${formatNumber(value, decimals)}%`;
|
||||||
typeof decimals === 'undefined' ? Math.max(value.dp(), 2) : decimals;
|
|
||||||
return `${value.dp(decimalPlaces).toFormat(decimalPlaces)}%`;
|
export const toNumberParts = (
|
||||||
|
value: BigNumber | null | undefined,
|
||||||
|
decimals = 18
|
||||||
|
): [integers: string, decimalPlaces: string] => {
|
||||||
|
if (!value) {
|
||||||
|
return ['0', '0'.repeat(decimals)];
|
||||||
|
}
|
||||||
|
// @ts-ignore confident not undefined
|
||||||
|
const separator = BigNumber.config().FORMAT.decimalSeparator as string;
|
||||||
|
const [integers, decimalsPlaces] = formatNumber(value, decimals)
|
||||||
|
.toString()
|
||||||
|
.split(separator);
|
||||||
|
return [integers, decimalsPlaces || ''];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useNumberParts = (
|
||||||
|
value: BigNumber | null | undefined,
|
||||||
|
decimals: number
|
||||||
|
): [integers: string, decimalPlaces: string] => {
|
||||||
|
return React.useMemo(() => toNumberParts(value, decimals), [decimals, value]);
|
||||||
};
|
};
|
||||||
|
15
apps/token/src/lib/truncate-middle.spec.ts
Normal file
15
apps/token/src/lib/truncate-middle.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { truncateMiddle } from './truncate-middle';
|
||||||
|
|
||||||
|
describe('truncateMiddle', () => {
|
||||||
|
it.each([
|
||||||
|
{ i: '1234567890134567890', o: '123456\u20267890' },
|
||||||
|
{ i: '12345678901', o: '123456\u20268901' },
|
||||||
|
{ i: '1234567890', o: '1234567890' },
|
||||||
|
{ i: '123456', o: '123456' },
|
||||||
|
])(
|
||||||
|
'truncates the middle section of any long string (address)',
|
||||||
|
({ i, o }) => {
|
||||||
|
expect(truncateMiddle(i)).toStrictEqual(o);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
@ -1,4 +1,5 @@
|
|||||||
export function truncateMiddle(address: string) {
|
export function truncateMiddle(address: string) {
|
||||||
|
if (address.length < 11) return address;
|
||||||
return (
|
return (
|
||||||
address.slice(0, 6) +
|
address.slice(0, 6) +
|
||||||
'\u2026' +
|
'\u2026' +
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { TokenInput } from '../../../components/token-input';
|
|
||||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
|
||||||
|
|
||||||
export const ContractDisassociate = ({
|
|
||||||
perform,
|
|
||||||
amount,
|
|
||||||
setAmount,
|
|
||||||
}: {
|
|
||||||
perform: () => void;
|
|
||||||
amount: string;
|
|
||||||
setAmount: React.Dispatch<React.SetStateAction<string>>;
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
appState: { lien },
|
|
||||||
} = useAppState();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
if (lien.isEqualTo('0')) {
|
|
||||||
return (
|
|
||||||
<div className="disassociate-page__error">
|
|
||||||
{t(
|
|
||||||
'You have no VEGA tokens currently staked through your connected Eth wallet.'
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TokenInput
|
|
||||||
submitText={t('Disassociate VEGA Tokens from key')}
|
|
||||||
perform={perform}
|
|
||||||
maximum={lien}
|
|
||||||
amount={amount}
|
|
||||||
setAmount={setAmount}
|
|
||||||
currency={t('VEGA Tokens')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,17 +1,12 @@
|
|||||||
import { StakingWalletsContainer } from '../staking-wallets-container';
|
import { StakingWalletsContainer } from '../staking-wallets-container';
|
||||||
import { DisassociatePage } from './disassociate-page';
|
import { DisassociatePage } from './disassociate-page';
|
||||||
import { DisassociatePageNoVega } from './disassociate-page-no-vega';
|
|
||||||
|
|
||||||
export const DisassociateContainer = () => {
|
export const DisassociateContainer = () => {
|
||||||
return (
|
return (
|
||||||
<StakingWalletsContainer needsEthereum={true} needsVega={false}>
|
<StakingWalletsContainer needsEthereum={true} needsVega={false}>
|
||||||
{({ address, currVegaKey = null }) =>
|
{({ address, currVegaKey = null }) => (
|
||||||
currVegaKey ? (
|
<DisassociatePage address={address} vegaKey={currVegaKey?.pub ?? ''} />
|
||||||
<DisassociatePage address={address} vegaKey={currVegaKey} />
|
)}
|
||||||
) : (
|
|
||||||
<DisassociatePageNoVega />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</StakingWalletsContainer>
|
</StakingWalletsContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import {
|
|
||||||
StakingMethod,
|
|
||||||
StakingMethodRadio,
|
|
||||||
} from '../../../components/staking-method-radio';
|
|
||||||
import { useSearchParams } from '../../../hooks/use-search-params';
|
|
||||||
import { ConnectToVega } from '../connect-to-vega';
|
|
||||||
import { ContractDisassociate } from './contract-disassociate';
|
|
||||||
|
|
||||||
export const DisassociatePageNoVega = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const params = useSearchParams();
|
|
||||||
const [amount, setAmount] = React.useState<string>('');
|
|
||||||
const [selectedStakingMethod, setSelectedStakingMethod] =
|
|
||||||
React.useState<StakingMethod | null>(
|
|
||||||
(params.method as StakingMethod) || null
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="disassociate-page" data-testid="disassociate-page">
|
|
||||||
<p>
|
|
||||||
{t(
|
|
||||||
'Use this form to disassociate VEGA tokens with a Vega key. This returns them to either the Ethereum wallet that used the Staking bridge or the vesting contract.'
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="disassociate-page__error">{t('Warning')}:</span>{' '}
|
|
||||||
{t(
|
|
||||||
'Any Tokens that have been nominated to a node will sacrifice any Rewards they are due for the current epoch. If you do not wish to sacrifices fees you should remove stake from a node at the end of an epoch before disassocation.'
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<h2>{t('What Vega wallet are you removing Tokens from?')}</h2>
|
|
||||||
<ConnectToVega />
|
|
||||||
<h2>{t('What tokens would you like to return?')}</h2>
|
|
||||||
<StakingMethodRadio
|
|
||||||
setSelectedStakingMethod={setSelectedStakingMethod}
|
|
||||||
selectedStakingMethod={selectedStakingMethod}
|
|
||||||
/>
|
|
||||||
{selectedStakingMethod &&
|
|
||||||
(selectedStakingMethod === StakingMethod.Wallet ? (
|
|
||||||
<ConnectToVega />
|
|
||||||
) : (
|
|
||||||
<ContractDisassociate
|
|
||||||
setAmount={setAmount}
|
|
||||||
amount={amount}
|
|
||||||
perform={() => undefined}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,65 +1,147 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { ConnectedVegaKey } from '../../../components/connected-vega-key';
|
|
||||||
import {
|
|
||||||
StakingMethod,
|
|
||||||
StakingMethodRadio,
|
|
||||||
} from '../../../components/staking-method-radio';
|
|
||||||
import { TxState } from '../../../hooks/transaction-reducer';
|
|
||||||
import { useSearchParams } from '../../../hooks/use-search-params';
|
|
||||||
import { useRefreshAssociatedBalances } from '../../../hooks/use-refresh-associated-balances';
|
|
||||||
import { ContractDisassociate } from './contract-disassociate';
|
|
||||||
import { DisassociateTransaction } from './disassociate-transaction';
|
import { DisassociateTransaction } from './disassociate-transaction';
|
||||||
|
import { formatNumber } from '../../../lib/format-number';
|
||||||
|
import { remove0x, toBigNum } from '@vegaprotocol/react-helpers';
|
||||||
|
import { Select } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { StakingMethod } from '../../../components/staking-method-radio';
|
||||||
|
import { TokenInput } from '../../../components/token-input';
|
||||||
|
import { TxState } from '../../../hooks/transaction-reducer';
|
||||||
|
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||||
|
import { useRefreshAssociatedBalances } from '../../../hooks/use-refresh-associated-balances';
|
||||||
import { useRemoveStake } from './hooks';
|
import { useRemoveStake } from './hooks';
|
||||||
import { WalletDisassociate } from './wallet-disassociate';
|
import type { RemoveStakePayload } from './hooks';
|
||||||
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { BigNumber } from '../../../lib/bignumber';
|
||||||
|
|
||||||
|
type Association = {
|
||||||
|
key: string;
|
||||||
|
value: BigNumber;
|
||||||
|
stakingMethod: StakingMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toListOfAssociations = (
|
||||||
|
obj: { [vegaKey: string]: BigNumber },
|
||||||
|
stakingMethod: StakingMethod
|
||||||
|
): Association[] =>
|
||||||
|
Object.keys(obj)
|
||||||
|
.map((k) => ({
|
||||||
|
key: remove0x(k),
|
||||||
|
value: obj[k],
|
||||||
|
stakingMethod,
|
||||||
|
}))
|
||||||
|
.filter((k) => k.value.isGreaterThan(0));
|
||||||
|
|
||||||
export const DisassociatePage = ({
|
export const DisassociatePage = ({
|
||||||
address,
|
address,
|
||||||
vegaKey,
|
vegaKey,
|
||||||
}: {
|
}: {
|
||||||
address: string;
|
address: string;
|
||||||
vegaKey: VegaKeyExtended;
|
vegaKey: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const params = useSearchParams();
|
|
||||||
const [amount, setAmount] = React.useState<string>('');
|
const {
|
||||||
const [selectedStakingMethod, setSelectedStakingMethod] =
|
appState: {
|
||||||
React.useState<StakingMethod | null>(
|
associationBreakdown: { stakingAssociations, vestingAssociations },
|
||||||
(params.method as StakingMethod) || null
|
},
|
||||||
);
|
} = useAppState();
|
||||||
|
|
||||||
|
const associations = useMemo(
|
||||||
|
() => [
|
||||||
|
...toListOfAssociations(stakingAssociations, StakingMethod.Wallet),
|
||||||
|
...toListOfAssociations(vestingAssociations, StakingMethod.Contract),
|
||||||
|
],
|
||||||
|
[stakingAssociations, vestingAssociations]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setChosen(associations.find((k) => k.key === vegaKey) || associations[0]);
|
||||||
|
}, [associations, vegaKey]);
|
||||||
|
|
||||||
|
const [chosen, setChosen] = useState<Association>();
|
||||||
|
|
||||||
|
const maximum = chosen?.value || toBigNum(0, 0);
|
||||||
|
const [amount, setAmount] = useState<string>('');
|
||||||
|
|
||||||
const refreshBalances = useRefreshAssociatedBalances();
|
const refreshBalances = useRefreshAssociatedBalances();
|
||||||
|
|
||||||
// Clear the amount when the staking method changes
|
const payload: RemoveStakePayload = {
|
||||||
React.useEffect(() => {
|
amount,
|
||||||
setAmount('');
|
vegaKey: chosen?.key || '',
|
||||||
}, [selectedStakingMethod]);
|
stakingMethod: chosen?.stakingMethod || StakingMethod.Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: txState,
|
state: txState,
|
||||||
dispatch: txDispatch,
|
dispatch: txDispatch,
|
||||||
perform: txPerform,
|
perform: txPerform,
|
||||||
} = useRemoveStake(address, amount, vegaKey.pub, selectedStakingMethod);
|
} = useRemoveStake(address, payload);
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (txState.txState === TxState.Complete) {
|
if (txState.txState === TxState.Complete) {
|
||||||
refreshBalances(address, vegaKey.pub);
|
refreshBalances(address, chosen?.key || '');
|
||||||
}
|
}
|
||||||
}, [txState, refreshBalances, address, vegaKey.pub]);
|
}, [txState, refreshBalances, address, chosen]);
|
||||||
|
|
||||||
if (txState.txState !== TxState.Default) {
|
if (txState.txState !== TxState.Default && payload) {
|
||||||
return (
|
return (
|
||||||
<DisassociateTransaction
|
<DisassociateTransaction
|
||||||
state={txState}
|
state={txState}
|
||||||
amount={amount}
|
amount={amount}
|
||||||
vegaKey={vegaKey.pub}
|
vegaKey={chosen?.key || ''}
|
||||||
stakingMethod={selectedStakingMethod as StakingMethod}
|
stakingMethod={payload.stakingMethod}
|
||||||
dispatch={txDispatch}
|
dispatch={txDispatch}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const noKeysMessage = (
|
||||||
|
<div className="disassociate-page__error">
|
||||||
|
{t(
|
||||||
|
'You have no VEGA tokens currently associated through your connected Ethereum wallet.'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const disassociate = (
|
||||||
|
<>
|
||||||
|
<div className="pb-8">
|
||||||
|
<Select
|
||||||
|
className="font-mono"
|
||||||
|
disabled={associations.length === 1}
|
||||||
|
id="vega-key-selector"
|
||||||
|
onChange={(e) => {
|
||||||
|
if (!e.target.value) return;
|
||||||
|
const chosen = associations.find((k) => k.key === e.target.value);
|
||||||
|
if (chosen) {
|
||||||
|
setChosen(chosen);
|
||||||
|
setAmount('');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={chosen?.key}
|
||||||
|
>
|
||||||
|
{associations.map((k) => (
|
||||||
|
<option
|
||||||
|
key={k.key}
|
||||||
|
value={k.key}
|
||||||
|
title={`${t(k.stakingMethod)}: ${formatNumber(k.value, 18)}`}
|
||||||
|
>
|
||||||
|
{k.key}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<TokenInput
|
||||||
|
submitText={t('Disassociate VEGA Tokens from key')}
|
||||||
|
perform={txPerform}
|
||||||
|
maximum={maximum}
|
||||||
|
amount={amount}
|
||||||
|
setAmount={setAmount}
|
||||||
|
currency={t('VEGA Tokens')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="disassociate-page" data-testid="disassociate-page">
|
<section className="disassociate-page" data-testid="disassociate-page">
|
||||||
<p>
|
<p>
|
||||||
@ -70,30 +152,12 @@ export const DisassociatePage = ({
|
|||||||
<p>
|
<p>
|
||||||
<span className="text-vega-red">{t('Warning')}:</span>{' '}
|
<span className="text-vega-red">{t('Warning')}:</span>{' '}
|
||||||
{t(
|
{t(
|
||||||
'Any Tokens that have been nominated to a node will sacrifice any Rewards they are due for the current epoch. If you do not wish to sacrifices fees you should remove stake from a node at the end of an epoch before disassocation.'
|
'Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.'
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<h2>{t('What Vega wallet are you removing Tokens from?')}</h2>
|
|
||||||
<ConnectedVegaKey pubKey={vegaKey.pub} />
|
|
||||||
<h2>{t('What tokens would you like to return?')}</h2>
|
<h2>{t('What tokens would you like to return?')}</h2>
|
||||||
<StakingMethodRadio
|
{associations.length === 0 ? noKeysMessage : disassociate}
|
||||||
setSelectedStakingMethod={setSelectedStakingMethod}
|
|
||||||
selectedStakingMethod={selectedStakingMethod}
|
|
||||||
/>
|
|
||||||
{selectedStakingMethod &&
|
|
||||||
(selectedStakingMethod === StakingMethod.Wallet ? (
|
|
||||||
<WalletDisassociate
|
|
||||||
setAmount={setAmount}
|
|
||||||
amount={amount}
|
|
||||||
perform={txPerform}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ContractDisassociate
|
|
||||||
setAmount={setAmount}
|
|
||||||
amount={amount}
|
|
||||||
perform={txPerform}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,12 +8,24 @@ import { TxState } from '../../../hooks/transaction-reducer';
|
|||||||
import { useGetAssociationBreakdown } from '../../../hooks/use-get-association-breakdown';
|
import { useGetAssociationBreakdown } from '../../../hooks/use-get-association-breakdown';
|
||||||
import { useRefreshBalances } from '../../../hooks/use-refresh-balances';
|
import { useRefreshBalances } from '../../../hooks/use-refresh-balances';
|
||||||
import { useTransaction } from '../../../hooks/use-transaction';
|
import { useTransaction } from '../../../hooks/use-transaction';
|
||||||
|
import { initialState } from '../../../hooks/transaction-reducer';
|
||||||
|
|
||||||
|
export type RemoveStakePayload = {
|
||||||
|
amount: string;
|
||||||
|
vegaKey: string;
|
||||||
|
stakingMethod: StakingMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_REMOVE = {
|
||||||
|
state: initialState,
|
||||||
|
dispatch: () => undefined,
|
||||||
|
perform: () => undefined as void,
|
||||||
|
reset: () => undefined as void,
|
||||||
|
};
|
||||||
|
|
||||||
export const useRemoveStake = (
|
export const useRemoveStake = (
|
||||||
address: string,
|
address: string,
|
||||||
amount: string,
|
payload: RemoveStakePayload
|
||||||
vegaKey: string,
|
|
||||||
stakingMethod: StakingMethod | null
|
|
||||||
) => {
|
) => {
|
||||||
const { appState } = useAppState();
|
const { appState } = useAppState();
|
||||||
const { staking, vesting } = useContracts();
|
const { staking, vesting } = useContracts();
|
||||||
@ -21,11 +33,18 @@ export const useRemoveStake = (
|
|||||||
// which if staked > wallet balance means you cannot unstaked
|
// which if staked > wallet balance means you cannot unstaked
|
||||||
// even worse if you stake everything then you can't unstake anything!
|
// even worse if you stake everything then you can't unstake anything!
|
||||||
const contractRemove = useTransaction(() =>
|
const contractRemove = useTransaction(() =>
|
||||||
vesting.remove_stake(removeDecimal(amount, appState.decimals), vegaKey)
|
vesting.remove_stake(
|
||||||
|
removeDecimal(payload.amount, appState.decimals),
|
||||||
|
payload.vegaKey
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const walletRemove = useTransaction(() =>
|
const walletRemove = useTransaction(() =>
|
||||||
staking.remove_stake(removeDecimal(amount, appState.decimals), vegaKey)
|
staking.remove_stake(
|
||||||
|
removeDecimal(payload.amount, appState.decimals),
|
||||||
|
payload.vegaKey
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshBalances = useRefreshBalances(address);
|
const refreshBalances = useRefreshBalances(address);
|
||||||
const getAssociationBreakdown = useGetAssociationBreakdown(
|
const getAssociationBreakdown = useGetAssociationBreakdown(
|
||||||
address,
|
address,
|
||||||
@ -49,10 +68,13 @@ export const useRemoveStake = (
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
if (stakingMethod === StakingMethod.Contract) {
|
switch (payload.stakingMethod) {
|
||||||
return contractRemove;
|
case StakingMethod.Contract:
|
||||||
} else {
|
return contractRemove;
|
||||||
return walletRemove;
|
case StakingMethod.Wallet:
|
||||||
|
return walletRemove;
|
||||||
|
default:
|
||||||
|
return EMPTY_REMOVE;
|
||||||
}
|
}
|
||||||
}, [contractRemove, stakingMethod, walletRemove]);
|
}, [contractRemove, payload, walletRemove]);
|
||||||
};
|
};
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { TokenInput } from '../../../components/token-input';
|
|
||||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
|
||||||
|
|
||||||
export const WalletDisassociate = ({
|
|
||||||
perform,
|
|
||||||
amount,
|
|
||||||
setAmount,
|
|
||||||
}: {
|
|
||||||
perform: () => void;
|
|
||||||
amount: string;
|
|
||||||
setAmount: React.Dispatch<React.SetStateAction<string>>;
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
appState: { walletAssociatedBalance },
|
|
||||||
} = useAppState();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
if (!walletAssociatedBalance || walletAssociatedBalance.isEqualTo('0')) {
|
|
||||||
return (
|
|
||||||
<div className="disassociate-page__error">
|
|
||||||
{t(
|
|
||||||
'You have no VEGA tokens currently staked through your connected Vega wallet.'
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TokenInput
|
|
||||||
submitText={t('Disassociate VEGA Tokens from key')}
|
|
||||||
perform={perform}
|
|
||||||
maximum={walletAssociatedBalance}
|
|
||||||
amount={amount}
|
|
||||||
setAmount={setAmount}
|
|
||||||
currency={t('VEGA Tokens')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,6 +1,11 @@
|
|||||||
import { prepend0x } from './prepend-0x';
|
import { prepend0x } from './prepend-0x';
|
||||||
|
|
||||||
test('Prepends strings with 0x', () => {
|
describe('prepend0x', () => {
|
||||||
expect(prepend0x('abc')).toEqual('0xabc');
|
it.each([
|
||||||
expect(prepend0x('123456789')).toEqual('0x123456789');
|
{ input: 'ABC123', output: '0xABC123' },
|
||||||
|
{ input: '0XABC123', output: '0x0XABC123' },
|
||||||
|
{ input: '0xABC123', output: '0xABC123' },
|
||||||
|
])('prepends strings with 0x only if needed', ({ input, output }) => {
|
||||||
|
expect(prepend0x(input)).toBe(output);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export function prepend0x(str: string) {
|
export function prepend0x(str: string) {
|
||||||
return `0x${str}`;
|
return !str || str.indexOf('0x') === 0 ? str : `0x${str}`;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user