Skip to content

Commit

Permalink
♻️ governance: change delegation component to use delegate registry
Browse files Browse the repository at this point in the history
  • Loading branch information
JuampiRombola committed Sep 27, 2023
1 parent 13980ac commit 59dcd7b
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 44 deletions.
69 changes: 37 additions & 32 deletions components/governance/Delegation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,53 @@
import React, { useMemo, useState } from 'react';
import React, { FC, useMemo, useState } from 'react';
import { Avatar, Box, Button, Collapse, Divider, Skeleton, TextField, Typography } from '@mui/material';
import { Trans, useTranslation } from 'react-i18next';
import { LoadingButton } from '@mui/lab';
import HowToVoteIcon from '@mui/icons-material/HowToVote';
import AddIcon from '@mui/icons-material/Add';
import { isAddress, parseEther, zeroAddress, formatEther } from 'viem';
import { isAddress, zeroAddress } from 'viem';
import { formatWallet } from 'utils/utils';
import { useEXA, useEXADelegates, useEXAPrepareDelegate } from 'hooks/useEXA';
import formatNumber from 'utils/formatNumber';
import { useEXA } from 'hooks/useEXA';
import useBalance from 'hooks/useBalance';
import { useWeb3 } from 'hooks/useWeb3';
import { useExaDelegate } from 'types/abi';
import { useDelegateRegistryClearDelegate, useDelegateRegistrySetDelegate } from 'types/abi';
import { mainnet, useEnsAvatar, useEnsName, useNetwork, useSwitchNetwork, useWaitForTransaction } from 'wagmi';
import * as blockies from 'blockies-ts';
import { useAirdropStreams } from 'hooks/useAirdrop';
import { useSablierV2LockupLinearGetWithdrawnAmount } from 'hooks/useSablier';
import { useDelegation, usePrepareClearDelegate, usePrepareDelegate } from 'hooks/useDelegateRegistry';
import formatNumber from 'utils/formatNumber';
import useGovernance from 'hooks/useGovernance';

type Props = {
amount: bigint;
fetchVotingPower: () => void;
};

const Delegation = ({ amount }: Props) => {
const Delegation: FC<Props> = ({ fetchVotingPower }) => {
const { votingPower } = useGovernance(false);
const { t } = useTranslation();
const { chain: displayNetwork, walletAddress, impersonateActive, exitImpersonate } = useWeb3();
const [selected, setSelected] = useState<'self-delegate' | 'add-delegate'>();
const [input, setInput] = useState<string>('');
const exa = useEXA();
const exaBalance = useBalance('EXA', exa?.address);
const { data: delegate, isLoading: isLoadingDelegate, refetch: refetchDelegate } = useEXADelegates();
const { config } = useEXAPrepareDelegate({
args:
delegate !== zeroAddress
? [zeroAddress]
: selected === 'add-delegate' && isAddress(input)
? [input]
: [walletAddress ?? zeroAddress],
});
const { write, isLoading: submitLoading, data } = useExaDelegate(config);
const { data: delegate, isLoading: isLoadingDelegate, refetch: refetchDelegate } = useDelegation();
const { config } = usePrepareDelegate(
delegate !== zeroAddress
? zeroAddress
: selected === 'add-delegate' && isAddress(input)
? input
: walletAddress ?? zeroAddress,
);
const { write, isLoading: submitLoading, data } = useDelegateRegistrySetDelegate(config);
const { config: configClearDelegate } = usePrepareClearDelegate();
const {
write: writeClearDelegate,
isLoading: clearDelegateLoading,
data: clearDelegateData,
} = useDelegateRegistryClearDelegate(configClearDelegate);
const { isLoading: waitingDelegate } = useWaitForTransaction({
hash: data?.hash,
hash: data?.hash ?? clearDelegateData?.hash,
onSettled: () => {
refetchDelegate();
fetchVotingPower();
},
});
const { data: delegateENS } = useEnsName({ address: delegate, chainId: mainnet.id });
Expand All @@ -52,13 +59,6 @@ const Delegation = ({ amount }: Props) => {
const { chain } = useNetwork();
const { switchNetwork, isLoading: switchIsLoading } = useSwitchNetwork();

const { data: stream } = useAirdropStreams();
const { data: withdrawn } = useSablierV2LockupLinearGetWithdrawnAmount(stream);

const totalVotes = useMemo(() => {
return formatNumber(formatEther(parseEther(exaBalance ?? '0') + (amount - (withdrawn ?? 0n))));
}, [exaBalance, amount, withdrawn]);

const delegateAvatar = useMemo(() => {
if (!delegate) return '';
if (delegateENSAvatar && !ensAvatarError) return delegateENSAvatar;
Expand All @@ -81,14 +81,14 @@ const Delegation = ({ amount }: Props) => {
<Divider flexItem />
<Box display="flex" flexDirection="column" gap={3}>
<Typography variant="h6">{t('Votes Delegation')}</Typography>
{exaBalance !== undefined ? (
{exaBalance !== undefined && votingPower !== undefined ? (
<Typography fontSize={14}>
<Trans
i18nKey="Your total of <1>{{amount}} voting power</1> is currently delegated to the following address:"
components={{
1: <strong></strong>,
}}
values={{ amount: totalVotes }}
values={{ amount: formatNumber(votingPower, 'USD', true) }}
/>
</Typography>
) : (
Expand All @@ -111,7 +111,12 @@ const Delegation = ({ amount }: Props) => {
{t('Please switch to {{network}} network', { network: displayNetwork.name })}
</LoadingButton>
) : (
<LoadingButton fullWidth variant="outlined" onClick={write} loading={submitLoading || waitingDelegate}>
<LoadingButton
fullWidth
variant="outlined"
onClick={writeClearDelegate}
loading={clearDelegateLoading || waitingDelegate}
>
{t('Revoke delegation')}
</LoadingButton>
)}
Expand All @@ -124,14 +129,14 @@ const Delegation = ({ amount }: Props) => {
<Divider flexItem />
<Box display="flex" flexDirection="column" gap={3}>
<Typography variant="h6">{t('Votes Delegation')}</Typography>
{exaBalance !== undefined ? (
{exaBalance !== undefined && votingPower !== undefined ? (
<Typography fontSize={14}>
<Trans
i18nKey="You have a total of <1>{{amount}} voting power</1> available to delegate."
components={{
1: <strong></strong>,
}}
values={{ amount: totalVotes }}
values={{ amount: formatNumber(votingPower, 'USD', true) }}
/>
</Typography>
) : (
Expand Down
2 changes: 1 addition & 1 deletion components/governance/VotingPower/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const VotingPower: FC<Props> = ({ votingPower }) => {
<Skeleton width={56} height={40} />
) : (
<Typography fontSize={28} color="grey.700">
{formatNumber(votingPower)}
{formatNumber(votingPower, 'USD', true)}
</Typography>
)}
</Box>
Expand Down
48 changes: 48 additions & 0 deletions hooks/useDelegateRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Address, stringToHex, zeroAddress } from 'viem';
import {
useDelegateRegistryDelegation,
usePrepareDelegateRegistryClearDelegate,
usePrepareDelegateRegistrySetDelegate,
} from 'types/abi';
import { useWeb3 } from './useWeb3';
import { useMemo } from 'react';

const DELEGATE_REGISTRY_ADDRESS = '0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446';

export const useDelegation = () => {
const { chain, walletAddress } = useWeb3();
const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]);
const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]);

return useDelegateRegistryDelegation({
chainId: chain.id,
address: DELEGATE_REGISTRY_ADDRESS,
args: [walletAddress ?? zeroAddress, encodedSpace],
});
};

export const usePrepareDelegate = (address: Address) => {
const { chain, walletAddress } = useWeb3();
const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]);
const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]);

return usePrepareDelegateRegistrySetDelegate({
chainId: chain.id,
address: DELEGATE_REGISTRY_ADDRESS,
account: walletAddress ?? zeroAddress,
args: [encodedSpace, address],
});
};

export const usePrepareClearDelegate = () => {
const { chain, walletAddress } = useWeb3();
const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]);
const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]);

return usePrepareDelegateRegistryClearDelegate({
chainId: chain.id,
address: DELEGATE_REGISTRY_ADDRESS,
account: walletAddress ?? zeroAddress,
args: [encodedSpace],
});
};
30 changes: 21 additions & 9 deletions hooks/useGovernance.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import snapshot from '@snapshot-labs/snapshot.js';
import { useWeb3 } from './useWeb3';
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import optimismEXA from '@exactly/protocol/deployments/optimism/EXA.json';
import goerliEXA from '@exactly/protocol/deployments/goerli/EXA.json';

export default function useGovernance() {
export default function useGovernance(delegation = true) {
const [votingPower, setVotingPower] = useState<number | undefined>(undefined);
const { chain, walletAddress } = useWeb3();

Expand Down Expand Up @@ -34,16 +34,28 @@ export default function useGovernance() {
[exaAddress],
);

const delegation = true;
const url = 'https://score.snapshot.org/';

useEffect(() => {
const fetchVotingPower = useCallback(async () => {
if (!walletAddress) return;
const { vp } = await snapshot.utils.getVp(
walletAddress,
String(chain.id),
strategies,
'latest',
space,
delegation,
{
url,
},
);
setVotingPower(vp);
return vp;
}, [chain.id, delegation, space, strategies, walletAddress]);

snapshot.utils
.getVp(walletAddress, String(chain.id), strategies, 'latest', space, delegation, { url })
.then(({ vp }) => setVotingPower(vp));
}, [chain.id, chain.network, delegation, space, strategies, url, votingPower, walletAddress]);
useEffect(() => {
fetchVotingPower();
}, [fetchVotingPower]);

return { votingPower };
return { votingPower, fetchVotingPower };
}
4 changes: 2 additions & 2 deletions pages/governance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import useMerkleTree from 'hooks/useMerkleTree';
const Governance: NextPage = () => {
const { t } = useTranslation();
const { isConnected, walletAddress, impersonateActive } = useWeb3();
const { votingPower } = useGovernance();
const { votingPower, fetchVotingPower } = useGovernance();
const mTree = useMerkleTree(walletAddress);

usePageView('/governance', 'Governance');
Expand Down Expand Up @@ -47,7 +47,7 @@ const Governance: NextPage = () => {
>
{mTree.canClaim && <Claimable amount={mTree.amount} proof={mTree.proof} />}
<VotingPower votingPower={votingPower} />
<Delegation amount={mTree.canClaim ? mTree.amount : 0n} />
<Delegation fetchVotingPower={fetchVotingPower} />
<Proposals />
</Box>
) : (
Expand Down

0 comments on commit 59dcd7b

Please sign in to comment.