Skip to content

Commit

Permalink
feat: VeCAKE voting power (#8474)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xjojoex authored Dec 15, 2023
1 parent 013e642 commit b9e7329
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 62 deletions.
4 changes: 2 additions & 2 deletions apps/web/src/utils/calls/pools.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChainId } from '@pancakeswap/chains'
import { getPoolsConfig } from '@pancakeswap/pools'
import { SerializedPool, getPoolsConfig } from '@pancakeswap/pools'

import chunk from 'lodash/chunk'
import { publicClient } from 'utils/wagmi'
Expand Down Expand Up @@ -36,7 +36,7 @@ const ABI = [
/**
* Returns the total number of pools that were active at a given block
*/
export const getActivePools = async (chainId: ChainId, block?: number) => {
export const getActivePools = async (chainId: ChainId, block?: number): Promise<SerializedPool[]> => {
const poolsConfig = getPoolsConfig(chainId)
const eligiblePools = poolsConfig
.filter((pool) => pool.sousId !== 0)
Expand Down
17 changes: 10 additions & 7 deletions apps/web/src/views/Voting/components/CastVoteModal/DetailsView.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useMemo } from 'react'
import BigNumber from 'bignumber.js'
import { Flex, Text, Box, HelpIcon, useTooltip, RocketIcon, ScanLink, Link } from '@pancakeswap/uikit'
import { useTranslation } from '@pancakeswap/localization'
import { styled } from 'styled-components'
import { getBlockExploreLink } from 'utils'
import { Box, Flex, HelpIcon, Link, RocketIcon, ScanLink, Text, useTooltip } from '@pancakeswap/uikit'
import { formatNumber } from '@pancakeswap/utils/formatBalance'
import BigNumber from 'bignumber.js'
import { useActiveChainId } from 'hooks/useActiveChainId'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { useMemo } from 'react'
import { styled } from 'styled-components'
import { getBlockExploreLink } from 'utils'
import { ModalInner, VotingBoxBorder, VotingBoxCardInner } from './styles'

const StyledScanLink = styled(ScanLink)`
export const StyledScanLink = styled(ScanLink)`
display: inline-flex;
font-size: 14px;
> svg {
Expand Down Expand Up @@ -73,6 +74,8 @@ const DetailsView: React.FC<React.PropsWithChildren<DetailsViewProps>> = ({
const { t } = useTranslation()
const blockTimestamp = useCurrentBlockTimestamp()

const { chainId } = useActiveChainId()

const isBoostingExpired = useMemo(() => {
return lockedEndTime !== 0 && new BigNumber(blockTimestamp?.toString()).gte(lockedEndTime)
}, [blockTimestamp, lockedEndTime])
Expand Down Expand Up @@ -124,7 +127,7 @@ const DetailsView: React.FC<React.PropsWithChildren<DetailsViewProps>> = ({
</VotingBoxBorder>
<Text color="secondary" textTransform="uppercase" mb="4px" bold fontSize="14px">
{t('Your voting power at block')}
<StyledScanLink useBscCoinFallback href={getBlockExploreLink(block, 'block')} ml="8px">
<StyledScanLink useBscCoinFallback href={getBlockExploreLink(block, 'block', chainId)} ml="8px">
{block}
</StyledScanLink>
</Text>
Expand Down
122 changes: 112 additions & 10 deletions apps/web/src/views/Voting/components/CastVoteModal/MainView.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { useMemo } from 'react'
import BigNumber from 'bignumber.js'
import { useTranslation } from '@pancakeswap/localization'
import {
IconButton,
Text,
Skeleton,
Button,
AutoRenewIcon,
Button,
ChevronRightIcon,
Message,
Flex,
IconButton,
Message,
RocketIcon,
Skeleton,
Text,
} from '@pancakeswap/uikit'
import { useTranslation } from '@pancakeswap/localization'
import { formatNumber } from '@pancakeswap/utils/formatBalance'
import BigNumber from 'bignumber.js'
import { useActiveChainId } from 'hooks/useActiveChainId'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { useMemo } from 'react'
import { getBlockExploreLink } from 'utils'
import { MyVeCakeCard } from 'views/CakeStaking/components/MyVeCakeCard'
import TextEllipsis from '../TextEllipsis'
import { VotingBoxBorder, VotingBoxCardInner, ModalInner } from './styles'
import { StyledScanLink } from './DetailsView'
import { ModalInner, VotingBoxBorder, VotingBoxCardInner } from './styles'
import { CastVoteModalProps } from './types'

interface MainViewProps {
Expand All @@ -35,6 +39,104 @@ interface MainViewProps {
onDismiss: CastVoteModalProps['onDismiss']
}

type VeMainViewProps = {
vote?: {
label: string
value: number
}
isLoading?: boolean
isPending?: boolean
isError?: boolean
total: number
disabled?: boolean
veCakeBalance?: number
onConfirm?: () => void
onDismiss?: CastVoteModalProps['onDismiss']
block: number
}

export const VeMainView = ({
vote,
total,
isPending,
isLoading,
isError,
onConfirm,
onDismiss,
disabled,
block,
veCakeBalance,
}: VeMainViewProps) => {
const { t } = useTranslation()

const { chainId } = useActiveChainId()

return (
<>
<ModalInner>
{vote ? (
<>
<Text color="secondary" mb="8px" textTransform="uppercase" fontSize="12px" bold>
{t('Voting For')}
</Text>
<TextEllipsis bold fontSize="20px" mb="8px" title={vote.label}>
{vote.label}
</TextEllipsis>
</>
) : null}

<Text color="secondary" textTransform="uppercase" bold fontSize="14px">
{t('Your voting power at block')}
<StyledScanLink useBscCoinFallback href={getBlockExploreLink(block, 'block', chainId)} ml="8px">
{block}
</StyledScanLink>
</Text>
{isLoading && !isError ? (
<Skeleton height="64px" mb="12px" />
) : isError ? (
<Message variant="danger" mb="12px">
<Text color="text">{t('Error occurred, please try again later')}</Text>
</Message>
) : (
<>
<br />
<MyVeCakeCard type="row" value={!veCakeBalance ? '0' : String(veCakeBalance)} />
<br />
<Text color="textSubtle" fontSize="14px">
{t(
'Your voting power is determined by the number of veCAKE you have at the block detailed above. CAKE held in other places does NOT contribute to your voting power.',
)}
</Text>
<br />
{onConfirm && (
<Text fontSize="14px" color="textSubtle">
{t('Once confirmed, voting action cannot be undone.')}
</Text>
)}
</>
)}
</ModalInner>
{onConfirm && (
<Button
isLoading={isPending}
endIcon={isPending ? <AutoRenewIcon spin color="currentColor" /> : null}
disabled={disabled || isLoading || total === 0}
width="100%"
mb="8px"
onClick={onConfirm}
>
{t('Confirm Vote')}
</Button>
)}
{onDismiss && (
<Button variant="secondary" width="100%" onClick={onDismiss}>
{t('Cancel')}
</Button>
)}
</>
)
}

const MainView: React.FC<React.PropsWithChildren<MainViewProps>> = ({
vote,
total,
Expand All @@ -54,7 +156,7 @@ const MainView: React.FC<React.PropsWithChildren<MainViewProps>> = ({
const hasLockedCake = lockedCakeBalance > 0

const isBoostingExpired = useMemo(() => {
return lockedEndTime !== 0 && new BigNumber(blockTimestamp?.toString()).gte(lockedEndTime)
return lockedEndTime !== 0 && new BigNumber(blockTimestamp?.toString() ?? 0).gte(lockedEndTime)
}, [blockTimestamp, lockedEndTime])

const hasBoosted = hasLockedCake && !isBoostingExpired
Expand Down
57 changes: 38 additions & 19 deletions apps/web/src/views/Voting/components/CastVoteModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useTranslation } from '@pancakeswap/localization'
import { Box, Modal, useToast } from '@pancakeswap/uikit'
import { useAccount, useWalletClient } from 'wagmi'
import snapshot from '@snapshot-labs/snapshot.js'
import useTheme from 'hooks/useTheme'
import { useState } from 'react'
import { PANCAKE_SPACE } from 'views/Voting/config'
import { VECAKE_VOTING_POWER_BLOCK } from 'views/Voting/helpers'
import { useAccount, useWalletClient } from 'wagmi'
import useGetVotingPower from '../../hooks/useGetVotingPower'
import DetailsView from './DetailsView'
import MainView from './MainView'
import MainView, { VeMainView } from './MainView'
import { CastVoteModalProps, ConfirmVoteView } from './types'

const hub = 'https://hub.snapshot.org'
Expand Down Expand Up @@ -39,10 +40,11 @@ const CastVoteModal: React.FC<React.PropsWithChildren<CastVoteModalProps>> = ({
ifoPoolBalance,
lockedCakeBalance,
lockedEndTime,
veCakeBalance,
} = useGetVotingPower(block)

const isStartView = view === ConfirmVoteView.MAIN
const handleBack = isStartView ? null : () => setView(ConfirmVoteView.MAIN)
const handleBack = isStartView ? undefined : () => setView(ConfirmVoteView.MAIN)
const handleViewDetails = () => setView(ConfirmVoteView.DETAILS)

const title = {
Expand All @@ -51,7 +53,7 @@ const CastVoteModal: React.FC<React.PropsWithChildren<CastVoteModalProps>> = ({
}

const handleDismiss = () => {
onDismiss()
onDismiss?.()
}

const handleConfirmVote = async () => {
Expand All @@ -61,7 +63,7 @@ const CastVoteModal: React.FC<React.PropsWithChildren<CastVoteModalProps>> = ({
getSigner: () => {
return {
_signTypedData: (domain, types, message) =>
signer.signTypedData({
signer?.signTypedData({
account,
domain,
types,
Expand All @@ -72,6 +74,10 @@ const CastVoteModal: React.FC<React.PropsWithChildren<CastVoteModalProps>> = ({
},
}

if (!account) {
return
}

await client.vote(web3 as any, account, {
space: PANCAKE_SPACE,
choice: vote.value,
Expand Down Expand Up @@ -101,20 +107,33 @@ const CastVoteModal: React.FC<React.PropsWithChildren<CastVoteModalProps>> = ({
headerBackground={theme.colors.gradientCardHeader}
>
<Box mb="24px">
{view === ConfirmVoteView.MAIN && (
<MainView
vote={vote}
isError={isError}
isLoading={isLoading}
isPending={isPending}
total={total}
lockedCakeBalance={lockedCakeBalance}
lockedEndTime={lockedEndTime}
onConfirm={handleConfirmVote}
onViewDetails={handleViewDetails}
onDismiss={handleDismiss}
/>
)}
{view === ConfirmVoteView.MAIN &&
(!block || BigInt(block) >= VECAKE_VOTING_POWER_BLOCK ? (
<VeMainView
block={block}
vote={vote}
total={total}
isPending={isPending}
isLoading={isLoading}
isError={isError}
veCakeBalance={veCakeBalance}
onConfirm={handleConfirmVote}
onDismiss={handleDismiss}
/>
) : (
<MainView
vote={vote}
isError={isError}
isLoading={isLoading}
isPending={isPending}
total={total}
lockedCakeBalance={lockedCakeBalance}
lockedEndTime={lockedEndTime}
onConfirm={handleConfirmVote}
onViewDetails={handleViewDetails}
onDismiss={handleDismiss}
/>
))}
{view === ConfirmVoteView.DETAILS && (
<DetailsView
total={total}
Expand Down
35 changes: 21 additions & 14 deletions apps/web/src/views/Voting/components/VoteDetailsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Box, Flex, InjectedModalProps, Modal, Button, Spinner } from '@pancakeswap/uikit'
import { useTranslation } from '@pancakeswap/localization'
import { Box, Button, Flex, InjectedModalProps, Modal, Spinner } from '@pancakeswap/uikit'
import useTheme from 'hooks/useTheme'
import { VECAKE_VOTING_POWER_BLOCK } from '../helpers'
import useGetVotingPower from '../hooks/useGetVotingPower'
import DetailsView from './CastVoteModal/DetailsView'
import { VeMainView } from './CastVoteModal/MainView'

interface VoteDetailsModalProps extends InjectedModalProps {
block: number
Expand All @@ -21,11 +23,12 @@ const VoteDetailsModal: React.FC<React.PropsWithChildren<VoteDetailsModalProps>>
ifoPoolBalance,
lockedCakeBalance,
lockedEndTime,
veCakeBalance,
} = useGetVotingPower(block)
const { theme } = useTheme()

const handleDismiss = () => {
onDismiss()
onDismiss?.()
}

return (
Expand All @@ -37,18 +40,22 @@ const VoteDetailsModal: React.FC<React.PropsWithChildren<VoteDetailsModalProps>>
</Flex>
) : (
<>
<DetailsView
total={total}
cakeBalance={cakeBalance}
cakeVaultBalance={cakeVaultBalance}
cakePoolBalance={cakePoolBalance}
poolsBalance={poolsBalance}
ifoPoolBalance={ifoPoolBalance}
cakeBnbLpBalance={cakeBnbLpBalance}
lockedCakeBalance={lockedCakeBalance}
lockedEndTime={lockedEndTime}
block={block}
/>
{!block || BigInt(block) >= VECAKE_VOTING_POWER_BLOCK ? (
<VeMainView block={block} total={total} veCakeBalance={veCakeBalance} />
) : (
<DetailsView
total={total}
cakeBalance={cakeBalance}
cakeVaultBalance={cakeVaultBalance}
cakePoolBalance={cakePoolBalance}
poolsBalance={poolsBalance}
ifoPoolBalance={ifoPoolBalance}
cakeBnbLpBalance={cakeBnbLpBalance}
lockedCakeBalance={lockedCakeBalance}
lockedEndTime={lockedEndTime}
block={block}
/>
)}
<Button variant="secondary" onClick={onDismiss} width="100%" mt="16px">
{t('Close')}
</Button>
Expand Down
Loading

2 comments on commit b9e7329

@vercel
Copy link

@vercel vercel bot commented on b9e7329 Dec 15, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

uikit – ./packages/uikit

uikit-git-develop.pancake.run
uikit.pancake.run

@vercel
Copy link

@vercel vercel bot commented on b9e7329 Dec 15, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.