Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Fix delegation issues #1888

Merged
merged 1 commit into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading