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

[ENG-115] Fix getting / showing voting weight on NFT DAO proposals #2661

Merged
merged 33 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2d362fb
remove network switching useEffect from establish essentials form
Da-Colon Dec 20, 2024
96b63e3
control network switching in DAOController
Da-Colon Dec 20, 2024
749d670
Leverage Viem's decodeFunctionData when Safe Decoder fails
mudrila Jan 6, 2025
2be56f6
proxy -> implementationAddress
mudrila Jan 6, 2025
5b85b67
Merge pull request #2645 from decentdao/eng-50/dao-creation-network-s…
Da-Colon Jan 6, 2025
08d9864
Merge branch 'develop' into fix/decode-transactions-with-abi-decoding
mudrila Jan 6, 2025
de9e0b1
Replace hero image with new asset
adamgall Jan 6, 2025
6952604
Merge branch 'release/v0.4.0' into develop
adamgall Jan 6, 2025
4ddba5e
Merge pull request #2649 from decentdao/fix/decode-transactions-with-…
mudrila Jan 6, 2025
e5d70f8
Merge branch 'develop' into ENG-76_hero-image
mudrila Jan 7, 2025
0d03e9d
Merge pull request #2650 from decentdao/ENG-76_hero-image
mudrila Jan 7, 2025
21a3a00
Fix misaligned toast loading icon
DarksightKellar Jan 7, 2025
ae42984
Have strategyContract use abis.LinearERC721Voting if strategyType is …
DarksightKellar Jan 7, 2025
c93fa5f
Remove termed roles feature flag
DarksightKellar Jan 7, 2025
5a04004
Move undefined checks higher
DarksightKellar Jan 7, 2025
1f083d1
Merge pull request #2652 from decentdao/eng-93-fix-proposal-load
mudrila Jan 7, 2025
d4fd33b
Merge pull request #2651 from decentdao/eng-5-misaligned-toast-loadin…
mudrila Jan 7, 2025
09aee92
Merge branch 'hotfix/v0.4.1' into develop
adamgall Jan 8, 2025
9d9c804
Refactor loading toast for failed DAO index to remove unnecessary dur…
Da-Colon Jan 8, 2025
c8a30dd
Merge pull request #2657 from decentdao/eng/84-toast-not-closing
mudrila Jan 9, 2025
204861a
Fix ugly line under search box
DarksightKellar Jan 10, 2025
0103754
Fix loadProposalVotingWeight - it was fetching the data correctly onl…
mudrila Jan 10, 2025
62f08ab
Merge pull request #2660 from decentdao/eng-99-fix-box-shadow
mudrila Jan 10, 2025
900f04c
Merge branch 'develop' into fix/content-is-too-large
mudrila Jan 10, 2025
717ee41
Formatting
mudrila Jan 10, 2025
04c19e3
Little bit of debugging
mudrila Jan 11, 2025
e8c3b2d
Welp, try running this thing multiple times
mudrila Jan 11, 2025
b225136
More logging
mudrila Jan 11, 2025
c4a8304
And more logging
mudrila Jan 11, 2025
6f038ee
And more logging
mudrila Jan 11, 2025
769effe
Formatting
mudrila Jan 11, 2025
cba1f59
More detailed logging
mudrila Jan 11, 2025
c938649
Remove all logs
mudrila Jan 11, 2025
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
Binary file modified public/images/hero_image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/css/Toast.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
margin-right: 0;
}

.sonner-toast[data-type='loading'] [data-icon] {
margin-top: 8px;
}

.sonner-toast [data-icon] svg {
width: 24px;
height: 24px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,6 @@ export function EstablishEssentials(props: ICreationStepProps) {
},
});

useEffect(() => {
if (chain.id !== walletChainID) {
const chainId = getChainIdFromPrefix(addressPrefix);
switchChain({ chainId });
}
}, [chain.id, walletChainID, addressPrefix, switchChain]);

return (
<>
<StepWrapper
Expand Down
20 changes: 20 additions & 0 deletions src/components/DaoCreator/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Box } from '@chakra-ui/react';
import { Formik } from 'formik';
import { useEffect } from 'react';
import { useChainId, useSwitchChain } from 'wagmi';
import { useDAOCreateSchema } from '../../hooks/schemas/DAOCreate/useDAOCreateSchema';
import { useNetworkConfigStore } from '../../providers/NetworkConfig/useNetworkConfigStore';
import {
AzoriusERC20DAO,
AzoriusERC721DAO,
Expand Down Expand Up @@ -28,6 +31,8 @@ function DaoCreator({
mode: DAOCreateMode;
}) {
const { totalParentVotingWeight } = useParentSafeVotingWeight();
const walletChainID = useChainId();
const { chain } = useNetworkConfigStore();

const { createDAOValidation } = useDAOCreateSchema({
isSubDAO: !!isSubDAO,
Expand All @@ -37,6 +42,21 @@ function DaoCreator({
const { prepareMultisigFormData, prepareAzoriusERC20FormData, prepareAzoriusERC721FormData } =
usePrepareFormData();

const { switchChain } = useSwitchChain({
mutation: {
onError: () => {
if (chain.id !== walletChainID && !isSubDAO) {
switchChain({ chainId: chain.id });
}
},
},
});
useEffect(() => {
if (chain.id !== walletChainID && !isSubDAO) {
switchChain({ chainId: chain.id });
}
}, [chain.id, walletChainID, switchChain, isSubDAO]);

return (
<Box>
<Formik<CreatorFormState>
Expand Down
74 changes: 56 additions & 18 deletions src/components/Proposals/ProposalSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { abis } from '@fractal-framework/fractal-contracts';
import { format } from 'date-fns';
import { formatInTimeZone } from 'date-fns-tz';
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getContract } from 'viem';
import { erc721Abi, getContract } from 'viem';
import { useAccount, usePublicClient } from 'wagmi';
import { TOOLTIP_MAXW } from '../../constants/common';
import useBlockTimestamp from '../../hooks/utils/useBlockTimestamp';
Expand Down Expand Up @@ -60,31 +60,69 @@ export function AzoriusProposalSummary({ proposal }: { proposal: AzoriusProposal

const publicClient = usePublicClient();

const getErc721VotingWeight = useCallback(async () => {
if (!address || !azoriusGovernance.erc721Tokens || !publicClient) {
return 0n;
}
const userVotingWeight = (
await Promise.all(
azoriusGovernance.erc721Tokens.map(async ({ address: tokenAddress, votingWeight }) => {
const tokenContract = getContract({
abi: erc721Abi,
address: tokenAddress,
client: publicClient,
});
// @todo We should be checking proposal state - if it's active, we should use the latest block, otherwise we should calculate the voting weight based on the startBlock and deadlineMs
const userBalance = await tokenContract.read.balanceOf([address], {
blockNumber: startBlock,
});
return userBalance * votingWeight;
}),
)
).reduce((prev, curr) => prev + curr, 0n);
return userVotingWeight;
}, [azoriusGovernance.erc721Tokens, publicClient, address, startBlock]);

const isERC20 = type === GovernanceType.AZORIUS_ERC20;
mudrila marked this conversation as resolved.
Show resolved Hide resolved
const isERC721 = type === GovernanceType.AZORIUS_ERC721;

useEffect(() => {
async function loadProposalVotingWeight() {
if (address && publicClient) {
const strategyContract = getContract({
abi: abis.LinearERC20Voting,
address: proposal.votingStrategy,
client: publicClient,
});
if (isERC20) {
const strategyContract = getContract({
abi: abis.LinearERC20Voting,
address: proposal.votingStrategy,
client: publicClient,
});

const pastVotingWeight = await strategyContract.read.getVotingWeight([
address,
Number(proposal.proposalId),
]);
const pastVotingWeight = await strategyContract.read.getVotingWeight([
address,
Number(proposal.proposalId),
]);

setProposalVotingWeight(
formatCoin(pastVotingWeight, true, votesToken?.decimals, undefined, false),
);
setProposalVotingWeight(
formatCoin(pastVotingWeight, true, votesToken?.decimals, undefined, false),
);
} else if (isERC721) {
const votingWeight = await getErc721VotingWeight();
setProposalVotingWeight(votingWeight.toString());
}
}
}

loadProposalVotingWeight();
}, [address, proposal.proposalId, proposal.votingStrategy, publicClient, votesToken?.decimals]);

const isERC20 = type === GovernanceType.AZORIUS_ERC20;
const isERC721 = type === GovernanceType.AZORIUS_ERC721;
}, [
address,
proposal.proposalId,
proposal.votingStrategy,
publicClient,
votesToken?.decimals,
isERC20,
isERC721,
getErc721VotingWeight,
startBlock,
]);

// @todo @dev (see below):
// Caching has introduced a new "problem" edge case -- a proposal can be loaded before `votingStrategy` is loaded.
Expand Down
84 changes: 40 additions & 44 deletions src/components/Proposals/ProposalVotes/context/VoteContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
useCallback,
useContext,
useEffect,
useRef,
useMemo,
useState,
} from 'react';
import { erc721Abi, getContract } from 'viem';
import { BlockTag, erc721Abi, getContract } from 'viem';
import { useAccount, usePublicClient } from 'wagmi';
import useSnapshotProposal from '../../../../hooks/DAO/loaders/snapshot/useSnapshotProposal';
import useUserERC721VotingTokens from '../../../../hooks/DAO/proposal/useUserERC721VotingTokens';
Expand Down Expand Up @@ -98,27 +98,30 @@ export function VoteContextProvider({
extendedSnapshotProposal,
]);

const erc721VotingWeight = useCallback(async () => {
const account = userAccount.address;
const azoriusGovernance = governance as AzoriusGovernance;
if (!account || !azoriusGovernance.erc721Tokens || !publicClient) {
return 0n;
}
const userVotingWeight = (
await Promise.all(
azoriusGovernance.erc721Tokens.map(async ({ address, votingWeight }) => {
const tokenContract = getContract({
abi: erc721Abi,
address: address,
client: publicClient,
});
const userBalance = await tokenContract.read.balanceOf([account]);
return userBalance * votingWeight;
}),
)
).reduce((prev, curr) => prev + curr, 0n);
return userVotingWeight;
}, [governance, publicClient, userAccount.address]);
const getErc721VotingWeight = useCallback(
async (blockTag?: BlockTag) => {
const account = userAccount.address;
const azoriusGovernance = governance as AzoriusGovernance;
if (!account || !azoriusGovernance.erc721Tokens || !publicClient) {
return 0n;
}
const userVotingWeight = (
await Promise.all(
azoriusGovernance.erc721Tokens.map(async ({ address, votingWeight }) => {
const tokenContract = getContract({
abi: erc721Abi,
address: address,
client: publicClient,
});
const userBalance = await tokenContract.read.balanceOf([account], { blockTag });
return userBalance * votingWeight;
}),
)
).reduce((prev, curr) => prev + curr, 0n);
return userVotingWeight;
},
[governance, publicClient, userAccount.address],
);

const getCanVote = useCallback(async () => {
setCanVoteLoading(true);
Expand All @@ -140,15 +143,14 @@ export function VoteContextProvider({
Number(proposal.proposalId),
])) > 0n && !hasVoted;
} else if (governance.type === GovernanceType.AZORIUS_ERC721) {
const votingWeight = await erc721VotingWeight();
const votingWeight = await getErc721VotingWeight();
newCanVote = votingWeight > 0n && remainingTokenIds.length > 0;
} else if (governance.type === GovernanceType.MULTISIG) {
newCanVote = !!safe?.owners.includes(userAccount.address);
} else {
newCanVote = false;
}
}

if (canVote !== newCanVote) {
setCanVote(newCanVote);
}
Expand All @@ -164,15 +166,10 @@ export function VoteContextProvider({
remainingTokenIds.length,
safe?.owners,
proposal,
erc721VotingWeight,
getErc721VotingWeight,
]);

const initialLoadRef = useRef(false);
useEffect(() => {
// Prevent running this effect multiple times
if (initialLoadRef.current) return;
initialLoadRef.current = true;

getCanVote();
getHasVoted();
}, [getCanVote, getHasVoted]);
Expand All @@ -184,18 +181,17 @@ export function VoteContextProvider({
}
}, [proposal, proposalVotesLength]);

return (
<VoteContext.Provider
value={{
canVote,
canVoteLoading,
hasVoted,
hasVotedLoading,
getHasVoted,
getCanVote,
}}
>
{children}
</VoteContext.Provider>
const voteContextValue = useMemo(
() => ({
canVote,
canVoteLoading,
hasVoted,
hasVotedLoading,
getHasVoted,
getCanVote,
}),
[canVote, canVoteLoading, hasVoted, hasVotedLoading, getHasVoted, getCanVote],
);

return <VoteContext.Provider value={voteContextValue}>{children}</VoteContext.Provider>;
}
2 changes: 1 addition & 1 deletion src/components/ui/menus/DAOSearch/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export function DAOSearch() {
marginTop="0.25rem"
rounded="0.5rem"
bg="neutral-2"
boxShadow={SEXY_BOX_SHADOW_T_T}
boxShadow={resolvedAddressesWithPrefix.length ? SEXY_BOX_SHADOW_T_T : 'none'}
hidden={!showResults}
w="full"
position="absolute"
Expand Down
2 changes: 0 additions & 2 deletions src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export const MAX_CONTENT_WIDTH = '80rem';

const features = {
developmentMode: 'DEVELOPMENT_MODE',
termedRoles: 'TERMED_ROLES',
demoMode: 'DEMO_MODE',
} as const;

Expand All @@ -72,7 +71,6 @@ export const isFeatureEnabled = (feature: FeatureFlag) => {
};

export const isDevMode = () => isFeatureEnabled(features.developmentMode);
export const isTermedRolesEnabled = () => isFeatureEnabled(features.termedRoles);
export const isDemoMode = () => isFeatureEnabled(features.demoMode);

/**
Expand Down
14 changes: 7 additions & 7 deletions src/hooks/DAO/loaders/governance/useAzoriusProposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ export const useAzoriusProposals = () => {

let proposalData;

if (
proposalCreatedEvent.args.proposer === undefined ||
proposalCreatedEvent.args.strategy === undefined
) {
continue;
}

if (proposalCreatedEvent.args.metadata && proposalCreatedEvent.args.transactions) {
const metadataEvent = parseProposalMetadata(proposalCreatedEvent.args.metadata);

Expand Down Expand Up @@ -273,13 +280,6 @@ export const useAzoriusProposals = () => {
}
}

if (
proposalCreatedEvent.args.proposer === undefined ||
proposalCreatedEvent.args.strategy === undefined
) {
continue;
}

let strategyType: VotingStrategyType | undefined;
const strategyAddress = proposalCreatedEvent.args.strategy;
const {
Expand Down
3 changes: 0 additions & 3 deletions src/hooks/DAO/proposal/useUserERC721VotingTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ export default function useUserERC721VotingTokens(

const tokenIdsSets = [...userERC721Tokens.values()];
const tokenAddressesKeys = [...userERC721Tokens.keys()];

await Promise.all(
// Same here
tokenIdsSets.map(async (tokenIdsSet, setIndex) => {
Expand Down Expand Up @@ -204,7 +203,6 @@ export default function useUserERC721VotingTokens(
);
}),
);

return {
totalVotingTokenAddresses: totalTokenAddresses,
totalVotingTokenIds: totalTokenIds,
Expand All @@ -226,7 +224,6 @@ export default function useUserERC721VotingTokens(
const loadUserERC721VotingTokens = useCallback(async () => {
const proposalIdNum = proposalId === null ? null : Number(proposalId);
const tokensInfo = await getUserERC721VotingTokens(safeAddress, proposalIdNum);

if (tokensInfo) {
setTotalVotingTokenAddresses(tokensInfo.totalVotingTokenAddresses);
setTotalVotingTokenIds(tokensInfo.totalVotingTokenIds);
Expand Down
4 changes: 1 addition & 3 deletions src/pages/create/SafeCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ export function SafeCreatePage() {
if (daoFound) {
navigate(DAO_ROUTES.dao.relative(addressPrefix, safeAddress));
} else {
toast.loading(t('failedIndexSafe'), {
duration: Infinity,
});
toast.loading(t('failedIndexSafe'));
navigate(BASE_ROUTES.landing);
}
},
Expand Down
Loading
Loading