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

Load proposals sequentially #1506

Merged
merged 26 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d711335
Set "batch" requesting, can't hurt right lol
adamgall Mar 30, 2024
951f587
Make a new action and reducer for setting a single Azorius proposal i…
adamgall Mar 30, 2024
ccf7b60
Unrelated to the rest of the work in here, but when getting Safe info…
adamgall Mar 30, 2024
3f549b6
Comment out all of the Azorius Proposal Listeners
adamgall Mar 30, 2024
ab359c0
Load proposals synchronously
adamgall Mar 30, 2024
5409650
Get all Voted and ProposalExecuted events one time, not per proposal
adamgall Mar 30, 2024
8fe9d1b
Rename function and do less param passing
adamgall Mar 30, 2024
c7a6359
Remove the @types/remove-markdown package, not used
adamgall Mar 31, 2024
bec53f2
If truncating Markdown, don't try to render URLs
adamgall Mar 31, 2024
cd0be94
Same event listener set up twice
adamgall Mar 31, 2024
d3f3999
Hm, not sure why these values are part of useeffect arrays
adamgall Mar 31, 2024
bf6af3f
Broke out a lot of functions from useAzoriusProposals to top level of…
adamgall Mar 31, 2024
c741273
Remove fully reloading azorius proposals after creating one
adamgall Apr 2, 2024
f813b30
Big refactor to re-enable setting up event listeners on Azorius and S…
adamgall Apr 2, 2024
9502942
Remove unneeded dependency
adamgall Apr 2, 2024
cd96779
Remove catch error, let's see if it keeps happening
adamgall Apr 3, 2024
f9f8c34
Move proposal creation / voted event listeners to own hook
adamgall Apr 3, 2024
8a05370
Remove dev testing console.logs
adamgall Apr 3, 2024
7c45797
Remove unnecessary function
adamgall Apr 3, 2024
fa26f01
Remove another unneeded object
adamgall Apr 3, 2024
02adb1a
Code is a flat circle
adamgall Apr 3, 2024
7d50962
Remove stray console.log
adamgall Apr 3, 2024
72f74ab
Delay processing new proposal for a block, to give RPC chance to catc…
adamgall Apr 3, 2024
8df3b39
Lint fixes, move listener
adamgall Apr 3, 2024
d1db677
More unnecessary dependency array params
adamgall Apr 3, 2024
db8823b
lint fixes
adamgall Apr 3, 2024
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
7 changes: 0 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"@testing-library/react": "^14.2.1",
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@types/remove-markdown": "^0.3.4",
Copy link
Member Author

Choose a reason for hiding this comment

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

Noticed that this unused package was hanging around, so removed it.

"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/proposal/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export default function Markdown({ truncate, content, collapsedLines = 6 }: IMar
maxWidth="100%"
>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
remarkPlugins={truncate ? [] : [remarkGfm]}
Copy link
Member Author

Choose a reason for hiding this comment

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

Don't try to render URLs as actual links if we're showing markdown in "truncate" mode.

We're using "truncate" when showing markdown in Proposal List cards, which are giant anchor tags, so we don't want to try to render more anchor tags within those.

Copy link
Member Author

Choose a reason for hiding this comment

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

this doesn't have anything to do with the rest of this PR

urlTransform={handleTransformURI}
components={MarkdownComponents}
className="markdown-body"
Expand Down
273 changes: 273 additions & 0 deletions src/hooks/DAO/loaders/governance/useAzoriusListeners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
import { LinearERC20Voting, LinearERC721Voting } from '@fractal-framework/fractal-contracts';
import { TypedListener } from '@fractal-framework/fractal-contracts/dist/typechain-types/common';
import { TimelockPeriodUpdatedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/MultisigFreezeGuard';
import {
Azorius,
ProposalCreatedEvent,
} from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/Azorius';
import { VotedEvent as ERC20VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC20Voting';
import { VotedEvent as ERC721VotedEvent } from '@fractal-framework/fractal-contracts/dist/typechain-types/contracts/azorius/LinearERC721Voting';
import { BigNumber } from 'ethers';
import { Dispatch, useEffect, useMemo } from 'react';
import { useFractal } from '../../../../providers/App/AppProvider';
import { FractalGovernanceAction } from '../../../../providers/App/governance/action';
import { useEthersProvider } from '../../../../providers/Ethers/hooks/useEthersProvider';
import {
ProposalMetadata,
VotingStrategyType,
DecodedTransaction,
FractalActions,
} from '../../../../types';
import { Providers } from '../../../../types/network';
import {
getProposalVotesSummary,
mapProposalCreatedEventToProposal,
decodeTransactions,
} from '../../../../utils';
import { getAverageBlockTime } from '../../../../utils/contract';
import useSafeContracts from '../../../safe/useSafeContracts';
import { useSafeDecoder } from '../../../utils/useSafeDecoder';

const proposalCreatedEventListener = (
azoriusContract: Azorius,
erc20StrategyContract: LinearERC20Voting | undefined,
erc721StrategyContract: LinearERC721Voting | undefined,
provider: Providers,
strategyType: VotingStrategyType,
decode: (value: string, to: string, data?: string | undefined) => Promise<DecodedTransaction[]>,
dispatch: Dispatch<FractalActions>,
): TypedListener<ProposalCreatedEvent> => {
return async (_strategyAddress, proposalId, proposer, transactions, metadata) => {
// Wait for a block before processing.
// We've seen that calling smart contract functions in `mapProposalCreatedEventToProposal`
// which include the `proposalId` error out because the RPC node (rather, the block it's on)
// doesn't see this proposal yet (despite the event being caught in the app...).
const averageBlockTime = await getAverageBlockTime(provider);
await new Promise(resolve => setTimeout(resolve, averageBlockTime * 1000));

if (!metadata) {
return;
}

const metaDataEvent: ProposalMetadata = JSON.parse(metadata);
const proposalData = {
metaData: {
title: metaDataEvent.title,
description: metaDataEvent.description,
documentationUrl: metaDataEvent.documentationUrl,
},
transactions: transactions,
decodedTransactions: await decodeTransactions(decode, transactions),
};

const proposal = await mapProposalCreatedEventToProposal(
erc20StrategyContract,
erc721StrategyContract,
strategyType,
proposalId,
proposer,
azoriusContract,
provider,
Promise.resolve(undefined),
Promise.resolve(undefined),
Promise.resolve(undefined),
proposalData,
);

dispatch({
type: FractalGovernanceAction.UPDATE_PROPOSALS_NEW,
payload: proposal,
});
};
};

const erc20VotedEventListener = (
erc20StrategyContract: LinearERC20Voting,
strategyType: VotingStrategyType,
dispatch: Dispatch<FractalActions>,
): TypedListener<ERC20VotedEvent> => {
return async (voter, proposalId, voteType, weight) => {
const votesSummary = await getProposalVotesSummary(
erc20StrategyContract,
undefined,
strategyType,
BigNumber.from(proposalId),
);

dispatch({
type: FractalGovernanceAction.UPDATE_NEW_AZORIUS_ERC20_VOTE,
payload: {
proposalId: proposalId.toString(),
voter,
support: voteType,
weight,
votesSummary,
},
});
};
};

const erc721VotedEventListener = (
erc721StrategyContract: LinearERC721Voting,
strategyType: VotingStrategyType,
dispatch: Dispatch<FractalActions>,
): TypedListener<ERC721VotedEvent> => {
return async (voter, proposalId, voteType, tokenAddresses, tokenIds) => {
const votesSummary = await getProposalVotesSummary(
undefined,
erc721StrategyContract,
strategyType,
BigNumber.from(proposalId),
);

dispatch({
type: FractalGovernanceAction.UPDATE_NEW_AZORIUS_ERC721_VOTE,
payload: {
proposalId: proposalId.toString(),
voter,
support: voteType,
tokenAddresses,
tokenIds: tokenIds.map(tokenId => tokenId.toString()),
votesSummary,
},
});
};
};

export const useAzoriusListeners = () => {
const {
action,
governanceContracts: {
azoriusContractAddress,
ozLinearVotingContractAddress,
erc721LinearVotingContractAddress,
},
} = useFractal();

const baseContracts = useSafeContracts();
const provider = useEthersProvider();
const decode = useSafeDecoder();

const azoriusContract = useMemo(() => {
if (!baseContracts || !azoriusContractAddress) {
return;
}

return baseContracts.fractalAzoriusMasterCopyContract.asProvider.attach(azoriusContractAddress);
}, [azoriusContractAddress, baseContracts]);

const strategyType = useMemo(() => {
if (ozLinearVotingContractAddress) {
return VotingStrategyType.LINEAR_ERC20;
} else if (erc721LinearVotingContractAddress) {
return VotingStrategyType.LINEAR_ERC721;
} else {
return undefined;
}
}, [ozLinearVotingContractAddress, erc721LinearVotingContractAddress]);

const erc20StrategyContract = useMemo(() => {
if (!baseContracts || !ozLinearVotingContractAddress) {
return undefined;
}

return baseContracts.linearVotingMasterCopyContract.asProvider.attach(
ozLinearVotingContractAddress,
);
}, [baseContracts, ozLinearVotingContractAddress]);

const erc721StrategyContract = useMemo(() => {
if (!baseContracts || !erc721LinearVotingContractAddress) {
return undefined;
}

return baseContracts.linearVotingERC721MasterCopyContract.asProvider.attach(
erc721LinearVotingContractAddress,
);
}, [baseContracts, erc721LinearVotingContractAddress]);

useEffect(() => {
if (!azoriusContract || !provider || !strategyType) {
return;
}

const proposalCreatedFilter = azoriusContract.filters.ProposalCreated();
const listener = proposalCreatedEventListener(
azoriusContract,
erc20StrategyContract,
erc721StrategyContract,
provider,
strategyType,
decode,
action.dispatch,
);

azoriusContract.on(proposalCreatedFilter, listener);

return () => {
azoriusContract.off(proposalCreatedFilter, listener);
};
}, [
action.dispatch,
azoriusContract,
decode,
erc20StrategyContract,
erc721StrategyContract,
provider,
strategyType,
]);

useEffect(() => {
if (strategyType !== VotingStrategyType.LINEAR_ERC20 || !erc20StrategyContract) {
return;
}

const votedEvent = erc20StrategyContract.filters.Voted();
const listener = erc20VotedEventListener(erc20StrategyContract, strategyType, action.dispatch);

erc20StrategyContract.on(votedEvent, listener);

return () => {
erc20StrategyContract.off(votedEvent, listener);
};
}, [action.dispatch, erc20StrategyContract, strategyType]);

useEffect(() => {
if (strategyType !== VotingStrategyType.LINEAR_ERC721 || !erc721StrategyContract) {
return;
}

const votedEvent = erc721StrategyContract.filters.Voted();
const listener = erc721VotedEventListener(
erc721StrategyContract,
strategyType,
action.dispatch,
);

erc721StrategyContract.on(votedEvent, listener);

return () => {
erc721StrategyContract.off(votedEvent, listener);
};
}, [action.dispatch, erc721StrategyContract, strategyType]);

useEffect(() => {
if (!azoriusContract) {
return;
}

const timeLockPeriodFilter = azoriusContract.filters.TimelockPeriodUpdated();
const timelockPeriodListener: TypedListener<TimelockPeriodUpdatedEvent> = timelockPeriod => {
action.dispatch({
type: FractalGovernanceAction.UPDATE_TIMELOCK_PERIOD,
payload: BigNumber.from(timelockPeriod),
});
};

azoriusContract.on(timeLockPeriodFilter, timelockPeriodListener);

return () => {
azoriusContract.off(timeLockPeriodFilter, timelockPeriodListener);
};
}, [action, azoriusContract]);
};
Loading