* chore: set curly eslint rule to multi line * chore: fix lint * chore: make eslint rule error and fix resulting errors
236 lines
7.6 KiB
TypeScript
236 lines
7.6 KiB
TypeScript
import { gql, useApolloClient } from '@apollo/client';
|
|
import * as Sentry from '@sentry/react';
|
|
import keyBy from 'lodash/keyBy';
|
|
import uniq from 'lodash/uniq';
|
|
import React from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import noIcon from '../../images/token-no-icon.png';
|
|
import vegaBlack from '../../images/vega_black.png';
|
|
import { BigNumber } from '../../lib/bignumber';
|
|
import { addDecimal } from '../../lib/decimals';
|
|
import type { WalletCardAssetProps } from '../wallet-card';
|
|
import type {
|
|
Delegations,
|
|
Delegations_party_delegations,
|
|
DelegationsVariables,
|
|
} from './__generated__/Delegations';
|
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
import { useContracts } from '../../contexts/contracts/contracts-context';
|
|
import { isAssetTypeERC20 } from '@vegaprotocol/react-helpers';
|
|
import { AccountType } from '@vegaprotocol/types';
|
|
|
|
const DELEGATIONS_QUERY = gql`
|
|
query Delegations($partyId: ID!) {
|
|
epoch {
|
|
id
|
|
}
|
|
party(id: $partyId) {
|
|
id
|
|
delegations {
|
|
amountFormatted @client
|
|
amount
|
|
node {
|
|
id
|
|
name
|
|
}
|
|
epoch
|
|
}
|
|
stake {
|
|
currentStakeAvailable
|
|
currentStakeAvailableFormatted @client
|
|
}
|
|
accounts {
|
|
asset {
|
|
name
|
|
id
|
|
decimals
|
|
symbol
|
|
source {
|
|
__typename
|
|
... on ERC20 {
|
|
contractAddress
|
|
}
|
|
}
|
|
}
|
|
type
|
|
balance
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
export const usePollForDelegations = () => {
|
|
const { token: vegaToken } = useContracts();
|
|
const { t } = useTranslation();
|
|
const { keypair } = useVegaWallet();
|
|
const client = useApolloClient();
|
|
const [delegations, setDelegations] = React.useState<
|
|
Delegations_party_delegations[]
|
|
>([]);
|
|
const [delegatedNodes, setDelegatedNodes] = React.useState<
|
|
{
|
|
nodeId: string;
|
|
name: string;
|
|
hasStakePending: boolean;
|
|
currentEpochStake?: BigNumber;
|
|
nextEpochStake?: BigNumber;
|
|
}[]
|
|
>([]);
|
|
const [accounts, setAccounts] = React.useState<WalletCardAssetProps[]>([]);
|
|
const [currentStakeAvailable, setCurrentStakeAvailable] =
|
|
React.useState<BigNumber>(new BigNumber(0));
|
|
|
|
React.useEffect(() => {
|
|
// eslint-disable-next-line
|
|
let interval: any;
|
|
let mounted = true;
|
|
|
|
if (keypair?.pub) {
|
|
// start polling for delegation
|
|
interval = setInterval(() => {
|
|
client
|
|
.query<Delegations, DelegationsVariables>({
|
|
query: DELEGATIONS_QUERY,
|
|
variables: { partyId: keypair.pub },
|
|
fetchPolicy: 'network-only',
|
|
})
|
|
.then((res) => {
|
|
if (!mounted) return;
|
|
const filter =
|
|
res.data.party?.delegations?.filter((d) => {
|
|
return d.epoch.toString() === res.data.epoch.id;
|
|
}) || [];
|
|
const sortedDelegations = [...filter].sort((a, b) => {
|
|
return new BigNumber(b.amountFormatted)
|
|
.minus(a.amountFormatted)
|
|
.toNumber();
|
|
});
|
|
setDelegations(sortedDelegations);
|
|
setCurrentStakeAvailable(
|
|
new BigNumber(
|
|
res.data.party?.stake.currentStakeAvailableFormatted || 0
|
|
)
|
|
);
|
|
const accounts = res.data.party?.accounts || [];
|
|
setAccounts(
|
|
accounts
|
|
.filter((a) => a.type === AccountType.ACCOUNT_TYPE_GENERAL)
|
|
.map((a) => {
|
|
const isVega =
|
|
isAssetTypeERC20(a.asset) &&
|
|
a.asset.source.contractAddress === vegaToken.address;
|
|
|
|
return {
|
|
isVega,
|
|
name: a.asset.name,
|
|
subheading: isVega ? t('collateral') : a.asset.symbol,
|
|
symbol: a.asset.symbol,
|
|
decimals: a.asset.decimals,
|
|
balance: new BigNumber(
|
|
addDecimal(new BigNumber(a.balance), a.asset.decimals)
|
|
),
|
|
image: isVega ? vegaBlack : noIcon,
|
|
border: isVega,
|
|
address: isAssetTypeERC20(a.asset)
|
|
? a.asset.source.contractAddress
|
|
: undefined,
|
|
};
|
|
})
|
|
.sort((a, b) => {
|
|
// Put VEGA at the top of the list
|
|
if (a.isVega) {
|
|
return -1;
|
|
}
|
|
if (b.isVega) {
|
|
return 1;
|
|
}
|
|
// Secondary sort by name
|
|
if (a.name < b.name) {
|
|
return -1;
|
|
}
|
|
if (a.name > b.name) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
})
|
|
);
|
|
const delegatedNextEpoch = keyBy(
|
|
res.data.party?.delegations?.filter((d) => {
|
|
return d.epoch === Number(res.data.epoch.id) + 1;
|
|
}) || [],
|
|
'node.id'
|
|
);
|
|
const delegatedThisEpoch = keyBy(
|
|
res.data.party?.delegations?.filter((d) => {
|
|
return d.epoch === Number(res.data.epoch.id);
|
|
}) || [],
|
|
'node.id'
|
|
);
|
|
const nodesDelegated = uniq([
|
|
...Object.keys(delegatedNextEpoch),
|
|
...Object.keys(delegatedThisEpoch),
|
|
]);
|
|
|
|
const delegatedAmounts = nodesDelegated
|
|
.map((d) => ({
|
|
nodeId: d,
|
|
name:
|
|
delegatedThisEpoch[d]?.node?.name ||
|
|
delegatedNextEpoch[d]?.node?.name,
|
|
hasStakePending: !!(
|
|
(delegatedThisEpoch[d]?.amountFormatted ||
|
|
delegatedNextEpoch[d]?.amountFormatted) &&
|
|
delegatedThisEpoch[d]?.amountFormatted !==
|
|
delegatedNextEpoch[d]?.amountFormatted &&
|
|
delegatedNextEpoch[d] !== undefined
|
|
),
|
|
currentEpochStake:
|
|
delegatedThisEpoch[d] &&
|
|
new BigNumber(delegatedThisEpoch[d].amountFormatted),
|
|
nextEpochStake:
|
|
delegatedNextEpoch[d] &&
|
|
new BigNumber(delegatedNextEpoch[d].amountFormatted),
|
|
}))
|
|
.sort((a, b) => {
|
|
if (
|
|
new BigNumber(a.currentEpochStake || 0).isLessThan(
|
|
b.currentEpochStake || 0
|
|
)
|
|
) {
|
|
return 1;
|
|
}
|
|
if (
|
|
new BigNumber(a.currentEpochStake || 0).isGreaterThan(
|
|
b.currentEpochStake || 0
|
|
)
|
|
) {
|
|
return -1;
|
|
}
|
|
if ((!a.name && b.name) || a.name < b.name) return 1;
|
|
if ((!b.name && a.name) || a.name > b.name) return -1;
|
|
if (a.nodeId > b.nodeId) return 1;
|
|
if (a.nodeId < b.nodeId) return -1;
|
|
return 0;
|
|
});
|
|
|
|
setDelegatedNodes(delegatedAmounts);
|
|
})
|
|
.catch((err: Error) => {
|
|
Sentry.captureException(err);
|
|
// If query fails stop interval. Its almost certain that the query
|
|
// will just continue to fail
|
|
clearInterval(interval);
|
|
});
|
|
}, 1000);
|
|
}
|
|
|
|
return () => {
|
|
clearInterval(interval);
|
|
mounted = false;
|
|
};
|
|
}, [client, keypair?.pub, t, vegaToken.address]);
|
|
|
|
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
|
};
|