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: add erc4626 review data #171

Merged
merged 10 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions apps/beets-frontend-v3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"build": "pnpm --filter @repo/lib graphql:gen && next build",
"dev": "concurrently \"pnpm --filter @repo/lib graphql:gen --watch\" \"next dev -p 3001\"",
"dev:no-gen": "next dev -p 3001",
"gen:wagmi": "pnpm wagmi generate",
"lint": "next lint",
"lint:fix": "next lint --fix",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend-v3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"build": "pnpm --filter @repo/lib graphql:gen && next build",
"dev": "concurrently \"pnpm --filter @repo/lib graphql:gen --watch\" \"next dev\"",
"dev:no-gen": "next dev",
"gen:wagmi": "pnpm wagmi generate",
"lint": "next lint",
"lint:fix": "next lint --fix",
Expand Down
135 changes: 135 additions & 0 deletions packages/lib/modules/pool/PoolDetail/PoolInfo/Erc4626Info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { TokenIcon } from '@repo/lib/modules/tokens/TokenIcon'
import { Erc4626ReviewData, GqlToken } from '@repo/lib/shared/services/api/generated/graphql'
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverArrow,
PopoverBody,
VStack,
HStack,
Text,
Heading,
Box,
Icon,
} from '@chakra-ui/react'
import Link from 'next/link'
import { ArrowUpRight } from 'react-feather'
import { getWarnings } from '../../pool.helpers'
import { PropsWithChildren } from 'react'

type Erc4626InfoPopOverProps = {
token: GqlToken
data: Erc4626ReviewData | undefined | null
level: number
} & PropsWithChildren

type PopoverInfoBodyProps = {
data: Erc4626ReviewData
level: number
}

function PopoverInfoBody({ data, level }: PopoverInfoBodyProps) {
const warnings = getWarnings(data.warnings || [])
return (
<>
{level === 0 && (
<>
<Text fontSize="sm">
This tokenized vault has not been reviewed.
<br />
Proceed with caution.
</Text>
<Text fontSize="sm">
Learn more about{' '}
<Link href="/risks#tokenized-vault-risk" target="_blank">
<Box as="span" color="font.link">
tokenized vault risks
</Box>
</Link>
</Text>
</>
)}
{level !== 0 && (
<>
<VStack alignItems="flex-start" gap="0">
<Text color="grayText" fontSize="sm">
Review summary:
</Text>
<Text fontSize="sm">
{data.summary === 'safe' ? 'No vulnerabilities were reported' : 'Unsafe'}
</Text>
</VStack>
<VStack alignItems="flex-start" gap="0">
<Text color="grayText" fontSize="sm">
Warnings:
</Text>
{warnings.length > 0 ? (
<Text fontSize="sm">Yes, see review details</Text>
) : (
<Text fontSize="sm">
None except{' '}
<Link href="/risks#tokenized-vault-risk" target="_blank">
<Box as="span" color="font.link">
tokenized vault risks
</Box>
</Link>
</Text>
)}
</VStack>
{data.reviewFile && (
<Link
href={`https://github.com/balancer/code-review/blob/main/erc4626/${data.reviewFile}`}
target="_blank"
>
<HStack gap="xxs">
<Text color="font.link" fontSize="sm">
View review details
</Text>
<Icon as={ArrowUpRight} color="font.link" size={12} />
</HStack>
</Link>
)}
</>
)}
</>
)
}

export function Erc4626InfoPopOver({ token, data, level, children }: Erc4626InfoPopOverProps) {
const body = data ? (
<PopoverInfoBody data={data} level={level} />
) : (
<Text fontSize="sm">
Rate provider data is missing.
<br />
Proceed with caution.
</Text>
)

return (
<Popover trigger="hover">
<PopoverTrigger>{children}</PopoverTrigger>
<PopoverContent w="auto">
<PopoverArrow bg="background.level2" />
<PopoverBody>
<VStack alignItems="flex-start" spacing="ms" w="full">
<HStack w="full">
<Heading fontSize="1.125rem" variant="h4">
{token.symbol} tokenized vault
</Heading>
<TokenIcon
address={token.address}
alt={token.symbol}
chain={token.chain}
ml="auto"
size={24}
/>
</HStack>
{body}
</VStack>
</PopoverBody>
</PopoverContent>
</Popover>
)
}
89 changes: 81 additions & 8 deletions packages/lib/modules/pool/PoolDetail/PoolInfo/PoolContracts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Link,
Divider,
Text,
Tooltip,

Check warning on line 16 in packages/lib/modules/pool/PoolDetail/PoolInfo/PoolContracts.tsx

View workflow job for this annotation

GitHub Actions / Lint

'Tooltip' is defined but never used
Popover,
PopoverTrigger,
PopoverContent,
Expand All @@ -26,6 +26,7 @@
GqlPriceRateProviderData,
GqlToken,
GqlHookReviewData,
Erc4626ReviewData,
} from '@repo/lib/shared/services/api/generated/graphql'
import { Address, zeroAddress } from 'viem'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
Expand All @@ -36,6 +37,7 @@
import { getBlockExplorerAddressUrl } from '@repo/lib/shared/hooks/useBlockExplorer'
import { getWarnings } from '@repo/lib/modules/pool/pool.helpers'
import { HookInfoPopOver } from './HookInfo'
import { Erc4626InfoPopOver } from './Erc4626Info'

type RateProvider = {
tokenAddress: Address
Expand Down Expand Up @@ -86,6 +88,20 @@
)
}

function getErc4626Icon(data: Erc4626ReviewData | undefined | null, token: GqlToken) {
const hasWarnings = getWarnings(data?.warnings || []).length > 0
const hasData = !!data
const isSafe = hasData && data?.summary === 'safe'

const { icon, level } = getIconAndLevel(hasWarnings, isSafe, hasData)

return (
<Erc4626InfoPopOver data={data} level={level} token={token}>
{icon}
</Erc4626InfoPopOver>
)
}

function getHookIcon(data: GqlHookReviewData | undefined | null) {
const hasWarnings = getWarnings(data?.warnings || []).length > 0
const isSafe = !!data?.summary && data?.summary === 'safe'
Expand Down Expand Up @@ -146,6 +162,14 @@
return [...(pool.hook ? [pool.hook] : []), ...nestedHooks]
}, [pool])

const erc4626Tokens = useMemo(() => {
const erc4626Tokens = pool.poolTokens.filter(token => token.isErc4626)
const erc4626NestedTokens = pool.poolTokens.flatMap(token =>
token.nestedPool ? token.nestedPool.tokens.filter(token => token.isErc4626) : []
)
return [...(erc4626Tokens ? erc4626Tokens : []), ...erc4626NestedTokens]
}, [pool])

return (
<Card {...props}>
<VStack alignItems="flex-start" spacing="md" width="full">
Expand Down Expand Up @@ -244,14 +268,12 @@
return (
token && (
<HStack key={provider.tokenAddress}>
<Tooltip fontSize="sm" label={token.symbol} shouldWrapChildren>
<TokenIcon
address={token.address}
alt={token.address}
chain={chain}
size={16}
/>
</Tooltip>
<TokenIcon
address={token.address}
alt={token.symbol}
chain={chain}
size={16}
/>
<Link
href={getBlockExplorerAddressUrl(provider.rateProviderAddress, chain)}
key={provider.rateProviderAddress}
Expand All @@ -274,6 +296,57 @@
</GridItem>
</Grid>
)}
{erc4626Tokens.length > 0 && (
<Grid gap="sm" templateColumns={{ base: '1fr 2fr', md: '1fr 3fr' }} w="full">
<GridItem>
<Popover trigger="hover">
<PopoverTrigger>
<Text className="tooltip-dashed-underline" minW="120px" variant="secondary">
{erc4626Tokens.length === 1 ? 'Tokenized vault:' : 'Tokenized vaults:'}
</Text>
</PopoverTrigger>
<PopoverContent maxW="300px" p="sm" w="auto">
<Text fontSize="sm" variant="secondary">
ERC-4626 (tokenized vault) is a standard to optimize and unify the technical
parameters of yield-bearing vaults. It provides a standard API for tokenized
yield-bearing vaults that represent shares of a single underlying ERC-20 token.
</Text>
</PopoverContent>
</Popover>
</GridItem>
<GridItem>
<VStack alignItems="flex-start">
{erc4626Tokens.map(erc4626Token => {
const token = getToken(erc4626Token.address, chain)
return (
token && (
<HStack key={erc4626Token.address}>
<TokenIcon
address={token.address}
alt={token.symbol}
chain={chain}
size={16}
/>
<Link
href={getBlockExplorerAddressUrl(erc4626Token.address, chain)}
key={erc4626Token.address}
target="_blank"
variant="link"
>
<HStack gap="xxs">
<Text color="link">{abbreviateAddress(erc4626Token.address)}</Text>
<ArrowUpRight size={12} />
</HStack>
</Link>
{getErc4626Icon(erc4626Token.erc4626ReviewData, token)}
</HStack>
)
)
})}
</VStack>
</GridItem>
</Grid>
)}
</VStack>
</Card>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ test('queries add liquidity transaction', async () => {
expect(result.current.receivedBptUnits).toBe('7.669852124112308228')
})

test('queries add liquidity with native token', async () => {
/*
Skip until dRPC fixes issue with polygon tx queries
*/
test.skip('queries add liquidity with native token', async () => {
// https://polygonscan.com/tx/0x611a0eeeff15c2a5efc587b173fa577475134de2554a452259f112db67bd4de8
const userAddress = '0xf76142b79Db34E57852d68F9c52C0E24f7349647'
const txHash = '0x611a0eeeff15c2a5efc587b173fa577475134de2554a452259f112db67bd4de8'
Expand Down
11 changes: 11 additions & 0 deletions packages/lib/shared/services/api/pool.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,15 @@ fragment PoolTokens on GqlPoolTokenDetail {
type
tokens {
index
name
address
decimals
isErc4626
erc4626ReviewData {
reviewFile
summary
warnings
}
underlyingToken {
address
decimals
Expand Down Expand Up @@ -262,6 +268,11 @@ fragment PoolTokens on GqlPoolTokenDetail {
name
symbol
}
erc4626ReviewData {
reviewFile
summary
warnings
}
}

query GetPoolSnapshots($poolId: String!, $range: GqlPoolSnapshotDataRange!, $chainId: GqlChain!) {
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/test/anvil/anvil-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
fallBackRpc: 'https://polygon-rpc.com',
port: ANVIL_PORTS.Polygon,
// Note - this has to be >= highest blockNo used in tests
forkBlockNumber: 60496806n,
forkBlockNumber: 64747630n,
},
Sepolia: {
networkName: 'Sepolia',
Expand All @@ -74,7 +74,7 @@
When jobId is provided, the fork proxy uses this id to create a different local rpc url (e.g. `http://127.0.0.1:/port/jobId>/`
so that tests can be run in parallel (depending on the number of threads of the host machine)
*/
export const pool = Number(process.env.VITEST_POOL_ID ?? 1)

Check warning on line 77 in packages/lib/test/anvil/anvil-setup.ts

View workflow job for this annotation

GitHub Actions / Lint

VITEST_POOL_ID is not listed as a dependency in turbo.json

export function getTestRpcSetup(networkName: NetworksWithFork) {
const network = ANVIL_NETWORKS[networkName]
Expand Down
Loading