Skip to content

Commit

Permalink
feat: Fix delegation issues (#1888)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaquinBattilana authored Dec 31, 2023
1 parent cb7ee1c commit 111ef6d
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 58 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"test:coverage": "jest --coverage"
},
"dependencies": {
"@aave/contract-helpers": "1.21.2-2022bac22871a85776f017f1d6214d9ecfcc5abe.0",
"@aave/contract-helpers": "1.21.2-a36249a4280cf8f987643baeec8c685814f5fb2b.0",
"@aave/math-utils": "1.21.2-2022bac22871a85776f017f1d6214d9ecfcc5abe.0",
"@bgd-labs/aave-address-book": "^2.13.1",
"@emotion/cache": "11.10.3",
Expand Down
30 changes: 30 additions & 0 deletions src/architecture/FixedPointDecimal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { BigNumber, BigNumberish } from 'ethers';
import { formatUnits } from 'ethers/lib/utils';

export class FixedPointDecimal {
private readonly _value: BigNumber;

constructor(_value: BigNumberish, private readonly _decimals: number) {
this._value = BigNumber.from(_value);
}

get value() {
return this._value;
}

get decimals() {
return this._decimals;
}

add(value: FixedPointDecimal) {
return new FixedPointDecimal(this._value.add(value._value), this._decimals);
}

eq(value: FixedPointDecimal) {
return this._value.eq(value._value);
}

format() {
return formatUnits(this._value, this._decimals);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { canBeEnsAddress } from '@aave/contract-helpers';
import { t, Trans } from '@lingui/macro';
import { FormControl, TextField, Typography } from '@mui/material';
import { utils } from 'ethers';
import { constants, utils } from 'ethers';
import { parseUnits } from 'ethers/lib/utils';
import { useEffect, useState } from 'react';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
import { GovernancePowerTypeApp } from 'src/helpers/types';
import { useGovernanceDelegatees } from 'src/hooks/governance/useDelegateeData';
import { useGovernanceTokens } from 'src/hooks/governance/useGovernanceTokens';
import { usePowers } from 'src/hooks/governance/usePowers';
import { ModalType, useModalContext } from 'src/hooks/useModal';
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { useRootStore } from 'src/store/root';
Expand Down Expand Up @@ -46,11 +46,10 @@ export const GovDelegationModalContent: React.FC<GovDelegationModalContentProps>
const { gasLimit, mainTxState: txState, txError } = useModalContext();
const currentNetworkConfig = useRootStore((store) => store.currentNetworkConfig);
const currentChainId = useRootStore((store) => store.currentChainId);
const currentMarketData = useRootStore((store) => store.currentMarketData);
const {
data: { aave, stkAave, aAave },
} = useGovernanceTokens();
const { data: powers, refetch } = usePowers(currentMarketData);
const { data: powers, refetch } = useGovernanceDelegatees();
// error states

// selector states
Expand All @@ -63,12 +62,17 @@ export const GovDelegationModalContent: React.FC<GovDelegationModalContentProps>
const onlyOnePowerToRevoke =
isRevokeModal &&
!!powers &&
((powers.aaveVotingDelegatee === '' && powers.stkAaveVotingDelegatee === '') ||
(powers.aavePropositionDelegatee === '' && powers.stkAavePropositionDelegatee === ''));
((powers.aaveVotingDelegatee === constants.AddressZero &&
powers.stkAaveVotingDelegatee === constants.AddressZero) ||
(powers.aavePropositionDelegatee === constants.AddressZero &&
powers.stkAavePropositionDelegatee === constants.AddressZero));

useEffect(() => {
if (onlyOnePowerToRevoke) {
if (powers.aaveVotingDelegatee === '' && powers.stkAaveVotingDelegatee === '')
if (
powers.aaveVotingDelegatee === constants.AddressZero &&
powers.stkAaveVotingDelegatee === constants.AddressZero
)
setDelegationType(GovernancePowerTypeApp.PROPOSITION);
else setDelegationType(GovernancePowerTypeApp.VOTING);
}
Expand Down Expand Up @@ -154,9 +158,10 @@ export const GovDelegationModalContent: React.FC<GovDelegationModalContentProps>
)}
{(isRevokeModal &&
!!powers &&
((powers.aaveVotingDelegatee === '' && powers.stkAaveVotingDelegatee === '') ||
(powers.aavePropositionDelegatee === '' &&
powers.stkAavePropositionDelegatee === ''))) || (
((powers.aaveVotingDelegatee === constants.AddressZero &&
powers.stkAaveVotingDelegatee === constants.AddressZero) ||
(powers.aavePropositionDelegatee === constants.AddressZero &&
powers.stkAavePropositionDelegatee === constants.AddressZero))) || (
<>
<Typography variant="description" color="text.secondary" sx={{ mb: 1 }}>
<Trans>{isRevokeModal ? 'Power to revoke' : 'Power to delegate'}</Trans>
Expand Down
3 changes: 1 addition & 2 deletions src/components/transactions/GovVote/GovVoteActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ export const GovVoteActions = ({
const user = useRootStore((store) => store.account);
const estimateGasLimit = useRootStore((store) => store.estimateGasLimit);
const { sendTx, signTxData } = useWeb3Context();
const currentMarketData = useRootStore((store) => store.currentMarketData);
const tokenPowers = useGovernanceTokensAndPowers(currentMarketData);
const tokenPowers = useGovernanceTokensAndPowers();
const [signature, setSignature] = useState<string | undefined>(undefined);
const proposalId = proposal.proposal.proposalId;
const blockHash = proposal.proposalData.proposalData.snapshotBlockHash;
Expand Down
50 changes: 50 additions & 0 deletions src/hooks/governance/useDelegateeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useQueries } from '@tanstack/react-query';
import { useRootStore } from 'src/store/root';
import { governanceV3Config } from 'src/ui-config/governanceConfig';
import { queryKeysFactory } from 'src/ui-config/queries';
import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider';

export const useTokenDelegatees = (tokens: string[]) => {
const { delegationTokenService } = useSharedDependencies();
const user = useRootStore((store) => store.account);
return useQueries({
queries: tokens.map((token) => ({
queryFn: () =>
delegationTokenService.getTokenDelegatees(user, token, governanceV3Config.coreChainId),
queryKey: queryKeysFactory.tokenDelegatees(user, token, governanceV3Config.coreChainId),
enabled: !!user,
})),
});
};

export const useGovernanceDelegatees = () => {
const queries = useTokenDelegatees(Object.values(governanceV3Config.votingAssets));
const isLoading = queries.some((elem) => elem.isLoading);
const error = queries.find((elem) => elem.error)?.error;
const refetch = () => queries.forEach((elem) => elem.refetch());
const allData = queries.reduce((acum, elem) => {
if (elem.data) {
return acum.concat([elem.data]);
}
return acum;
}, [] as { votingDelegatee: string; propositionDelegatee: string }[]);
if (allData.length !== 3) {
return {
data: undefined,
isLoading,
error,
refetch,
};
}
return {
data: {
aaveVotingDelegatee: allData[0].votingDelegatee,
aavePropositionDelegatee: allData[0].propositionDelegatee,
aAaveVotingDelegatee: allData[1].votingDelegatee,
aAavePropositionDelegatee: allData[1].propositionDelegatee,
stkAaveVotingDelegatee: allData[2].votingDelegatee,
stkAavePropositionDelegatee: allData[2].propositionDelegatee,
},
refetch,
};
};
7 changes: 2 additions & 5 deletions src/hooks/governance/useGovernanceTokensAndPowers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { QueryObserverResult, RefetchOptions, RefetchQueryFilters } from '@tanstack/react-query';
import { Powers } from 'src/services/GovernanceService';
import { GovernanceTokensBalance } from 'src/services/WalletBalanceService';
import { MarketDataType } from 'src/ui-config/marketsConfig';

import { useGovernanceTokens } from './useGovernanceTokens';
import { usePowers } from './usePowers';
Expand All @@ -15,10 +14,8 @@ interface GovernanceTokensAndPowers extends Powers, GovernanceTokensBalance {
) => Promise<QueryObserverResult<Powers, unknown>>;
}

export const useGovernanceTokensAndPowers = (
marketData: MarketDataType
): GovernanceTokensAndPowers | undefined => {
const { data: powers, refetch: refetchPowers } = usePowers(marketData);
export const useGovernanceTokensAndPowers = (): GovernanceTokensAndPowers | undefined => {
const { data: powers, refetch: refetchPowers } = usePowers();
const { data: governanceTokens } = useGovernanceTokens();

if (!powers || !governanceTokens) {
Expand Down
5 changes: 2 additions & 3 deletions src/hooks/governance/usePowers.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import { useRootStore } from 'src/store/root';
import { governanceV3Config } from 'src/ui-config/governanceConfig';
import { MarketDataType } from 'src/ui-config/marketsConfig';
import { POLLING_INTERVAL, queryKeysFactory } from 'src/ui-config/queries';
import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider';

export const usePowers = (marketData: MarketDataType) => {
export const usePowers = () => {
const { governanceService } = useSharedDependencies();
const user = useRootStore((store) => store.account);
return useQuery({
queryFn: () => governanceService.getPowers(governanceV3Config.coreChainId, user),
queryKey: queryKeysFactory.powers(user, marketData),
queryKey: queryKeysFactory.powers(user, governanceV3Config.coreChainId),
enabled: !!user,
refetchInterval: POLLING_INTERVAL,
});
Expand Down
53 changes: 53 additions & 0 deletions src/hooks/governance/useTokensPower.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useQueries } from '@tanstack/react-query';
import { FixedPointDecimal } from 'src/architecture/FixedPointDecimal';
import { TokenDelegationPower } from 'src/services/DelegationTokenService';
import { useRootStore } from 'src/store/root';
import { governanceV3Config } from 'src/ui-config/governanceConfig';
import { queryKeysFactory } from 'src/ui-config/queries';
import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider';

export const useTokensPowers = (tokens: string[]) => {
const { delegationTokenService } = useSharedDependencies();
const user = useRootStore((store) => store.account);
return useQueries({
queries: tokens.map((token) => ({
queryFn: () =>
delegationTokenService.getTokenPowers(user, token, governanceV3Config.coreChainId),
queryKey: queryKeysFactory.tokenPowers(user, token, governanceV3Config.coreChainId),
enabled: !!user,
})),
});
};

export const useTotalTokensPowers = (tokens: string[]) => {
const queries = useTokensPowers(tokens);
const isLoading = queries.some((elem) => elem.isLoading);
const error = queries.find((elem) => elem.error)?.error;
const allData = queries.reduce((acum, elem) => {
if (elem.data) {
return acum.concat([elem.data]);
}
return acum;
}, [] as TokenDelegationPower[]);
if (allData.length !== tokens.length) {
return {
data: undefined,
isLoading,
error,
};
}
return {
data: allData.reduce(
(acum, elem) => {
return {
propositionPower: acum.propositionPower.add(elem.propositionPower),
votingPower: acum.votingPower.add(elem.votingPower),
};
},
{
propositionPower: new FixedPointDecimal(0, 18),
votingPower: new FixedPointDecimal(0, 18),
}
),
};
};
40 changes: 18 additions & 22 deletions src/modules/governance/DelegatedInfoPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { Trans } from '@lingui/macro';
import { Button, Divider, Paper, Typography } from '@mui/material';
import { Box } from '@mui/system';
import { constants } from 'ethers';
import { AvatarSize } from 'src/components/Avatar';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
import { Link } from 'src/components/primitives/Link';
import { Row } from 'src/components/primitives/Row';
import { TokenIcon } from 'src/components/primitives/TokenIcon';
import { ExternalUserDisplay } from 'src/components/UserDisplay';
import { useGovernanceDelegatees } from 'src/hooks/governance/useDelegateeData';
import { useGovernanceTokens } from 'src/hooks/governance/useGovernanceTokens';
import { usePowers } from 'src/hooks/governance/usePowers';
import { useModalContext } from 'src/hooks/useModal';
import { ZERO_ADDRESS } from 'src/modules/governance/utils/formatProposal';
import { useRootStore } from 'src/store/root';
import { GENERAL } from 'src/utils/mixPanelEvents';

type DelegatedPowerProps = {
user: string;
aavePower: string;
stkAavePower: string;
aaveDelegatee: string;
Expand All @@ -26,7 +26,6 @@ type DelegatedPowerProps = {
};

const DelegatedPower: React.FC<DelegatedPowerProps> = ({
user,
aavePower,
stkAavePower,
aaveDelegatee,
Expand All @@ -35,9 +34,9 @@ const DelegatedPower: React.FC<DelegatedPowerProps> = ({
aAavePower,
title,
}) => {
const isAaveSelfDelegated = !aaveDelegatee || user === aaveDelegatee;
const isStkAaveSelfDelegated = !stkAaveDelegatee || user === stkAaveDelegatee;
const isAAaveSelfDelegated = !aAaveDelegatee || user === aAaveDelegatee;
const isAaveSelfDelegated = !aaveDelegatee || constants.AddressZero === aaveDelegatee;
const isStkAaveSelfDelegated = !stkAaveDelegatee || constants.AddressZero === stkAaveDelegatee;
const isAAaveSelfDelegated = !aAaveDelegatee || constants.AddressZero === aAaveDelegatee;

if (isAaveSelfDelegated && isStkAaveSelfDelegated && isAAaveSelfDelegated) return null;

Expand Down Expand Up @@ -133,11 +132,10 @@ const DelegatedPower: React.FC<DelegatedPowerProps> = ({

export const DelegatedInfoPanel = () => {
const address = useRootStore((store) => store.account);
const currentMarketData = useRootStore((store) => store.currentMarketData);
const {
data: { aave, stkAave, aAave },
} = useGovernanceTokens();
const { data: powers } = usePowers(currentMarketData);
const { data: powers } = useGovernanceDelegatees();
const { openGovDelegation, openRevokeGovDelegation } = useModalContext();
const trackEvent = useRootStore((store) => store.trackEvent);

Expand All @@ -147,20 +145,20 @@ export const DelegatedInfoPanel = () => {
Number(aave) <= 0 &&
Number(stkAave) <= 0 &&
Number(aAave) <= 0 &&
powers.aavePropositionDelegatee === '' &&
powers.aaveVotingDelegatee === '' &&
powers.stkAavePropositionDelegatee === '' &&
powers.stkAaveVotingDelegatee === '' &&
powers.aAavePropositionDelegatee === '' &&
powers.aAaveVotingDelegatee === '';
powers.aavePropositionDelegatee === constants.AddressZero &&
powers.aaveVotingDelegatee === constants.AddressZero &&
powers.stkAavePropositionDelegatee === constants.AddressZero &&
powers.stkAaveVotingDelegatee === constants.AddressZero &&
powers.aAavePropositionDelegatee === constants.AddressZero &&
powers.aAaveVotingDelegatee === constants.AddressZero;

const showRevokeButton =
powers.aavePropositionDelegatee !== '' ||
powers.aaveVotingDelegatee !== '' ||
powers.stkAavePropositionDelegatee !== '' ||
powers.stkAaveVotingDelegatee !== '' ||
powers.aAaveVotingDelegatee !== '' ||
powers.aAavePropositionDelegatee !== '';
powers.aavePropositionDelegatee !== constants.AddressZero ||
powers.aaveVotingDelegatee !== constants.AddressZero ||
powers.stkAavePropositionDelegatee !== constants.AddressZero ||
powers.stkAaveVotingDelegatee !== constants.AddressZero ||
powers.aAaveVotingDelegatee !== constants.AddressZero ||
powers.aAavePropositionDelegatee !== constants.AddressZero;

return (
<Paper sx={{ mt: 2 }}>
Expand Down Expand Up @@ -198,7 +196,6 @@ export const DelegatedInfoPanel = () => {
aAaveDelegatee={powers.aAaveVotingDelegatee}
aaveDelegatee={powers.aaveVotingDelegatee}
stkAaveDelegatee={powers.stkAaveVotingDelegatee}
user={address}
title="Voting power"
/>
<DelegatedPower
Expand All @@ -208,7 +205,6 @@ export const DelegatedInfoPanel = () => {
stkAavePower={stkAave}
aaveDelegatee={powers.aavePropositionDelegatee}
stkAaveDelegatee={powers.stkAavePropositionDelegatee}
user={address}
title="Proposition power"
/>
</>
Expand Down
Loading

2 comments on commit 111ef6d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit was deployed on ipfs

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit was deployed on ipfs

Please sign in to comment.