Skip to content

Commit

Permalink
refactor: optimize DelegatorsList component and hooks for it
Browse files Browse the repository at this point in the history
  • Loading branch information
katamarinaki committed Aug 27, 2024
1 parent 794474a commit 39f4981
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 145 deletions.
3 changes: 0 additions & 3 deletions modules/delegation/constants.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { BigNumber } from 'ethers'

export const SNAPSHOT_LIDO_SPACE_NAME =
'0x6c69646f2d736e617073686f742e657468000000000000000000000000000000' // lido-snapshot.eth

export const DELEGATORS_PAGE_SIZE = 10
export const DELEGATORS_FETCH_SIZE = 50
export const DELEGATORS_FETCH_TOTAL = 200
export const VP_MIN_TO_SHOW = BigNumber.from(10).pow(16)
74 changes: 45 additions & 29 deletions modules/delegation/hooks/useDelegators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,47 @@ import { CHAINS } from '@lido-sdk/constants'
import { useLidoSWR } from '@lido-sdk/react'
import { ContractVoting } from 'modules/blockChain/contracts'
import { useWeb3 } from 'modules/blockChain/hooks/useWeb3'
import {
DELEGATORS_FETCH_SIZE,
DELEGATORS_FETCH_TOTAL,
VP_MIN_TO_SHOW,
} from '../constants'
import { DELEGATORS_FETCH_SIZE, DELEGATORS_FETCH_TOTAL } from '../constants'
import { BigNumber } from 'ethers'

type DelegatorData = { address: string; balance: BigNumber }

type DelegatorsData = {
nonZeroDelegators: DelegatorData[]
totalVotingPower: BigNumber
notFetchedDelegatorsCount: number
}

/*
SWR data hook to fetch first N delegators of the current wallet address.
Returns up to DELEGATORS_FETCH_TOTAL delegators with their voting power.
The list contains only delegators with voting power greater than 0.
*/
export function useDelegators() {
const { walletAddress, chainId } = useWeb3()
const voting = ContractVoting.useRpc()
return useLidoSWR(
walletAddress
? [`swr:useDelegatorsPaginatedList`, chainId, walletAddress]
: null,

const { data, initialLoading, loading, error } = useLidoSWR<DelegatorsData>(
walletAddress ? [`swr:useDelegators`, chainId, walletAddress] : null,
async (_key: string, _chainId: CHAINS, _walletAddress: string) => {
const delegatorsCount = (
const totalDelegatorsCount = (
await voting.getDelegatedVotersCount(_walletAddress)
).toNumber()

if (delegatorsCount === 0) {
if (totalDelegatorsCount === 0) {
return {
totalCount: 0,
fetchedCount: 0,
wealthyCount: 0,
list: [] as { address: string; balance: BigNumber }[],
fetchedValue: 0,
nonZeroDelegators: [] as DelegatorData[],
totalVotingPower: BigNumber.from(0),
notFetchedDelegatorsCount: 0,
}
}

const fetchLimit = Math.min(delegatorsCount, DELEGATORS_FETCH_TOTAL)
const fetchLimit = Math.min(totalDelegatorsCount, DELEGATORS_FETCH_TOTAL)
const fetchCount = Math.ceil(fetchLimit / DELEGATORS_FETCH_SIZE)
const fetchNumbers = Array(fetchCount).fill(0)

const delegators: { address: string; balance: BigNumber }[] = []
let fetchedValue = BigNumber.from(0)
const delegators: DelegatorData[] = []
let totalVotingPower = BigNumber.from(0)

await Promise.all(
fetchNumbers.map(async (_, fetchIndex) => {
Expand All @@ -59,23 +65,33 @@ export function useDelegators() {
address: delegator,
balance: delegatorsAtPageBalances[index],
})
fetchedValue = fetchedValue.add(delegatorsAtPageBalances[index])
totalVotingPower = totalVotingPower.add(
delegatorsAtPageBalances[index],
)
})
}),
)

const fetchedCount = delegators.length

const wealthyDelegators = delegators.filter(delegator =>
delegator.balance.gt(VP_MIN_TO_SHOW),
const nonZeroDelegators = delegators.filter(delegator =>
delegator.balance.gt(0),
)

return {
totalCount: delegatorsCount,
fetchedCount,
wealthyCount: wealthyDelegators.length,
list: wealthyDelegators,
fetchedValue,
nonZeroDelegators,
totalVotingPower,
notFetchedDelegatorsCount: totalDelegatorsCount - delegators.length,
}
},
)

return {
data: {
nonZeroDelegators: data?.nonZeroDelegators ?? [],
totalVotingPower: data?.totalVotingPower ?? BigNumber.from(0),
notFetchedDelegatorsCount: data?.notFetchedDelegatorsCount ?? 0,
} as DelegatorsData,
initialLoading,
loading,
error,
}
}
16 changes: 0 additions & 16 deletions modules/delegation/hooks/useDelegatorsInfo.ts

This file was deleted.

21 changes: 0 additions & 21 deletions modules/delegation/hooks/useDelegatorsPaginatedList.ts

This file was deleted.

68 changes: 39 additions & 29 deletions modules/delegation/ui/DelegatorsList/DelegatorsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,37 @@ import {
CounterBadge,
TitleWrap,
} from './DelegatorsListStyle'
import { DelegatorsListPage } from './DelegatorsListPage'
import { useDelegatorsInfo } from 'modules/delegation/hooks/useDelegatorsInfo'
import { PageLoader } from 'modules/shared/ui/Common/PageLoader'
import {
DELEGATORS_FETCH_TOTAL,
DELEGATORS_PAGE_SIZE,
} from 'modules/delegation/constants'
import { useGovernanceSymbol } from 'modules/tokens/hooks/useGovernanceSymbol'
import { InfoLabel } from 'modules/shared/ui/Common/InfoRow'
import { getEtherscanAddressLink } from '@lido-sdk/helpers'
import { AragonVoting } from 'modules/blockChain/contractAddresses'
import { formatBalance } from 'modules/blockChain/utils/formatBalance'
import { useDelegators } from 'modules/delegation/hooks/useDelegators'
import { ExternalLink } from 'modules/shared/ui/Common/ExternalLink'
import { useGovernanceBalance } from 'modules/tokens/hooks/useGovernanceBalance'
import { DelegatorsListItem } from './DelegatorsListItem'

const DAO_OPS_FORUM_LINK =
'https://research.lido.fi/new-message?groupname=DAO_Ops'

export function DelegatorsList() {
const { isWalletConnected, chainId } = useWeb3()
const [pageCount, setPageCount] = useState(1)
const { data: governanceSymbol } = useGovernanceSymbol()
const { data: governanceToken } = useGovernanceBalance()

const { data, initialLoading } = useDelegatorsInfo()
const { data, initialLoading } = useDelegators()

const pages = useMemo(() => {
const result = []
for (let i = 0; i < pageCount; i++) {
result.push(<DelegatorsListPage pageNumber={i} key={i} />)
const delegatorsToShow = useMemo(() => {
if (!data.nonZeroDelegators.length) {
return []
}
return result
}, [pageCount])

return data.nonZeroDelegators.slice(0, pageCount * DELEGATORS_PAGE_SIZE)
}, [data.nonZeroDelegators, pageCount])

if (!isWalletConnected) {
return (
Expand All @@ -50,7 +54,9 @@ export function DelegatorsList() {
return <PageLoader />
}

if (data.wealthyCount === 0) {
const nonZeroDelegatorsCount = data.nonZeroDelegators.length

if (nonZeroDelegatorsCount === 0) {
return (
<Wrap $empty={true}>
<Text size="sm" color="secondary">
Expand All @@ -60,34 +66,40 @@ export function DelegatorsList() {
)
}

const outOfList = data.totalCount - data.fetchedCount

return (
<Wrap>
<TitleWrap>
<InfoLabel>Delegated</InfoLabel>
<CounterBadge>
{formatBalance(data.fetchedValue || 0)} {governanceSymbol}
{formatBalance(data.totalVotingPower)} {governanceToken?.symbol}
</CounterBadge>
<InfoLabel>
from {data.wealthyCount} address
{data.wealthyCount > 1 ? 'es' : ''} on-chain
from {nonZeroDelegatorsCount} address
{nonZeroDelegatorsCount > 1 ? 'es' : ''} on-chain
</InfoLabel>
</TitleWrap>
<DeelgatorsListStyled>
{pages}
{data.wealthyCount > pageCount * DELEGATORS_PAGE_SIZE && (
{delegatorsToShow.map(delegator => (
<DelegatorsListItem
key={delegator.address}
address={delegator.address}
balance={delegator.balance}
governanceSymbol={governanceToken?.symbol}
/>
))}
{nonZeroDelegatorsCount > pageCount * DELEGATORS_PAGE_SIZE && (
<ShowMoreButton onClick={() => setPageCount(count => count + 1)}>
Show More
</ShowMoreButton>
)}
</DeelgatorsListStyled>
{outOfList > 0 && (
<Text size="xs" color="secondary">
The voting power list displays addresses with a positive LDO balance
from the first{` ${DELEGATORS_FETCH_TOTAL} `}delegators. You have
{` ${outOfList} `}more delegator{outOfList > 1 ? 's' : ''} who were
not included in the list. To see all your delegators, use the{' '}
{data.notFetchedDelegatorsCount > 0 && (
<Text size="xxs" color="secondary">
This list displays addresses with a positive {governanceToken?.symbol}{' '}
balance from the first {DELEGATORS_FETCH_TOTAL} delegators. You have{' '}
{data.notFetchedDelegatorsCount} more delegator
{data.notFetchedDelegatorsCount > 1 ? 's' : ''} who were not included
in the list. To see all your delegators, use the{' '}
<Link
href={
getEtherscanAddressLink(chainId, AragonVoting[chainId] ?? '') +
Expand All @@ -97,10 +109,8 @@ export function DelegatorsList() {
Voting contract
</Link>
. If needed, contact the{' '}
<Link href="https://research.lido.fi/new-message?groupname=DAO_Ops">
DAO Ops{' '}
</Link>{' '}
on the forum for assistance.
<ExternalLink href={DAO_OPS_FORUM_LINK}>DAO Ops </ExternalLink> on the
forum for assistance.
</Text>
)}
</Wrap>
Expand Down
37 changes: 37 additions & 0 deletions modules/delegation/ui/DelegatorsList/DelegatorsListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Identicon, Text, trimAddress } from '@lidofinance/lido-ui'
import { AddressPop } from 'modules/shared/ui/Common/AddressPop'
import {
AddressBadgeWrap,
DelegatorsListItemStyled,
} from './DelegatorsListStyle'
import { formatBalance } from 'modules/blockChain/utils/formatBalance'
import { BigNumber } from 'ethers'

type Props = {
address: string
balance: BigNumber
governanceSymbol: string | undefined
}

export function DelegatorsListItem({
address,
balance,
governanceSymbol,
}: Props) {
return (
<DelegatorsListItemStyled key={address}>
<AddressPop address={address}>
<AddressBadgeWrap>
<Identicon address={address} diameter={20} />
<Text as="span" size="xxs">
{/* {(ensNameList && ensNameList[i]) || trimAddress(address, 4)} */}
{trimAddress(address, 4)}
</Text>
</AddressBadgeWrap>
</AddressPop>
<Text size="xs">
{formatBalance(balance)} {governanceSymbol ?? ''}
</Text>
</DelegatorsListItemStyled>
)
}
46 changes: 0 additions & 46 deletions modules/delegation/ui/DelegatorsList/DelegatorsListPage.tsx

This file was deleted.

3 changes: 2 additions & 1 deletion modules/delegation/ui/DelegatorsList/DelegatorsListStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const Wrap = styled.div<{ $empty?: boolean }>`
gap: 12px;
max-width: 496px;
margin: 0 auto;
text-align: center;
${({ $empty }) =>
$empty &&
Expand Down Expand Up @@ -46,7 +47,7 @@ export const AddressBadgeWrap = styled.span`
}
`

export const DelegatorsListItem = styled.div`
export const DelegatorsListItemStyled = styled.div`
padding: 12px 20px;
display: flex;
align-items: center;
Expand Down

0 comments on commit 39f4981

Please sign in to comment.