vega-frontend-monorepo/apps/token/src/components/vega-wallet/hooks.ts
Matthew Russell b8d5680011
chore: set curly eslint rule to multi line (#1322)
* chore: set curly eslint rule to multi line

* chore: fix lint

* chore: make eslint rule error and fix resulting errors
2022-09-16 17:04:32 +01:00

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 };
};