fix: Improve Vote page (#3429)
* introduce useQuorum introduce LATEST_GOVERNOR_INDEX * use CurrencyAmounts and fix % logic * gate useQuorum to mainnet, just to be safe * comment
This commit is contained in:
parent
542bf0bf66
commit
4d69c946bf
@ -23,3 +23,5 @@ export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 13
|
||||
export const AVERAGE_BLOCK_TIME_IN_SECS: { [chainId: number]: number } = {
|
||||
1: DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
|
||||
}
|
||||
|
||||
export const LATEST_GOVERNOR_INDEX = 2
|
||||
|
@ -23,6 +23,7 @@ import styled from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
|
||||
import { CreateProposalTabs } from '../../components/NavigationTabs'
|
||||
import { LATEST_GOVERNOR_INDEX } from '../../constants/governance'
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import AppBody from '../AppBody'
|
||||
import { ProposalActionDetail } from './ProposalActionDetail'
|
||||
@ -88,8 +89,7 @@ export default function CreateProposal() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
|
||||
const latestProposalId = useLatestProposalId(account ?? undefined) ?? '0'
|
||||
// the first argument below should be the index of the latest governor
|
||||
const latestProposalData = useProposalData(/* governorIndex */ 2, latestProposalId)
|
||||
const latestProposalData = useProposalData(LATEST_GOVERNOR_INDEX, latestProposalId)
|
||||
const { votes: availableVotes } = useUserVotes()
|
||||
const proposalThreshold: CurrencyAmount<Token> | undefined = useProposalThreshold()
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Fraction, Token } from '@uniswap/sdk-core'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
|
||||
@ -34,6 +33,7 @@ import {
|
||||
ProposalData,
|
||||
ProposalState,
|
||||
useProposalData,
|
||||
useQuorum,
|
||||
useUserDelegatee,
|
||||
useUserVotesAsOfBlock,
|
||||
} from '../../state/governance/hooks'
|
||||
@ -100,7 +100,7 @@ const Progress = styled.div<{ status: 'for' | 'against'; percentageString?: stri
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: ${({ theme, status }) => (status === 'for' ? theme.green1 : theme.red1)};
|
||||
width: ${({ percentageString }) => percentageString};
|
||||
width: ${({ percentageString }) => percentageString ?? '0%'};
|
||||
`
|
||||
|
||||
const MarkDownWrapper = styled.div`
|
||||
@ -146,10 +146,14 @@ export default function VotePage({
|
||||
params: { governorIndex, id },
|
||||
},
|
||||
}: RouteComponentProps<{ governorIndex: string; id: string }>) {
|
||||
const parsedGovernorIndex = Number.parseInt(governorIndex)
|
||||
|
||||
const { chainId, account } = useActiveWeb3React()
|
||||
|
||||
const quorumAmount = useQuorum(parsedGovernorIndex)
|
||||
|
||||
// get data for this specific proposal
|
||||
const proposalData: ProposalData | undefined = useProposalData(Number.parseInt(governorIndex), id)
|
||||
const proposalData: ProposalData | undefined = useProposalData(parsedGovernorIndex, id)
|
||||
|
||||
// update vote option based on button interactions
|
||||
const [voteOption, setVoteOption] = useState<VoteOption | undefined>(undefined)
|
||||
@ -189,13 +193,11 @@ export default function VotePage({
|
||||
}
|
||||
|
||||
// get total votes and format percentages for UI
|
||||
const totalVotes: number | undefined = proposalData ? proposalData.forCount + proposalData.againstCount : undefined
|
||||
const forPercentage: string = t`${
|
||||
proposalData && totalVotes ? ((proposalData.forCount * 100) / totalVotes).toFixed(0) : '0'
|
||||
} %`
|
||||
const againstPercentage: string = t`${
|
||||
proposalData && totalVotes ? ((proposalData.againstCount * 100) / totalVotes).toFixed(0) : '0'
|
||||
} %`
|
||||
const totalVotes = proposalData?.forCount?.add(proposalData.againstCount)
|
||||
const forPercentage = totalVotes
|
||||
? proposalData?.forCount?.asFraction?.divide(totalVotes.asFraction)?.multiply(100)
|
||||
: undefined
|
||||
const againstPercentage = forPercentage ? new Fraction(100).subtract(forPercentage) : undefined
|
||||
|
||||
// only count available votes as of the proposal start block
|
||||
const availableVotes: CurrencyAmount<Token> | undefined = useUserVotesAsOfBlock(proposalData?.startBlock ?? undefined)
|
||||
@ -321,13 +323,20 @@ export default function VotePage({
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
<Trans>For</Trans>
|
||||
</ThemedText.Black>
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
{proposalData?.forCount?.toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||
</ThemedText.Black>
|
||||
{proposalData && (
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
{proposalData.forCount.toFixed(0, { groupSeparator: ',' })}
|
||||
{quorumAmount && (
|
||||
<span style={{ fontWeight: 400 }}>{` / ${quorumAmount.toExact({
|
||||
groupSeparator: ',',
|
||||
})}`}</span>
|
||||
)}
|
||||
</ThemedText.Black>
|
||||
)}
|
||||
</WrapSmall>
|
||||
</AutoColumn>
|
||||
<ProgressWrapper>
|
||||
<Progress status={'for'} percentageString={forPercentage} />
|
||||
{forPercentage && <Progress status={'for'} percentageString={`${forPercentage.toFixed(0)}%`} />}
|
||||
</ProgressWrapper>
|
||||
</CardSection>
|
||||
</StyledDataCard>
|
||||
@ -338,13 +347,17 @@ export default function VotePage({
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
<Trans>Against</Trans>
|
||||
</ThemedText.Black>
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
{proposalData?.againstCount?.toLocaleString(undefined, { maximumFractionDigits: 0 })}
|
||||
</ThemedText.Black>
|
||||
{proposalData && (
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
{proposalData.againstCount.toFixed(0, { groupSeparator: ',' })}
|
||||
</ThemedText.Black>
|
||||
)}
|
||||
</WrapSmall>
|
||||
</AutoColumn>
|
||||
<ProgressWrapper>
|
||||
<Progress status={'against'} percentageString={againstPercentage} />
|
||||
{againstPercentage && (
|
||||
<Progress status={'against'} percentageString={`${againstPercentage.toFixed(0)}%`} />
|
||||
)}
|
||||
</ProgressWrapper>
|
||||
</CardSection>
|
||||
</StyledDataCard>
|
||||
|
@ -3,18 +3,19 @@ import { isAddress } from '@ethersproject/address'
|
||||
import { Contract } from '@ethersproject/contracts'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { toUtf8String, Utf8ErrorFuncs, Utf8ErrorReason } from '@ethersproject/strings'
|
||||
import { formatUnits } from '@ethersproject/units'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import GovernorAlphaJson from '@uniswap/governance/build/GovernorAlpha.json'
|
||||
import UniJson from '@uniswap/governance/build/Uni.json'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { ChainId } from '@uniswap/smart-order-router'
|
||||
import GOVERNOR_BRAVO_ABI from 'abis/governor-bravo.json'
|
||||
import {
|
||||
GOVERNANCE_ALPHA_V0_ADDRESSES,
|
||||
GOVERNANCE_ALPHA_V1_ADDRESSES,
|
||||
GOVERNANCE_BRAVO_ADDRESSES,
|
||||
} from 'constants/addresses'
|
||||
import { LATEST_GOVERNOR_INDEX } from 'constants/governance'
|
||||
import { POLYGON_PROPOSAL_TITLE } from 'constants/proposals/polygon_proposal_title'
|
||||
import { UNISWAP_GRANTS_PROPOSAL_DESCRIPTION } from 'constants/proposals/uniswap_grants_proposal_description'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
@ -39,23 +40,24 @@ import { VoteOption } from './types'
|
||||
const { abi: GOVERNANCE_ABI } = GovernorAlphaJson
|
||||
const { abi: UNI_ABI } = UniJson
|
||||
|
||||
export function useGovernanceV0Contract(): Contract | null {
|
||||
function useGovernanceV0Contract(): Contract | null {
|
||||
return useContract(GOVERNANCE_ALPHA_V0_ADDRESSES, GOVERNANCE_ABI, false)
|
||||
}
|
||||
|
||||
export function useGovernanceV1Contract(): Contract | null {
|
||||
function useGovernanceV1Contract(): Contract | null {
|
||||
return useContract(GOVERNANCE_ALPHA_V1_ADDRESSES, GOVERNANCE_ABI, false)
|
||||
}
|
||||
|
||||
export function useGovernanceBravoContract(): Contract | null {
|
||||
function useGovernanceBravoContract(): Contract | null {
|
||||
return useContract(GOVERNANCE_BRAVO_ADDRESSES, GOVERNOR_BRAVO_ABI, true)
|
||||
}
|
||||
|
||||
export const useLatestGovernanceContract = useGovernanceBravoContract
|
||||
const useLatestGovernanceContract = useGovernanceBravoContract
|
||||
|
||||
export function useUniContract() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return useContract(chainId ? UNI[chainId]?.address : undefined, UNI_ABI, true)
|
||||
const uniAddress = useMemo(() => (chainId ? UNI[chainId]?.address : undefined), [chainId])
|
||||
return useContract(uniAddress, UNI_ABI, true)
|
||||
}
|
||||
|
||||
interface ProposalDetail {
|
||||
@ -70,8 +72,8 @@ export interface ProposalData {
|
||||
description: string
|
||||
proposer: string
|
||||
status: ProposalState
|
||||
forCount: number
|
||||
againstCount: number
|
||||
forCount: CurrencyAmount<Token>
|
||||
againstCount: CurrencyAmount<Token>
|
||||
startBlock: number
|
||||
endBlock: number
|
||||
details: ProposalDetail[]
|
||||
@ -244,6 +246,8 @@ export function useAllProposalData(): { data: ProposalData[]; loading: boolean }
|
||||
const formattedLogsV1 = useFormattedProposalCreatedLogs(gov1, gov1ProposalIndexes)
|
||||
const formattedLogsV2 = useFormattedProposalCreatedLogs(gov2, gov2ProposalIndexes)
|
||||
|
||||
const uni = useMemo(() => (chainId ? UNI[chainId] : undefined), [chainId])
|
||||
|
||||
// early return until events are fetched
|
||||
return useMemo(() => {
|
||||
const proposalsCallData = [...proposalsV0, ...proposalsV1, ...proposalsV2]
|
||||
@ -251,6 +255,7 @@ export function useAllProposalData(): { data: ProposalData[]; loading: boolean }
|
||||
const formattedLogs = [...(formattedLogsV0 ?? []), ...(formattedLogsV1 ?? []), ...(formattedLogsV2 ?? [])]
|
||||
|
||||
if (
|
||||
!uni ||
|
||||
proposalsCallData.some((p) => p.loading) ||
|
||||
proposalStatesCallData.some((p) => p.loading) ||
|
||||
(gov0 && !formattedLogsV0) ||
|
||||
@ -280,9 +285,8 @@ export function useAllProposalData(): { data: ProposalData[]; loading: boolean }
|
||||
description: description ?? t`No description.`,
|
||||
proposer: proposal?.result?.proposer,
|
||||
status: proposalStatesCallData[i]?.result?.[0] ?? ProposalState.UNDETERMINED,
|
||||
forCount: parseFloat(formatUnits(proposal?.result?.forVotes?.toString() ?? 0, 18)),
|
||||
againstCount: parseFloat(formatUnits(proposal?.result?.againstVotes?.toString() ?? 0, 18)),
|
||||
abstainCount: parseFloat(formatUnits(proposal?.result?.abstainVotes?.toString() ?? 0, 18)),
|
||||
forCount: CurrencyAmount.fromRawAmount(uni, proposal?.result?.forVotes),
|
||||
againstCount: CurrencyAmount.fromRawAmount(uni, proposal?.result?.againstVotes),
|
||||
startBlock,
|
||||
endBlock: parseInt(proposal?.result?.endBlock?.toString()),
|
||||
details: formattedLogs[i]?.details,
|
||||
@ -304,6 +308,7 @@ export function useAllProposalData(): { data: ProposalData[]; loading: boolean }
|
||||
proposalsV0,
|
||||
proposalsV1,
|
||||
proposalsV2,
|
||||
uni,
|
||||
])
|
||||
}
|
||||
|
||||
@ -312,6 +317,24 @@ export function useProposalData(governorIndex: number, id: string): ProposalData
|
||||
return data.filter((p) => p.governorIndex === governorIndex)?.find((p) => p.id === id)
|
||||
}
|
||||
|
||||
export function useQuorum(governorIndex: number): CurrencyAmount<Token> | undefined {
|
||||
const latestGovernanceContract = useLatestGovernanceContract()
|
||||
const quorumVotes = useSingleCallResult(latestGovernanceContract, 'quorumVotes')?.result?.[0]
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const uni = useMemo(() => (chainId ? UNI[chainId] : undefined), [chainId])
|
||||
|
||||
if (
|
||||
!latestGovernanceContract ||
|
||||
!quorumVotes ||
|
||||
chainId !== ChainId.MAINNET ||
|
||||
!uni ||
|
||||
governorIndex !== LATEST_GOVERNOR_INDEX
|
||||
)
|
||||
return undefined
|
||||
|
||||
return CurrencyAmount.fromRawAmount(uni, quorumVotes)
|
||||
}
|
||||
|
||||
// get the users delegatee if it exists
|
||||
export function useUserDelegatee(): string {
|
||||
const { account } = useActiveWeb3React()
|
||||
@ -339,7 +362,7 @@ export function useUserVotesAsOfBlock(block: number | undefined): CurrencyAmount
|
||||
const uniContract = useUniContract()
|
||||
|
||||
// check for available votes
|
||||
const uni = chainId ? UNI[chainId] : undefined
|
||||
const uni = useMemo(() => (chainId ? UNI[chainId] : undefined), [chainId])
|
||||
const votes = useSingleCallResult(uniContract, 'getPriorVotes', [account ?? undefined, block ?? undefined])
|
||||
?.result?.[0]
|
||||
return votes && uni ? CurrencyAmount.fromRawAmount(uni, votes) : undefined
|
||||
@ -451,7 +474,7 @@ export function useProposalThreshold(): CurrencyAmount<Token> | undefined {
|
||||
|
||||
const latestGovernanceContract = useLatestGovernanceContract()
|
||||
const res = useSingleCallResult(latestGovernanceContract, 'proposalThreshold')
|
||||
const uni = chainId ? UNI[chainId] : undefined
|
||||
const uni = useMemo(() => (chainId ? UNI[chainId] : undefined), [chainId])
|
||||
|
||||
if (res?.result?.[0] && uni) {
|
||||
return CurrencyAmount.fromRawAmount(uni, res.result[0])
|
||||
|
Loading…
Reference in New Issue
Block a user