Skip to content

Commit

Permalink
Merge pull request #409 from DXgovernance/release/v2.0.1
Browse files Browse the repository at this point in the history
Release/v2.0.1
  • Loading branch information
rossneilson authored Nov 7, 2022
2 parents d57334a + 70fdea9 commit 40062bb
Show file tree
Hide file tree
Showing 21 changed files with 2,147 additions and 2,042 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "davi",
"version": "2.0.0",
"version": "2.0.1",
"description": "Decentralized Autonomous Voting Interface",
"scripts": {
"dev": "./scripts/dev.sh",
Expand Down Expand Up @@ -39,6 +39,7 @@
"@dnd-kit/modifiers": "^5.0.0",
"@dnd-kit/sortable": "^6.0.1",
"@dnd-kit/utilities": "^3.1.0",
"@ensdomains/content-hash": "^2.5.7",
"@nomiclabs/hardhat-ethers": "^2.0.6",
"@nomiclabs/hardhat-truffle5": "^2.0.5",
"@nomiclabs/hardhat-web3": "^2.0.0",
Expand All @@ -60,7 +61,6 @@
"babel-loader": "8.1.0",
"bignumber.js": "^9.0.2",
"buffer": "^6.0.3",
"content-hash": "^2.5.2",
"copy-to-clipboard": "^3.3.1",
"crypto-js": "^4.1.1",
"diff": "^5.1.0",
Expand Down
11 changes: 11 additions & 0 deletions setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ jest.mock('react-i18next', () => {
};
});

jest.mock('@ensdomains/content-hash', () => {
return {
fromIpfs: () => {
return 'ipfs://';
},
decode: () => {
return 'QmRAQB6YaCyidP37UdDnjFY5vQuiBrcqdyoW1CuDgwxkD4';
},
};
});

25 changes: 19 additions & 6 deletions src/Modules/Guilds/Hooks/useGuildImplementationType.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useNetwork, useProvider } from 'wagmi';
import { SHA256, enc } from 'crypto-js';
import { GuildImplementationType } from 'types/types.guilds.d';
Expand All @@ -23,6 +23,7 @@ interface ImplementationTypeConfig {
interface ImplementationTypeConfigReturn extends ImplementationTypeConfig {
isRepGuild: boolean;
isSnapshotGuild: boolean;
loaded?: boolean;
}
const parseConfig = (
config: ImplementationTypeConfig
Expand All @@ -46,15 +47,27 @@ export default function useGuildImplementationTypeConfig(
const isLocalhost = useMemo(() => chain?.id === LOCALHOST_ID, [chain]);

const [guildBytecode, setGuildBytecode] = useState<string>('');
const [loaded, setLoaded] = useState<boolean>(false);
const provider = useProvider();
useEffect(() => {
const getBytecode = async () => {
const btcode = await provider.getCode(guildAddress);
const hashedBytecode = `0x${SHA256(btcode).toString(enc.Hex)}`;
setGuildBytecode(hashedBytecode);
const localBtcode = localStorage.getItem(
`hashed-bytecode-${guildAddress}`
);
if (!localBtcode) {
const btcode = await provider.getCode(guildAddress);
const hashedBytecode = `0x${SHA256(btcode).toString(enc.Hex)}`;
setGuildBytecode(hashedBytecode);
localStorage.setItem(`hashed-bytecode-${guildAddress}`, hashedBytecode);
setLoaded(true);
return;
}
setGuildBytecode(localBtcode);
setLoaded(true);
};
getBytecode();
}, [guildAddress, provider]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [guildAddress]);

const implementationTypeConfig: ImplementationTypeConfig = useMemo(() => {
if (!guildBytecode) return defaultImplementation;
Expand All @@ -66,5 +79,5 @@ export default function useGuildImplementationTypeConfig(
return match ?? defaultImplementation; // default to IERC20Guild
}, [guildBytecode, isLocalhost]);

return parseConfig(implementationTypeConfig);
return { ...parseConfig(implementationTypeConfig), loaded };
}
8 changes: 5 additions & 3 deletions src/Modules/Guilds/Hooks/useProposalCalls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ const useProposalCalls = (guildId: string, proposalId: string) => {
const { data: proposal } = useProposal(guildId, proposalId);
const { data: metadata } = useProposalMetadata(guildId, proposalId);
const votingResults = useVotingResults(guildId, proposalId);

const { contracts } = useRichContractRegistry();
const { chain } = useNetwork();
const { t } = useTranslation();
// Used to wait for the bytecode to be fetched
const { isSnapshotGuild } = useGuildImplementationTypeConfig(guildId);
const { loaded } = useGuildImplementationTypeConfig(guildId);
const theme = useTheme();
const [options, setOptions] = useState<Option[]>([]);

Expand Down Expand Up @@ -87,7 +88,7 @@ const useProposalCalls = (guildId: string, proposalId: string) => {
}, [calls, callsPerOption, totalOptionsNum]);

useEffect(() => {
if (!guildId || !proposalId || !splitCalls) {
if (!guildId || !proposalId || !splitCalls || !loaded) {
setOptions([]);
return;
}
Expand Down Expand Up @@ -165,7 +166,8 @@ const useProposalCalls = (guildId: string, proposalId: string) => {
theme,
optionLabels,
totalOptionsNum,
isSnapshotGuild,
votingResults?.totalLocked,
loaded,
]);

return {
Expand Down
8 changes: 6 additions & 2 deletions src/Modules/Guilds/Hooks/useTotalLocked.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const useTotalLocked = (guildAddress: string, snapshotId?: string) => {

const SNAPSHOT_ID = snapshotId ?? _snapshotId?.toString() ?? null;

const { isSnapshotGuild, isRepGuild } =
const { isSnapshotGuild, isRepGuild, loaded } =
useGuildImplementationType(guildAddress);

const { data: totalLockedResponse } = useContractRead({
Expand All @@ -41,7 +41,11 @@ const useTotalLocked = (guildAddress: string, snapshotId?: string) => {
});

// Return response based on implementation type

if (!loaded) {
return {
data: undefined,
};
}
if (isRepGuild && isSnapshotGuild) {
return SNAPSHOT_ID
? {
Expand Down
15 changes: 14 additions & 1 deletion src/Modules/Guilds/Hooks/useVotingPowerForProposalExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ interface UseVotingPowerForProposalExecutionProps {
interface GetConfigArgs {
isRepGuild: boolean;
isSnapshotGuild: boolean;
loaded: boolean;
contractAddress: string;
snapshotId: BigNumber;
proposalId: string;
}
const getConfig = ({
isRepGuild,
isSnapshotGuild,
loaded,
contractAddress,
snapshotId,
proposalId,
Expand All @@ -61,7 +63,17 @@ const getConfig = ({
functionName: 'getSnapshotVotingPowerForProposalExecution(bytes32)',
args: [proposalId],
};

const stillLoadingConf = {
enabled: false,
addressOrName: null,
contractInterface: SnapshotRepERC20Guild.abi,
functionName: 'getSnapshotVotingPowerForProposalExecution(bytes32)',
args: [proposalId],
};

// Validate args to avoid null values
if (!loaded) return stillLoadingConf;
if (isRepGuild && isSnapshotGuild && !!proposalId) return snapshotRepConf;
if (isSnapshotGuild && !!snapshotId) return snapshotConfig;
return baseErc20Config;
Expand All @@ -71,7 +83,7 @@ export const useVotingPowerForProposalExecution = ({
contractAddress,
proposalId: proposalIdProp,
}: UseVotingPowerForProposalExecutionProps): WagmiUseContractReadResponse<BigNumber> => {
const { isSnapshotGuild, isRepGuild } =
const { isSnapshotGuild, isRepGuild, loaded } =
useGuildImplementationType(contractAddress);

const { proposalId: FALLBACK_PROPOSAL_ID } = useTypedParams();
Expand All @@ -86,6 +98,7 @@ export const useVotingPowerForProposalExecution = ({
getConfig({
isRepGuild,
isSnapshotGuild,
loaded,
contractAddress,
snapshotId,
proposalId,
Expand Down
2 changes: 1 addition & 1 deletion src/Modules/Guilds/pages/CreateProposal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import SidebarInfoCardWrapper from 'Modules/Guilds/Wrappers/SidebarInfoCardWrapp
import { Input } from 'components/primitives/Forms/Input';
import { Box, Flex } from 'components/primitives/Layout';
import { useTypedParams } from 'Modules/Guilds/Hooks/useTypedParams';
import contentHash from 'content-hash';
import contentHash from '@ensdomains/content-hash';
import { useTransactions } from 'contexts/Guilds';
import { GuildAvailabilityContext } from 'contexts/Guilds/guildAvailability';
import { BigNumber } from 'ethers';
Expand Down
133 changes: 70 additions & 63 deletions src/Modules/Guilds/pages/Proposal/Proposal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
} from './Proposal.styled';
import { useTranslation } from 'react-i18next';
import useTimeDetail from 'Modules/Guilds/Hooks/useTimeDetail';
import useGuildImplementationTypeConfig from 'Modules/Guilds/Hooks/useGuildImplementationType';

const ProposalPage: React.FC = () => {
const { t } = useTranslation();
Expand All @@ -52,6 +53,7 @@ const ProposalPage: React.FC = () => {
const { data: proposal, error } = useProposal(guildId, proposalId);
const { options } = useProposalCalls(guildId, proposalId);
const { data: guildConfig } = useGuildConfig(guildId);
const { loaded } = useGuildImplementationTypeConfig(guildId);

const { data: metadata, error: metadataError } = useProposalMetadata(
guildId,
Expand All @@ -77,75 +79,80 @@ const ProposalPage: React.FC = () => {
data: { executeProposal },
} = useExecutable();

if (!isGuildAvailabilityLoading) {
if (!proposalIds?.includes(proposalId)) {
return (
<Result
state={ResultState.ERROR}
title="We couldn't find that proposal."
subtitle="It probably doesn't exist."
extra={
<UnstyledLink to={`/${chainName}/${guildId}`}>
<IconButton iconLeft>
<FiArrowLeft /> See all proposals
</IconButton>
</UnstyledLink>
}
/>
);
} else if (error) {
return (
<Result
state={ResultState.ERROR}
title={t('errorMessage.genericProposalError')}
subtitle={error.message}
/>
);
if (!loaded) {
return <></>;
} else {
if (!isGuildAvailabilityLoading) {
if (!proposalIds?.includes(proposalId)) {
return (
<Result
state={ResultState.ERROR}
title="We couldn't find that proposal."
subtitle="It probably doesn't exist."
extra={
<UnstyledLink to={`/${chainName}/${guildId}`}>
<IconButton iconLeft>
<FiArrowLeft /> See all proposals
</IconButton>
</UnstyledLink>
}
/>
);
} else if (error) {
return (
<Result
state={ResultState.ERROR}
title={t('errorMessage.genericProposalError')}
subtitle={error.message}
/>
);
}
}
}

return (
<PageContainer>
<PageContent>
<PageHeader>
<HeaderTopRow>
<UnstyledLink to={`/${chainName}/${guildId}`}>
<StyledIconButton variant="secondary" iconLeft>
<FaChevronLeft style={{ marginRight: '15px' }} />{' '}
{guildConfig?.name}
</StyledIconButton>
</UnstyledLink>
return (
<PageContainer>
<PageContent>
<PageHeader>
<HeaderTopRow>
<UnstyledLink to={`/${chainName}/${guildId}`}>
<StyledIconButton variant="secondary" iconLeft>
<FaChevronLeft style={{ marginRight: '15px' }} />{' '}
{guildConfig?.name}
</StyledIconButton>
</UnstyledLink>

<ProposalStatus status={status} endTime={endTime} />
{status === ProposalState.Executable && !isReadOnly(connector) && (
<ExecuteButton executeProposal={executeProposal} />
)}
</HeaderTopRow>
<PageTitle>
{proposal?.title || (
<Loading loading text skeletonProps={{ width: '800px' }} />
)}
</PageTitle>
</PageHeader>
<ProposalStatus status={status} endTime={endTime} />
{status === ProposalState.Executable &&
!isReadOnly(connector) && (
<ExecuteButton executeProposal={executeProposal} />
)}
</HeaderTopRow>
<PageTitle>
{proposal?.title || (
<Loading loading text skeletonProps={{ width: '800px' }} />
)}
</PageTitle>
</PageHeader>

<AddressButton address={proposal?.creator} />
<AddressButton address={proposal?.creator} />

<ProposalDescription metadata={metadata} error={metadataError} />
<ProposalDescription metadata={metadata} error={metadataError} />

<ProposalActionsWrapper>
<ActionsBuilder options={options} editable={false} />
</ProposalActionsWrapper>
</PageContent>
<SidebarContent>
<ProposalVoteCardWrapper />
<ProposalInfoCard
proposal={proposal}
guildConfig={guildConfig}
quorum={quorum}
/>
</SidebarContent>
</PageContainer>
);
<ProposalActionsWrapper>
<ActionsBuilder options={options} editable={false} />
</ProposalActionsWrapper>
</PageContent>
<SidebarContent>
<ProposalVoteCardWrapper />
<ProposalInfoCard
proposal={proposal}
guildConfig={guildConfig}
quorum={quorum}
/>
</SidebarContent>
</PageContainer>
);
}
};

export default ProposalPage;
Binary file not shown.
Loading

0 comments on commit 40062bb

Please sign in to comment.