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

feat: voting list #148

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
22 changes: 2 additions & 20 deletions apps/frontend-v3/app/(app)/vebal/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
'use client'

import { TokenBalancesProvider } from '@repo/lib/modules/tokens/TokenBalancesProvider'
import { DefaultPageContainer } from '@repo/lib/shared/components/containers/DefaultPageContainer'
import { PropsWithChildren } from 'react'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
import { CrossChainSyncProvider } from '@repo/lib/modules/vebal/cross-chain/CrossChainSyncProvider'
import { TransactionStateProvider } from '@repo/lib/modules/transactions/transaction-steps/TransactionStateProvider'

export default function VebalLayout({ children }: PropsWithChildren) {
const { vebalBptToken } = useTokens()

if (!vebalBptToken) throw new Error('vebalBptToken not found')

return (
<TokenBalancesProvider initTokens={[vebalBptToken]}>
<CrossChainSyncProvider>
<TransactionStateProvider>
<DefaultPageContainer>{children}</DefaultPageContainer>
</TransactionStateProvider>
</CrossChainSyncProvider>
</TokenBalancesProvider>
)
export default async function VeBALLayout({ children }: PropsWithChildren) {
return <DefaultPageContainer minH="100vh">{children}</DefaultPageContainer>
}
24 changes: 24 additions & 0 deletions apps/frontend-v3/app/(app)/vebal/manage/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client'

import { TokenBalancesProvider } from '@repo/lib/modules/tokens/TokenBalancesProvider'
import { DefaultPageContainer } from '@repo/lib/shared/components/containers/DefaultPageContainer'
import { PropsWithChildren } from 'react'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
import { CrossChainSyncProvider } from '@repo/lib/modules/vebal/cross-chain/CrossChainSyncProvider'
import { TransactionStateProvider } from '@repo/lib/modules/transactions/transaction-steps/TransactionStateProvider'

export default function VebalLayout({ children }: PropsWithChildren) {
alter-eggo marked this conversation as resolved.
Show resolved Hide resolved
const { vebalBptToken } = useTokens()

if (!vebalBptToken) throw new Error('vebalBptToken not found')

return (
<TokenBalancesProvider initTokens={[vebalBptToken]}>
<CrossChainSyncProvider>
<TransactionStateProvider>
<DefaultPageContainer>{children}</DefaultPageContainer>
</TransactionStateProvider>
</CrossChainSyncProvider>
</TokenBalancesProvider>
)
}
2 changes: 1 addition & 1 deletion apps/frontend-v3/app/(app)/vebal/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Stack } from '@chakra-ui/react'

import NextLink from 'next/link'

export default function VebalPage() {
export default function VeBALPage() {
return (
<Stack gap="lg" maxW="200px">
<Button as={NextLink} href="/vebal/manage" size="lg" variant="primary">
Expand Down
14 changes: 14 additions & 0 deletions apps/frontend-v3/app/(app)/vebal/vote/layout.tsx
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be fetching data in this component and passing it into providers.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Metadata } from 'next'
import { PropsWithChildren } from 'react'

export const metadata: Metadata = {
title: 'Vote and earn external incentives',
description: `
Voting on pool gauges helps to direct weekly BAL liquidity mining incentives.
Voters are also eligible to earn additional 3rd party voting incentives.
`,
}

export default async function Pools({ children }: PropsWithChildren) {
return <>{children}</>
}
13 changes: 11 additions & 2 deletions apps/frontend-v3/app/(app)/vebal/vote/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Stack } from '@chakra-ui/react'
import { Skeleton } from '@chakra-ui/react'
import FadeInOnView from '@repo/lib/shared/components/containers/FadeInOnView'
import { Suspense } from 'react'
import { VoteList } from '@repo/lib/modules/vebal/vote/VoteList/VoteList'

export default function VotePage() {
return <Stack>Vote</Stack>
return (
<FadeInOnView animateOnce={false}>
<Suspense fallback={<Skeleton h="500px" w="full" />}>
<VoteList />
</Suspense>
</FadeInOnView>
)
}
1 change: 1 addition & 0 deletions packages/lib/config/config.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface ContractsConfig {
permit2?: Address
omniVotingEscrow?: Address
gaugeWorkingBalanceHelper?: Address
gaugeController?: Address
}
export interface PoolsConfig {
issues: Partial<Record<PoolIssue, string[]>>
Expand Down
1 change: 1 addition & 0 deletions packages/lib/config/networks/mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const networkConfig: NetworkConfig = {
veDelegationProxy: '0x6f5a2eE11E7a772AeB5114A20d0D7c0ff61EB8A0',
veBAL: '0xC128a9954e6c874eA3d62ce62B468bA073093F25',
omniVotingEscrow: '0x96484f2aBF5e58b15176dbF1A799627B53F13B6d',
gaugeController: '0xC128468b7Ce63eA702C1f104D55A2566b13D3ABD',
},

pools: convertHexToLowerCase({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ export default function PoolMetaBadges() {
width={20}
/>
</Badge>
<PoolListTokenPills pool={pool} px="sm" py="2" />
<PoolListTokenPills
chain={pool.chain}
displayTokens={pool.displayTokens}
Copy link
Collaborator

Choose a reason for hiding this comment

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

displayTokens are deprecated in the API.
Depending on what we need here, we can probably reuse some helpers from pool.helpers or we can implement the same logic that the api implements for displayTokens

Copy link
Contributor Author

Choose a reason for hiding this comment

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

used getPoolDisplayTokens

px="sm"
py="2"
type={pool.type}
/>
<PoolVersionTag isSmall pool={pool} />
<PoolTypeTag pool={pool} />
{hasHook && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ export function PoolListTableRow({ pool, keyValue, ...rest }: Props) {
</GridItem>
<GridItem>
<PoolListTokenPills
chain={pool.chain}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see that you replaced pool={pool} prop to spread its used fields so that you can reuse PoolListTokenPills component in the voting list.

Instead of spreading the fields: chain, displayTokens, type... into the props I would create a new TS subtype that both the PoolListTableRow and the VotingListRow can satisfy.

For example:

type PoolTokenData = {
chain: GqlChain, 
displayTokens: ...
type: ...
}

that way its's easier to understand the purpose of that group of pool fields.

displayTokens={pool.displayTokens}
Copy link
Collaborator

Choose a reason for hiding this comment

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

displayTokens is deprecated.

h={['32px', '36px']}
iconSize={20}
p={['xxs', 'sm']}
pool={pool}
pr={[1.5, 'ms']}
type={pool.type}
/>
</GridItem>
<GridItem minW="32">
Expand Down
48 changes: 25 additions & 23 deletions packages/lib/modules/pool/PoolList/PoolListTokenPills.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { Badge, BadgeProps, HStack, Text, Wrap } from '@chakra-ui/react'
import { GqlChain, GqlPoolTokenDisplay } from '@repo/lib/shared/services/api/generated/graphql'
import { PoolListItem } from '../pool.types'
import {
GqlChain,
GqlPoolTokenDisplay,
GqlPoolType,
} from '@repo/lib/shared/services/api/generated/graphql'
import { TokenIcon } from '../../tokens/TokenIcon'
import { fNum } from '@repo/lib/shared/utils/numbers'
import { isStableLike, isWeightedLike } from '../pool.helpers'
import { Pool } from '../PoolProvider'

type DisplayToken = Pick<GqlPoolTokenDisplay, 'address' | 'symbol' | 'weight'>

function WeightedTokenPills({
tokens,
chain,
iconSize = 24,
...badgeProps
}: { tokens: GqlPoolTokenDisplay[]; chain: GqlChain; iconSize?: number } & BadgeProps) {
}: { tokens: DisplayToken[]; chain: GqlChain; iconSize?: number } & BadgeProps) {
return (
<Wrap spacing="xs">
{tokens.map(token => {
Expand Down Expand Up @@ -51,7 +55,7 @@ function StableTokenPills({
chain,
iconSize = 24,
...badgeProps
}: { tokens: GqlPoolTokenDisplay[]; chain: GqlChain; iconSize?: number } & BadgeProps) {
}: { tokens: DisplayToken[]; chain: GqlChain; iconSize?: number } & BadgeProps) {
const isFirstToken = (index: number) => index === 0
const zIndices = Array.from({ length: tokens.length }, (_, index) => index).reverse()

Expand Down Expand Up @@ -90,42 +94,40 @@ function StableTokenPills({
}

type Props = {
pool: Pool | PoolListItem
type: GqlPoolType
chain: GqlChain
displayTokens: DisplayToken[]
iconSize?: number
}

export function PoolListTokenPills({ pool, iconSize = 24, ...badgeProps }: Props & BadgeProps) {
const shouldUseWeightedPills = isWeightedLike(pool.type)
const shouldUseStablePills = isStableLike(pool.type)
export function PoolListTokenPills({
chain,
type,
displayTokens,
iconSize = 24,
...badgeProps
}: Props & BadgeProps) {
const shouldUseWeightedPills = isWeightedLike(type)
const shouldUseStablePills = isStableLike(type)

if (shouldUseStablePills) {
return (
<StableTokenPills
chain={pool.chain}
iconSize={iconSize}
tokens={pool.displayTokens}
{...badgeProps}
/>
<StableTokenPills chain={chain} iconSize={iconSize} tokens={displayTokens} {...badgeProps} />
)
}

if (shouldUseWeightedPills) {
return (
<WeightedTokenPills
chain={pool.chain}
chain={chain}
iconSize={iconSize}
tokens={pool.displayTokens}
tokens={displayTokens}
{...badgeProps}
/>
)
}

return (
<WeightedTokenPills
chain={pool.chain}
iconSize={iconSize}
tokens={pool.displayTokens}
{...badgeProps}
/>
<WeightedTokenPills chain={chain} iconSize={iconSize} tokens={displayTokens} {...badgeProps} />
)
}
19 changes: 10 additions & 9 deletions packages/lib/modules/pool/pool.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,26 @@ export const chainToSlugMap: Record<GqlChain, ChainSlug> = {
}
export const slugToChainMap = invert(chainToSlugMap) as Record<ChainSlug, GqlChain>

function getVariant(pool: Pool | PoolListItem): PoolVariant {
function getVariant(type: GqlPoolType, protocolVersion: number | undefined): PoolVariant {
// if a pool has certain properties return a custom variant
if (pool.type === GqlPoolType.CowAmm) return PartnerVariant.cow
if (pool.protocolVersion === 3) return BaseVariant.v3
if (type === GqlPoolType.CowAmm) return PartnerVariant.cow
if (protocolVersion === 3) return BaseVariant.v3

// default variant
return BaseVariant.v2
}

/**
* Constructs path to pool detail page.
* @param {String} id Pool ID could be ID or address depending on variant.
* @param {GqlChain} chain Chain enum.
* @param {String} variant Pool variant, defaults to v2.
* @returns {String} Path to pool detail page.
*/
export function getPoolPath(pool: Pool | PoolListItem) {
const variant = getVariant(pool)
return `/pools/${chainToSlugMap[pool.chain]}/${variant}/${pool.id}`
export function getPoolPath(
params: Pick<Pool | PoolListItem, 'id' | 'chain' | 'type'> & {
protocolVersion: number | undefined
}
) {
const variant = getVariant(params.type, params.protocolVersion)
return `/pools/${chainToSlugMap[params.chain]}/${variant}/${params.id}`
}

// TODO: the following 2 functions (getAprLabel & getTotalAprLabel) most likely need revisiting somewhere in the near future and refactored to just one
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@ export function PortfolioTableRow({ pool, keyValue, veBalBoostMap, ...rest }: Pr
</GridItem>
<GridItem>
<PoolListTokenPills
chain={pool.chain}
displayTokens={pool.displayTokens}
h={['32px', '36px']}
iconSize={20}
p={['xxs', 'sm']}
pool={pool}
pr={[1.5, 'ms']}
type={pool.type}
/>
</GridItem>
<GridItem>
Expand Down
57 changes: 57 additions & 0 deletions packages/lib/modules/vebal/vote/VoteCapTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
HStack,
Text,
Popover,
PopoverTrigger,
Portal,
PopoverContent,
VStack,
} from '@chakra-ui/react'
import { VoteCapIcon } from '@repo/lib/shared/components/icons/VoteCapIcon'
import { fNum } from '@repo/lib/shared/utils/numbers'
import { VotesState } from '@repo/lib/modules/vebal/vote/vote.types'

interface Props {
relativeWeightCap: number
votesState?: VotesState
usePortal?: boolean
}

export function VoteCapTooltip({ relativeWeightCap, votesState, usePortal = true }: Props) {
const votesColor =
votesState === 'normal' ? undefined : votesState === 'close' ? 'font.warning' : 'red.400'

const voteCapText =
votesState === 'normal'
? 'vote cap'
: votesState === 'close'
? 'vote cap is close'
: 'vote cap exceeded'

const popoverContent = (
<PopoverContent bg="background.level3" minWidth={['100px', '170px']} p="sm" shadow="3xl">
<VStack alignItems="start" spacing="sm" width="full">
<Text color={votesColor ?? 'font.secondary'} fontSize="sm" fontWeight={700}>
{fNum('apr', relativeWeightCap)} {voteCapText}
</Text>
<Text color="font.secondary" fontSize="sm">
Governance by veBAL holders have set a cap on this gauge. Any votes that push the vote
above this cap will be disregarded.
</Text>
</VStack>
</PopoverContent>
)
return (
<Popover trigger="hover">
<>
<PopoverTrigger>
<HStack color={votesColor ?? 'font.secondary'}>
<VoteCapIcon height="16px" width="16px" />
</HStack>
</PopoverTrigger>

{usePortal ? <Portal>{popoverContent}</Portal> : popoverContent}
</>
</Popover>
)
}
14 changes: 14 additions & 0 deletions packages/lib/modules/vebal/vote/VoteList/VoteList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { PoolListProvider } from '@repo/lib/modules/pool/PoolList/PoolListProvider'
import { VoteListLayout } from './VoteListLayout'
import { VoteListProvider } from '@repo/lib/modules/vebal/vote/VoteList/VoteListProvider'

export async function VoteList() {
return (
<VoteListProvider>
{/* fix: remove PoolListProvider when voteFilters implemented */}
<PoolListProvider>
<VoteListLayout />
</PoolListProvider>
</VoteListProvider>
)
}
Loading
Loading