-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement delegation fetching and table
- Loading branch information
Showing
9 changed files
with
226 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { useMemo } from 'react'; | ||
import { FullWidthSpinner } from 'src/components/animation/Spinner'; | ||
import { SolidButton } from 'src/components/buttons/SolidButton'; | ||
import { PLACEHOLDER_BAR_CHART_ITEM, StackedBarChart } from 'src/components/charts/StackedBarChart'; | ||
import { sortAndCombineChartData } from 'src/components/charts/chartData'; | ||
import { HeaderAndSubheader } from 'src/components/layout/HeaderAndSubheader'; | ||
import { formatNumberString } from 'src/components/numbers/Amount'; | ||
import { DelegationAmount } from 'src/features/delegation/types'; | ||
import { TransactionFlowType } from 'src/features/transactions/TransactionFlowType'; | ||
import { useTransactionModal } from 'src/features/transactions/TransactionModal'; | ||
import { ValidatorGroupLogoAndName } from 'src/features/validators/ValidatorGroupLogo'; | ||
import { ValidatorGroup } from 'src/features/validators/types'; | ||
import { tableClasses } from 'src/styles/common'; | ||
import { fromWei } from 'src/utils/amount'; | ||
import { bigIntSum, percent } from 'src/utils/math'; | ||
import { objKeys, objLength } from 'src/utils/objects'; | ||
|
||
// TODO pass in addressToDelegatee data here | ||
export function DelegationsTable({ | ||
delegateeToAmount, | ||
addressToDelegatee, | ||
}: { | ||
delegateeToAmount?: AddressTo<DelegationAmount>; | ||
addressToDelegatee?: AddressTo<ValidatorGroup>; | ||
}) { | ||
const showTxModal = useTransactionModal(TransactionFlowType.Delegate); | ||
|
||
const { chartData, tableData } = useMemo(() => { | ||
if (!delegateeToAmount || !objLength(delegateeToAmount)) { | ||
return { tableData: [], chartData: [PLACEHOLDER_BAR_CHART_ITEM] }; | ||
} | ||
|
||
const total = fromWei( | ||
bigIntSum(Object.values(delegateeToAmount).map((amount) => amount.expected)), | ||
); | ||
|
||
const tableData = objKeys(delegateeToAmount) | ||
.map((address) => { | ||
const amount = fromWei(delegateeToAmount[address].expected); | ||
const percentage = total ? percent(amount, total) : 0; | ||
const name = addressToDelegatee?.[address]?.name || 'Unknown Delegatee'; | ||
return { address, name, amount, percentage }; | ||
}) | ||
.sort((a, b) => b.amount - a.amount); | ||
|
||
const chartData = sortAndCombineChartData( | ||
tableData.map(({ address, amount, percentage }) => ({ | ||
label: addressToDelegatee?.[address]?.name || 'Unknown Delegatee', | ||
value: amount, | ||
percentage, | ||
})), | ||
); | ||
return { chartData, tableData }; | ||
}, [delegateeToAmount, addressToDelegatee]); | ||
|
||
if (!delegateeToAmount) { | ||
return <FullWidthSpinner>Loading delegation data</FullWidthSpinner>; | ||
} | ||
|
||
if (!objLength(delegateeToAmount)) { | ||
return ( | ||
<HeaderAndSubheader | ||
header="No funds delegated" | ||
subHeader={`You currently have no delegations. You can delegate voting power to contribute to Celo governance.`} | ||
className="my-10" | ||
> | ||
<SolidButton onClick={() => showTxModal()}>Delegate CELO</SolidButton> | ||
</HeaderAndSubheader> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="mt-4 space-y-2"> | ||
<StackedBarChart data={chartData} /> | ||
<table className="w-full"> | ||
<thead> | ||
<tr> | ||
<th className={tableClasses.th}>Name</th> | ||
<th className={tableClasses.th}>Amount</th> | ||
<th className={tableClasses.th}>%</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{tableData.map(({ address, name, amount, percentage }) => ( | ||
<tr key={address}> | ||
<td className={tableClasses.td}> | ||
<ValidatorGroupLogoAndName address={address} name={name} /> | ||
</td> | ||
<td className={tableClasses.td}>{formatNumberString(amount, 2) + ' CELO'}</td> | ||
<td className={tableClasses.td}>{percentage + '%'}</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export interface DelegationAmount { | ||
expected: bigint; | ||
real: bigint; | ||
} | ||
|
||
export interface DelegationBalances { | ||
percentDelegated: bigint; | ||
delegateeToAmount: AddressTo<DelegationAmount>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { lockedGoldABI } from '@celo/abis'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { useToastError } from 'src/components/notifications/useToastError'; | ||
import { Addresses } from 'src/config/contracts'; | ||
import { DelegationBalances } from 'src/features/delegation/types'; | ||
import { logger } from 'src/utils/logger'; | ||
import { MulticallReturnType, PublicClient } from 'viem'; | ||
import { usePublicClient } from 'wagmi'; | ||
|
||
export function useDelegationBalances(address?: Address) { | ||
const publicClient = usePublicClient(); | ||
|
||
const { isLoading, isError, error, data, refetch } = useQuery({ | ||
queryKey: ['useDelegationBalances', publicClient, address], | ||
queryFn: async () => { | ||
if (!address || !publicClient) return null; | ||
logger.debug('Fetching delegation balances'); | ||
return fetchDelegationBalances(publicClient, address); | ||
}, | ||
gcTime: 10 * 60 * 1000, // 10 minutes | ||
staleTime: 1 * 60 * 1000, // 1 minute | ||
}); | ||
|
||
useToastError(error, 'Error fetching delegation balances'); | ||
|
||
return { | ||
isLoading, | ||
isError, | ||
delegations: data, | ||
refetch, | ||
}; | ||
} | ||
|
||
async function fetchDelegationBalances( | ||
publicClient: PublicClient, | ||
address: Address, | ||
): Promise<DelegationBalances> { | ||
const result: DelegationBalances = { | ||
percentDelegated: 0n, | ||
delegateeToAmount: {}, | ||
}; | ||
|
||
// First fetch the list of delegatees addresses | ||
const delegateeAddresses = await publicClient.readContract({ | ||
address: Addresses.LockedGold, | ||
abi: lockedGoldABI, | ||
functionName: 'getDelegateesOfDelegator', | ||
args: [address], | ||
}); | ||
|
||
// If there are none, stop here | ||
if (!delegateeAddresses.length) return result; | ||
|
||
// Fetch the fraction delegated | ||
const delegatorPercent = await publicClient.readContract({ | ||
address: Addresses.LockedGold, | ||
abi: lockedGoldABI, | ||
functionName: 'getAccountTotalDelegatedFraction', | ||
args: [address], | ||
}); | ||
result.percentDelegated = delegatorPercent; | ||
|
||
// Prepare a list of account, delegatee tuples | ||
const accountAndDelegatee = delegateeAddresses.map((a) => [address, a]); | ||
|
||
// @ts-ignore TODO Bug with viem 2.0 multicall types | ||
const delegatedAmounts: MulticallReturnType<any> = await publicClient.multicall({ | ||
contracts: accountAndDelegatee.map(([acc, del]) => ({ | ||
address: Addresses.LockedGold, | ||
abi: lockedGoldABI, | ||
functionName: 'getDelegatorDelegateeExpectedAndRealAmount', | ||
args: [acc, del], | ||
})), | ||
}); | ||
|
||
for (let i = 0; i < delegateeAddresses.length; i++) { | ||
const delegateeAddress = delegateeAddresses[i]; | ||
const amounts = delegatedAmounts[i]; | ||
if (amounts.status !== 'success') throw new Error('Delegated amount call failed'); | ||
const [expected, real] = amounts.result as [bigint, bigint]; | ||
result.delegateeToAmount[delegateeAddress] = { | ||
expected, | ||
real, | ||
}; | ||
} | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters