handle undelegation of locked tokens

This commit is contained in:
fmtabbara
2022-05-30 22:07:05 +01:00
parent b2ce142c17
commit 37d55f3a06
4 changed files with 69 additions and 63 deletions
+16 -14
View File
@@ -7,7 +7,12 @@ import {
TransactionExecuteResult,
} from '@nymproject/types';
import type { Network } from 'src/types';
import { delegateToMixnode, getAllPendingDelegations, vestingDelegateToMixnode } from 'src/requests';
import {
delegateToMixnode,
getAllPendingDelegations,
vestingDelegateToMixnode,
vestingUndelegateFromMixnode,
} from 'src/requests';
import { TPoolOption } from 'src/components';
export type TDelegationContext = {
@@ -22,8 +27,7 @@ export type TDelegationContext = {
data: { identity: string; amount: MajorCurrencyAmount },
tokenPool: TPoolOption,
) => Promise<TransactionExecuteResult>;
updateDelegation: (newDelegation: DelegationWithEverything) => Promise<TDelegationTransaction>;
undelegate: (identity: string) => Promise<TransactionExecuteResult>;
undelegate: (identity: string, proxy: string | null) => Promise<TransactionExecuteResult>;
};
export type TDelegationTransaction = {
@@ -36,9 +40,6 @@ export const DelegationContext = createContext<TDelegationContext>({
addDelegation: async () => {
throw new Error('Not implemented');
},
updateDelegation: async () => {
throw new Error('Not implemented');
},
undelegate: async () => {
throw new Error('Not implemented');
},
@@ -68,12 +69,15 @@ export const DelegationContextProvider: FC<{
}
};
const updateDelegation = async (): Promise<TDelegationTransaction> => {
throw new Error('Not implemented');
};
const undelegate = async (identity: string) => {
const delegationResult = await undelegateFromMixnode(identity);
const undelegate = async (identity: string, proxy: string | null) => {
let delegationResult;
if ((proxy || '').trim().length === 0) {
// the owner of the delegation is main account (the owner of the vesting account), so it is delegation with unlocked tokens
delegationResult = await undelegateFromMixnode(identity);
} else {
// the delegation is with locked tokens, so use the vesting contract
delegationResult = await vestingUndelegateFromMixnode(identity);
}
await refresh();
return delegationResult;
};
@@ -101,7 +105,6 @@ export const DelegationContextProvider: FC<{
}, [network]);
useEffect(() => {
// reset state and refresh
resetState();
refresh();
}, [network]);
@@ -116,7 +119,6 @@ export const DelegationContextProvider: FC<{
totalRewards,
refresh,
addDelegation,
updateDelegation,
undelegate,
}),
[isLoading, error, delegations, pendingDelegations, totalDelegations],
+1 -13
View File
@@ -7,7 +7,7 @@ export const MockRewardsContextProvider: FC = ({ children }) => {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string>();
const [totalRewards, setTotalRewards] = useState<undefined | string>();
const { delegations, updateDelegation } = useDelegationContext();
const { delegations } = useDelegationContext();
const delegationsHash = delegations?.map((d) => d.accumulated_rewards).join(',');
const resetState = () => {
@@ -55,10 +55,6 @@ export const MockRewardsContextProvider: FC = ({ children }) => {
await mockSleep(1000);
// the `updateDelegation as any` is hacking typescript with type erasure so that
// the hook interface can stay the same and the mock implementation is easy
await (updateDelegation as any)({ ...d, reward: undefined }, true);
return {
transactionUrl:
'https://sandbox-blocks.nymtech.net/transactions/55303CD4B91FAC4C2715E40EBB52BB3B92829D9431B3A279D37B5CC58432E354',
@@ -72,14 +68,6 @@ export const MockRewardsContextProvider: FC = ({ children }) => {
await mockSleep(1000);
// eslint-disable-next-line no-restricted-syntax
for (const d of delegations) {
// the `updateDelegation as any` is hacking typescript with type erasure so that
// the hook interface can stay the same and the mock implementation is easy
// eslint-disable-next-line no-await-in-loop
await (updateDelegation as any)({ ...d, reward: undefined }, true);
}
return {
transactionUrl:
'https://sandbox-blocks.nymtech.net/transactions/55303CD4B91FAC4C2715E40EBB52BB3B92829D9431B3A279D37B5CC58432E354',
+4 -4
View File
@@ -31,10 +31,10 @@ export type TUseuserBalance = {
currentVestingPeriod?: Period;
vestingAccountInfo?: VestingAccountInfo;
isLoading: boolean;
fetchBalance: () => void;
fetchBalance: () => Promise<void>;
fetchTokenAllocation: () => Promise<void>;
clearBalance: () => void;
clearAll: () => void;
fetchTokenAllocation: () => void;
};
export const useGetBalance = (clientDetails?: Account): TUseuserBalance => {
@@ -60,7 +60,7 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => {
vestedCoins,
lockedCoins,
spendableCoins,
currentVestingPer,
currentVestingPeriod,
vestingAccountDetail,
] = await Promise.all([
getOriginalVesting(clientDetails?.client_address),
@@ -72,7 +72,7 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => {
getVestingAccountInfo(clientDetails?.client_address),
]);
setOriginalVesting(originalVestingValue);
setCurrentVestingPeriod(currentVestingPer);
setCurrentVestingPeriod(currentVestingPeriod);
setTokenAllocation({
vesting: vestingCoins.amount,
vested: vestedCoins.amount,
+48 -32
View File
@@ -1,8 +1,11 @@
import React, { FC, useContext, useEffect, useState } from 'react';
import { Box, Button, Link, Paper, Stack, Typography } from '@mui/material';
import { DelegationWithEverything, MajorCurrencyAmount } from '@nymproject/types';
import { AppContext } from 'src/context/main';
import { AppContext, urls } from 'src/context/main';
import { DelegationList } from 'src/components/Delegation/DelegationList';
import { PendingEvents } from 'src/components/Delegation/PendingEvents';
import { TPoolOption } from 'src/components';
import { getSpendableCoins, userBalance } from 'src/requests';
import { RewardsSummary } from '../../components/Rewards/RewardsSummary';
import { useDelegationContext, DelegationContextProvider } from '../../context/delegations';
import { RewardsContextProvider, useRewardsContext } from '../../context/rewards';
@@ -11,8 +14,6 @@ import { UndelegateModal } from '../../components/Delegation/UndelegateModal';
import { DelegationListItemActions } from '../../components/Delegation/DelegationActions';
import { RedeemModal } from '../../components/Rewards/RedeemModal';
import { DelegationModal, DelegationModalProps } from '../../components/Delegation/DelegationModal';
import { PendingEvents } from 'src/components/Delegation/PendingEvents';
import { TPoolOption } from 'src/components';
const explorerUrl = 'https://sandbox-explorer.nymtech.net';
@@ -25,7 +26,11 @@ export const Delegation: FC = () => {
const [confirmationModalProps, setConfirmationModalProps] = useState<DelegationModalProps | undefined>();
const [currentDelegationListActionItem, setCurrentDelegationListActionItem] = useState<DelegationWithEverything>();
const { clientDetails, userBalance } = useContext(AppContext);
const {
clientDetails,
network,
userBalance: { balance, originalVesting },
} = useContext(AppContext);
const { redeemAllRewards, redeemRewards, totalRewards, isLoading: isLoadingRewards } = useRewardsContext();
const {
delegations,
@@ -33,7 +38,6 @@ export const Delegation: FC = () => {
totalDelegations,
isLoading: isLoadingDelegations,
addDelegation,
updateDelegation,
undelegate,
refresh,
} = useDelegationContext();
@@ -44,8 +48,6 @@ export const Delegation: FC = () => {
return () => clearInterval(timer);
}, []);
// TODO: replace with real operation
const handleDelegationItemActionClick = (item: DelegationWithEverything, action: DelegationListItemActions) => {
setCurrentDelegationListActionItem(item);
// eslint-disable-next-line default-case
@@ -77,12 +79,20 @@ export const Delegation: FC = () => {
},
tokenPool,
);
await userBalance.fetchBalance();
const balance = await userBalance();
let spendableLocked;
if (tokenPool === 'locked') spendableLocked = await getSpendableCoins();
setConfirmationModalProps({
status: 'success',
action: 'delegate',
balance: userBalance.balance?.printable_balance || '-',
transactionUrl: tx.transaction_hash,
balance:
tokenPool === 'locked'
? `${spendableLocked?.amount} ${spendableLocked?.denom}`
: balance?.printable_balance || '-',
transactionUrl: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`,
});
} catch (e) {
setConfirmationModalProps({
@@ -117,12 +127,17 @@ export const Delegation: FC = () => {
},
tokenPool,
);
await userBalance.fetchBalance();
let balance = await userBalance();
let spendableLocked;
if (originalVesting) spendableLocked = await getSpendableCoins();
setConfirmationModalProps({
status: 'success',
action: 'delegate',
balance: userBalance.balance?.printable_balance || '-',
transactionUrl: tx.transaction_hash,
balance:
tokenPool === 'locked' ? `${spendableLocked} ${clientDetails?.denom}` : balance?.printable_balance || '-',
transactionUrl: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`,
});
} catch (e) {
setConfirmationModalProps({
@@ -133,21 +148,23 @@ export const Delegation: FC = () => {
}
};
const handleUndelegate = async (identityKey: string) => {
const handleUndelegate = async (identityKey: string, proxy: string | null) => {
setConfirmationModalProps({
status: 'loading',
action: 'undelegate',
});
setShowUndelegateModal(false);
setCurrentDelegationListActionItem(undefined);
try {
const tx = await undelegate(identityKey);
await userBalance.fetchBalance();
const tx = await undelegate(identityKey, proxy);
const balance = await userBalance();
setConfirmationModalProps({
status: 'success',
action: 'undelegate',
balance: userBalance.balance?.printable_balance || '-',
transactionUrl: tx.transaction_hash,
balance: balance?.printable_balance || '-',
transactionUrl: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`,
});
} catch (e) {
setConfirmationModalProps({
@@ -175,13 +192,13 @@ export const Delegation: FC = () => {
if (clientDetails?.client_address) {
try {
const tx = await redeemRewards(identityKey);
await userBalance.fetchBalance();
const balance = await userBalance();
setConfirmationModalProps({
status: 'success',
action: 'redeem',
balance: userBalance.balance?.printable_balance || '-',
balance: balance?.printable_balance || '-',
recipient: clientDetails?.client_address,
transactionUrl: tx.transactionUrl,
transactionUrl: `${urls(network).blockExplorer}/${tx}}`,
});
} catch (e) {
setConfirmationModalProps({
@@ -202,11 +219,12 @@ export const Delegation: FC = () => {
setCurrentDelegationListActionItem(undefined);
try {
const tx = await redeemAllRewards();
await userBalance.fetchBalance();
const balance = await userBalance();
setConfirmationModalProps({
status: 'success',
action: 'redeem-all',
balance: userBalance.balance?.printable_balance || '-',
balance: balance?.printable_balance || '-',
recipient: clientDetails?.client_address,
transactionUrl: tx.transactionUrl,
});
@@ -226,7 +244,7 @@ export const Delegation: FC = () => {
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Delegations</Typography>
<Link
href={`${explorerUrl}/network-components/mixnodes/`}
href={`${urls(network).networkExplorer}/network-components/mixnodes/`}
target="_blank"
rel="noreferrer"
underline="hover"
@@ -280,12 +298,9 @@ export const Delegation: FC = () => {
buttonText="Delegate stake"
currency={clientDetails!.denom}
fee={0.004375}
estimatedMonthlyReward={50.123}
accountBalance={userBalance.balance?.printable_balance}
nodeUptimePercentage={99.28394}
profitMarginPercentage={11.12334234}
accountBalance={balance?.printable_balance}
rewardInterval="weekly"
hasVestingContract={Boolean(userBalance.originalVesting)}
hasVestingContract={Boolean(originalVesting)}
/>
)}
@@ -299,12 +314,12 @@ export const Delegation: FC = () => {
identityKey={currentDelegationListActionItem.node_identity}
currency={clientDetails!.denom}
fee={0.004375}
estimatedMonthlyReward={0}
accountBalance={userBalance.balance?.printable_balance}
estimatedReward={0}
accountBalance={balance?.printable_balance}
nodeUptimePercentage={currentDelegationListActionItem.avg_uptime_percent}
profitMarginPercentage={currentDelegationListActionItem.profit_margin_percent}
rewardInterval="weekly"
hasVestingContract={Boolean(userBalance.originalVesting)}
hasVestingContract={Boolean(originalVesting)}
/>
)}
@@ -313,6 +328,7 @@ export const Delegation: FC = () => {
open={showUndelegateModal}
onClose={() => setShowUndelegateModal(false)}
onOk={handleUndelegate}
proxy={currentDelegationListActionItem.proxy}
currency={currentDelegationListActionItem.amount.denom}
fee={0.1}
amount={+currentDelegationListActionItem.amount.amount}