From 5dd1249dddc3e097c26a68bd5b4157ffa341dd60 Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Sun, 13 Jun 2021 15:15:00 -0400 Subject: [PATCH] feat: quick fix for new governor --- src/constants/addresses.ts | 10 +- src/constants/governance.ts | 25 +---- src/constants/proposals/index.ts | 6 -- src/hooks/useContract.ts | 9 +- src/pages/Vote/index.tsx | 5 +- src/state/governance/hooks.ts | 171 +++++++++++++++++++------------ 6 files changed, 122 insertions(+), 104 deletions(-) delete mode 100644 src/constants/proposals/index.ts diff --git a/src/constants/addresses.ts b/src/constants/addresses.ts index 536abf2958..8aefe9d664 100644 --- a/src/constants/addresses.ts +++ b/src/constants/addresses.ts @@ -16,10 +16,12 @@ export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap( '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', false ) -export const GOVERNANCE_ADDRESS: AddressMap = constructSameAddressMap( - '0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F', - false -) +export const GOVERNANCE_ADDRESSES: AddressMap[] = [ + constructSameAddressMap('0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F', false), + { + [SupportedChainId.MAINNET]: '0xC4e172459f1E7939D522503B81AFAaC1014CE6F6', + }, +] export const TIMELOCK_ADDRESS: AddressMap = constructSameAddressMap('0x1a9C8182C09F50C8318d769245beA52c32BE35BC', false) export const MERKLE_DISTRIBUTOR_ADDRESS: AddressMap = { [SupportedChainId.MAINNET]: '0x090D4613473dEE047c3f2706764f49E0821D256e', diff --git a/src/constants/governance.ts b/src/constants/governance.ts index 7c4857d3be..823236bbeb 100644 --- a/src/constants/governance.ts +++ b/src/constants/governance.ts @@ -1,31 +1,12 @@ -import { GOVERNANCE_ADDRESS, TIMELOCK_ADDRESS, UNI_ADDRESS } from './addresses' +import { GOVERNANCE_ADDRESSES, TIMELOCK_ADDRESS, UNI_ADDRESS } from './addresses' export const COMMON_CONTRACT_NAMES: { [chainId: number]: { [address: string]: string } } = { [1]: { [UNI_ADDRESS[1]]: 'UNI', - [GOVERNANCE_ADDRESS[1]]: 'Governance', + [GOVERNANCE_ADDRESSES[0][1]]: 'Governance (V0)', + [GOVERNANCE_ADDRESSES[1][1]]: 'Governance', [TIMELOCK_ADDRESS[1]]: 'Timelock', }, - [4]: { - [UNI_ADDRESS[4]]: 'Rinkeby UNI', - [GOVERNANCE_ADDRESS[4]]: 'Rinkeby Governance', - [TIMELOCK_ADDRESS[4]]: 'Rinkeby Timelock', - }, - [3]: { - [UNI_ADDRESS[3]]: 'Ropsten UNI', - [GOVERNANCE_ADDRESS[3]]: 'Ropsten Governance', - [TIMELOCK_ADDRESS[3]]: 'Ropsten Timelock', - }, - [42]: { - [UNI_ADDRESS[42]]: 'Kovan UNI', - [GOVERNANCE_ADDRESS[42]]: 'Kovan Governance', - [TIMELOCK_ADDRESS[42]]: 'Kovan Timelock', - }, - [5]: { - [UNI_ADDRESS[5]]: 'Goerli UNI', - [GOVERNANCE_ADDRESS[5]]: 'Goerli Governance', - [TIMELOCK_ADDRESS[5]]: 'Goerli Timelock', - }, } export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 13 diff --git a/src/constants/proposals/index.ts b/src/constants/proposals/index.ts deleted file mode 100644 index 6612cb355e..0000000000 --- a/src/constants/proposals/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UNISWAP_GRANTS_PROPOSAL_DESCRIPTION } from './uniswap_grants_proposal_description' - -// Proposals are 0-indexed -export const PROPOSAL_DESCRIPTION_TEXT: { [proposalId: number]: string } = { - [2]: UNISWAP_GRANTS_PROPOSAL_DESCRIPTION, -} diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index f6694fb867..92ebabc22c 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -27,7 +27,7 @@ import { V3_CORE_FACTORY_ADDRESSES, V3_MIGRATOR_ADDRESSES, ARGENT_WALLET_DETECTOR_ADDRESS, - GOVERNANCE_ADDRESS, + GOVERNANCE_ADDRESSES, MERKLE_DISTRIBUTOR_ADDRESS, MULTICALL2_ADDRESSES, V2_ROUTER_ADDRESS, @@ -116,8 +116,11 @@ export function useMerkleDistributorContract() { return useContract(MERKLE_DISTRIBUTOR_ADDRESS, MERKLE_DISTRIBUTOR_ABI, true) } -export function useGovernanceContract() { - return useContract(GOVERNANCE_ADDRESS, GOVERNANCE_ABI, true) +export function useGovernanceContracts(): (Contract | null)[] { + return [ + useContract(GOVERNANCE_ADDRESSES[0], GOVERNANCE_ABI, false), + useContract(GOVERNANCE_ADDRESSES[1], GOVERNANCE_ABI, true), + ] } export function useUniContract() { diff --git a/src/pages/Vote/index.tsx b/src/pages/Vote/index.tsx index 623b7f426a..d045b21a2c 100644 --- a/src/pages/Vote/index.tsx +++ b/src/pages/Vote/index.tsx @@ -120,7 +120,8 @@ export default function Vote() { const toggleDelegateModal = useToggleDelegateModal() // get data to list all proposals - const allProposals: ProposalData[] = useAllProposalData() + // TODO don't hardcode for first gov alpha + const allProposals: ProposalData[] = useAllProposalData()[0] // user data const availableVotes: CurrencyAmount | undefined = useUserVotes() @@ -248,7 +249,7 @@ export default function Vote() { )} - {allProposals?.map((p: ProposalData, i) => { + {allProposals?.reverse()?.map((p: ProposalData, i) => { return ( {p.id} diff --git a/src/state/governance/hooks.ts b/src/state/governance/hooks.ts index 652e9685ec..6b62b4dd88 100644 --- a/src/state/governance/hooks.ts +++ b/src/state/governance/hooks.ts @@ -1,8 +1,7 @@ import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { isAddress } from 'ethers/lib/utils' -import { PROPOSAL_DESCRIPTION_TEXT } from '../../constants/proposals' import { UNI } from '../../constants/tokens' -import { useGovernanceContract, useUniContract } from '../../hooks/useContract' +import { useGovernanceContracts, useUniContract } from '../../hooks/useContract' import { calculateGasMargin } from '../../utils/calculateGasMargin' import { useSingleCallResult, useSingleContractMultipleData } from '../multicall/hooks' import { useActiveWeb3React } from '../../hooks/web3' @@ -11,6 +10,7 @@ import { TransactionResponse } from '@ethersproject/providers' import { useTransactionAdder } from '../transactions/hooks' import { useState, useEffect, useCallback, useMemo } from 'react' import { abi as GOV_ABI } from '@uniswap/governance/build/GovernorAlpha.json' +import { UNISWAP_GRANTS_PROPOSAL_DESCRIPTION } from 'constants/proposals/uniswap_grants_proposal_description' interface ProposalDetail { target: string @@ -43,10 +43,9 @@ export enum ProposalState { Executed, } -// get count of all proposals made -export function useProposalCount(): number | undefined { - const gov = useGovernanceContract() - const res = useSingleCallResult(gov, 'proposalCount') +// get count of all proposals made on the given governor alpha +function useProposalCount(govContract: ethers.Contract | null): number | undefined { + const res = useSingleCallResult(govContract, 'proposalCount') if (res.result && !res.loading) { return parseInt(res.result[0]) } @@ -58,15 +57,15 @@ export function useProposalCount(): number | undefined { * new proposal event. */ const eventParser = new ethers.utils.Interface(GOV_ABI) -export function useDataFromEventLogs() { +function useDataFromEventLogs(govContract: ethers.Contract | null) { const { library, chainId } = useActiveWeb3React() const [formattedEvents, setFormattedEvents] = useState<{ description: string; details: { target: string; functionSig: string; callData: string }[] }[]>() - const govContract = useGovernanceContract() // create filter for these specific events const filter = useMemo( - () => (govContract ? { ...govContract.filters.ProposalCreated(), fromBlock: 0, toBlock: 'latest' } : undefined), + () => + govContract ? { ...govContract.filters.ProposalCreated(), fromBlock: 10861678, toBlock: 'latest' } : undefined, [govContract] ) @@ -79,28 +78,25 @@ export function useDataFromEventLogs() { .getLogs(filter) .then((proposalEvents) => { if (stale) return - // reverse events to get them from newest to odlest - const formattedEventData = proposalEvents - ?.map((event) => { - const eventParsed = eventParser.parseLog(event).args - return { - description: eventParsed.description, - details: eventParsed.targets.map((target: string, i: number) => { - const signature = eventParsed.signatures[i] - const [name, types] = signature.substr(0, signature.length - 1).split('(') + const formattedEventData = proposalEvents?.map((event) => { + const eventParsed = eventParser.parseLog(event).args + return { + description: eventParsed.description, + details: eventParsed.targets.map((target: string, i: number) => { + const signature = eventParsed.signatures[i] + const [name, types] = signature.substr(0, signature.length - 1).split('(') - const calldata = eventParsed.calldatas[i] - const decoded = utils.defaultAbiCoder.decode(types.split(','), calldata) + const calldata = eventParsed.calldatas[i] + const decoded = utils.defaultAbiCoder.decode(types.split(','), calldata) - return { - target, - functionSig: name, - callData: decoded.join(', '), - } - }), - } - }) - .reverse() + return { + target, + functionSig: name, + callData: decoded.join(', '), + } + }), + } + }) setFormattedEvents(formattedEventData) }) .catch((error) => { @@ -118,55 +114,93 @@ export function useDataFromEventLogs() { } // get data for all past and active proposals -export function useAllProposalData() { - const proposalCount = useProposalCount() - const govContract = useGovernanceContract() +export function useAllProposalData(): ProposalData[][] { + // fetch all governance contracts + const govContracts = useGovernanceContracts() - const proposalIndexes = [] - for (let i = 1; i <= (proposalCount ?? 0); i++) { - proposalIndexes.push([i]) - } + // fetch the proposal count on the active contract + const proposalCount = useProposalCount(govContracts[govContracts.length - 1]) - // get metadata from past events - const formattedEvents = useDataFromEventLogs() + // get all proposals for all contracts + const proposalsIndicesByGovContract = [ + [1, 2, 3, 4], // hardcoded for first governor alpha + typeof proposalCount === 'number' ? new Array(proposalCount).fill(0).map((_, i) => i + 1) : [], // dynamic for current governor alpha + ] // get all proposal entities - const allProposals = useSingleContractMultipleData(govContract, 'proposals', proposalIndexes) + const allProposalsByGovContract = [ + useSingleContractMultipleData( + govContracts[0], + 'proposals', + proposalsIndicesByGovContract[0].map((i) => [i]) + ), + useSingleContractMultipleData( + govContracts[1], + 'proposals', + proposalsIndicesByGovContract[1].map((i) => [i]) + ), + ] // get all proposal states - const allProposalStates = useSingleContractMultipleData(govContract, 'state', proposalIndexes) + const allProposalStatesByGovContract = [ + useSingleContractMultipleData( + govContracts[0], + 'state', + proposalsIndicesByGovContract[0].map((i) => [i]) + ), + useSingleContractMultipleData( + govContracts[1], + 'state', + proposalsIndicesByGovContract[1].map((i) => [i]) + ), + ] - if (formattedEvents && allProposals && allProposalStates) { - allProposals.reverse() - allProposalStates.reverse() + // get metadata from past events + const formattedEventsByGovContract = [useDataFromEventLogs(govContracts[0]), useDataFromEventLogs(govContracts[1])] - return allProposals - .filter((p, i) => { - return Boolean(p.result) && Boolean(allProposalStates[i]?.result) && Boolean(formattedEvents[i]) - }) - .map((p, i) => { - const description = PROPOSAL_DESCRIPTION_TEXT[allProposals.length - i - 1] || formattedEvents[i].description - const formattedProposal: ProposalData = { - id: allProposals[i]?.result?.id.toString(), - title: description?.split(/# |\n/g)[1] || 'Untitled', - description: description || 'No description.', - proposer: allProposals[i]?.result?.proposer, - status: allProposalStates[i]?.result?.[0] ?? ProposalState.Undetermined, - forCount: parseFloat(ethers.utils.formatUnits(allProposals[i]?.result?.forVotes.toString(), 18)), - againstCount: parseFloat(ethers.utils.formatUnits(allProposals[i]?.result?.againstVotes.toString(), 18)), - startBlock: parseInt(allProposals[i]?.result?.startBlock?.toString()), - endBlock: parseInt(allProposals[i]?.result?.endBlock?.toString()), - details: formattedEvents[i].details, - } - return formattedProposal - }) - } else { - return [] + const returnData: ProposalData[][] = [] + + for (let governorIndex = 0; governorIndex < allProposalsByGovContract.length; governorIndex++) { + const allProposals = allProposalsByGovContract[governorIndex] + const allProposalStates = allProposalStatesByGovContract[governorIndex] + const formattedEvents = formattedEventsByGovContract[governorIndex] + + if ( + allProposals?.every((p) => Boolean(p.result)) && + allProposalStates?.every((p) => Boolean(p.result)) && + formattedEvents?.every((p) => Boolean(p)) + ) { + returnData.push( + allProposals.map((proposal, i): ProposalData => { + let description = formattedEvents[i].description + // overwrite broken description + if (governorIndex === 0 && i === 2) description = UNISWAP_GRANTS_PROPOSAL_DESCRIPTION + + return { + id: proposal?.result?.id.toString(), + title: description?.split(/# |\n/g)[1] ?? 'Untitled', + description: description ?? 'No description.', + proposer: proposal?.result?.proposer, + status: allProposalStates[i]?.result?.[0] ?? ProposalState.Undetermined, + forCount: parseFloat(ethers.utils.formatUnits(proposal?.result?.forVotes.toString(), 18)), + againstCount: parseFloat(ethers.utils.formatUnits(proposal?.result?.againstVotes.toString(), 18)), + startBlock: parseInt(proposal?.result?.startBlock?.toString()), + endBlock: parseInt(proposal?.result?.endBlock?.toString()), + details: formattedEvents[i].details, + } + }) + ) + } else { + returnData.push([]) + } } + + return returnData } export function useProposalData(id: string): ProposalData | undefined { - const allProposalData = useAllProposalData() + // TODO don't hardcode for first gov alpha + const allProposalData = useAllProposalData()[0] return allProposalData?.find((p) => p.id === id) } @@ -232,7 +266,10 @@ export function useVoteCallback(): { } { const { account } = useActiveWeb3React() - const govContract = useGovernanceContract() + // we only care about voting on the active governance contract + const govContracts = useGovernanceContracts() + const govContract = govContracts[govContracts.length - 1] + const addTransaction = useTransactionAdder() const voteCallback = useCallback(