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

TOK-541: add ABI #472

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
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
8 changes: 6 additions & 2 deletions src/app/collective-rewards/metrics/Metrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getAddress } from 'viem'
import { tokenContracts } from '@/lib/contracts'
import { getCoinbaseAddress } from '@/app/collective-rewards/utils'
import { PricesContextProvider } from '@/shared/context/PricesContext'
import { ABIMetrics } from './components/ABIMetrics'

const HeaderWithBuilderButton = withBuilderButton(HeaderTitle)

Expand All @@ -35,14 +36,17 @@ export const Metrics = () => {
<PricesContextProvider>
<CycleContextProvider>
<div className="flex gap-4 w-full">
<div className="flex gap-4 h-min w-3/4">
<div className="flex gap-4 h-min w-3/5">
<CycleMetrics />
<TotalActiveBuildersMetrics />
<TotalAllocationsMetrics gauges={gauges} token={tokens.rif} />
</div>
<div className="w-1/4">
<div className="w-1/5">
<AllTimeRewardsMetrics gauges={gauges} tokens={tokens} />
</div>
<div className="w-1/5">
<ABIMetrics />
</div>
</div>
</CycleContextProvider>
</PricesContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@/app/collective-rewards/rewards'
import { withSpinner } from '@/components/LoadingSpinner/withLoadingSpinner'
import { useHandleErrors } from '@/app/collective-rewards/utils'
import { useGetTotalAllocation } from './hooks/useGetTotalAllocation'

type TotalAllocationsProps = {
gauges: Address[]
Expand All @@ -25,13 +26,11 @@ export const TotalAllocationsMetrics: FC<TotalAllocationsProps> = ({
currency = 'USD',
}) => {
const { prices } = usePricesContext()
const { data, isLoading, error } = useGaugesGetFunction(gauges, 'totalAllocation')
const { data: totalAllocations, isLoading, error } = useGetTotalAllocation(gauges)
useHandleErrors({ error, title: 'Error loading total allocations' })
const totalAllocationsInHuman = Number(formatOnchainFraction(totalAllocations))

const price = prices[symbol]?.price ?? 0

const totalAllocations = Object.values(data).reduce((acc, allocation) => acc + allocation, 0n)
const totalAllocationsInHuman = Number(formatOnchainFraction(totalAllocations))
const fiatAmount = `= ${currency} ${formatCurrency(totalAllocationsInHuman * price, currency)}`

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { MetricsCard, MetricsCardTitle, TokenMetricsCardRow } from '@/app/collective-rewards/rewards'
import { withSpinner } from '@/components/LoadingSpinner/withLoadingSpinner'
import { useGetABI } from './hooks/useGetABI'

export const ABIMetrics = () => {
const { data: abiPct, isLoading } = useGetABI()
return (
<>
<MetricsCard borderless>
<MetricsCardTitle
title="ABI %"
data-testid="abiPct"
tooltip={{
text: (
<p className="font-rootstock-sans text-sm font-normal">
The Annual Backers Incentives (%) represents an estimate of the annualized percentage of
rewards that backers could receive based on their backing allocations.
<br />
<br />
The calculation follows the formula: (1 + Rewards per stRIF per Cycle / RIF price)^26 - 1.
<br />
<br />
This estimation is dynamic and may vary based on total rewards and user activity. This data is
for informational purposes only.{' '}
</p>
),
popoverProps: {
size: 'medium',
position: 'left-bottom',
},
}}
/>
{withSpinner(
TokenMetricsCardRow,
'min-h-0 grow-0',
)({
amount: abiPct.toFixed(2),
isLoading,
})}
</MetricsCard>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import {
useGetBackersRewardPercentage,
useGetRewardsCoinbase,
useGetRewardsERC20,
} from '@/app/collective-rewards/rewards'
import { usePricesContext } from '@/shared/context/PricesContext'
import { useGetBuildersByState } from '@/app/collective-rewards/user'
import { Builder } from '../../../../types'
import { useGaugesGetFunction } from '../../../../shared'
import { useCycleContext } from '../../../context'
import { formatEther } from 'viem'

export const useGetABI = () => {
const { data: rifRewards, isLoading: rifRewardsLoading, error: rifRewardsError } = useGetRewardsERC20()
const {
data: rbtcRewards,
isLoading: rbtcRewardsLoading,
error: rbtcRewardsError,
} = useGetRewardsCoinbase()
const { data: activatedBuilders } = useGetBuildersByState<Required<Builder>>({
activated: true,
communityApproved: true,
kycApproved: true,
revoked: false,
})

const {
data: { cycleNext },
isLoading: cycleLoading,
error: cycleError,
} = useCycleContext()

const gauges = activatedBuilders.map(({ gauge }) => gauge)
const {
data: totalAllocation,
isLoading: totalAllocationLoading,
error: totalAllocationError,
} = useGaugesGetFunction(gauges, 'totalAllocation')
const sumTotalAllocation = Object.values(totalAllocation ?? {}).reduce(
(acc, value) => acc + (value ?? 0n),
1n,
)

const buildersAddress = activatedBuilders.map(({ address }) => address)
const {
data: backersRewardsPct,
isLoading: backersRewardsPctLoading,
error: backersRewardsPctError,
} = useGetBackersRewardPercentage(buildersAddress, cycleNext.toSeconds())

const weightedAverageBuilderRewardsPct =
activatedBuilders
.reduce<Array<{ allocation: bigint; current: number }>>((acc, builder) => {
const allocation = totalAllocation[builder.gauge]
const rewardPct = backersRewardsPct[builder.address]
if (allocation && rewardPct) {
acc.push({ allocation, current: rewardPct.current })
}
return acc
}, [])
.sort((a, b) => (a.allocation > b.allocation ? -1 : 1))
.slice(0, 5)
.reduce(
(acc, { allocation, current }) =>
acc + Number(((allocation * 100n) / sumTotalAllocation) * BigInt(current)),
0,
) / 100

console.log('🚀 ~ useGetABI ~ weightedAverageBuilderRewardsPct:', weightedAverageBuilderRewardsPct)

const { prices } = usePricesContext()
const rifPrice = prices.RIF?.price ?? 0
const rbtcPrice = prices.RBTC?.price ?? 0
const rifAmount = Number(formatEther(rifRewards ?? 0n))
console.log('🚀 ~ useGetABI ~ rifAmount:', rifAmount)
const rbtcAmount = Number(formatEther(rbtcRewards ?? 0n))
console.log('🚀 ~ useGetABI ~ rbtcAmount:', rbtcAmount)
const totalAllocationInEther = Number(formatEther(sumTotalAllocation))
console.log('🚀 ~ useGetABI ~ totalAllocationInEther:', totalAllocationInEther)

const cyclePayout = rifAmount * rifPrice + rbtcAmount * rbtcPrice
console.log('🚀 ~ useGetABI ~ cyclePayout:', cyclePayout)

// FIXME: verify if we need to use the cyclePayout in wei or in RIF and if we need to use bigint or Number
const rewardsPerStRIFPerCycle =
(cyclePayout * (weightedAverageBuilderRewardsPct / totalAllocationInEther / rifPrice)) / 100

console.log('🚀 ~ useGetABI ~ rewardsPerStRIFPerCycle:', rewardsPerStRIFPerCycle)

const abi = (Math.pow(1 + rewardsPerStRIFPerCycle, 26) - 1) * 100
console.log('🚀 ~ useGetABI ~ abi1:', abi)
console.log('🚀 ~ useGetABI ~ abi2:', abi.toFixed(2))

const isLoading =
rifRewardsLoading ||
rbtcRewardsLoading ||
cycleLoading ||
totalAllocationLoading ||
backersRewardsPctLoading
const error =
rifRewardsError ?? rbtcRewardsError ?? totalAllocationError ?? cycleError ?? backersRewardsPctError
return {
data: abi,
isLoading,
error,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ABIMetrics'
12 changes: 12 additions & 0 deletions src/app/collective-rewards/metrics/hooks/useGetTotalAllocation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useGaugesGetFunction } from '../../shared'

export const useGetTotalAllocation = (gauges: any[]) => {
const { data, isLoading, error } = useGaugesGetFunction(gauges, 'totalAllocation')

const totalAllocations = Object.values(data).reduce((acc, allocation) => acc + allocation, 0n)
return {
data: totalAllocations,
isLoading,
error,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const isBuilderShown = (
allocations: Allocations,
) => {
const allocation = allocations[address]
return (kycApproved && !revoked && communityApproved && !paused) || (allocation && allocation > 0n)
return (kycApproved && !revoked && communityApproved) || (allocation && allocation > 0n)
}

// FIXME: remove and use Builder and/or combination of existing types
Expand Down
4 changes: 2 additions & 2 deletions src/app/collective-rewards/rewards/components/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react'
import { FC, ReactNode } from 'react'
import { Popover, PopoverProps } from '../../../../components/Popover'
import { Button, ButtonProps } from '../../../../components/Button'

Expand All @@ -22,7 +22,7 @@ const TooltipSvg = () => (
)

export type TooltipProps = {
text: string
text: ReactNode
popoverProps?: Pick<PopoverProps, 'size' | 'position'>
}

Expand Down
3 changes: 2 additions & 1 deletion src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
disabled?: boolean
trigger?: 'click' | 'hover'
background?: 'dark' | 'light'
position?: 'top' | 'bottom' | 'right' | 'left'
position?: 'top' | 'bottom' | 'right' | 'left' | 'left-bottom'
size?: 'small' | 'medium'
hasCaret?: boolean
}
Expand Down Expand Up @@ -73,6 +73,7 @@
position === 'bottom' && 'top-full',
position === 'right' && 'left-full bottom-full',
position === 'left' && 'right-full bottom-full',
position === 'left-bottom' && 'right-full top-full',
size === 'small' && 'w-36',
size === 'medium' && 'w-96',
)}
Expand All @@ -85,7 +86,7 @@
)}
>
{content}
{hasCaret && <PopoverCaret position={position} />}

Check failure on line 89 in src/components/Popover/Popover.tsx

View workflow job for this annotation

GitHub Actions / test

Type '"bottom" | "left" | "right" | "top" | "left-bottom"' is not assignable to type '"bottom" | "left" | "right" | "top"'.
</div>
</div>
</div>
Expand Down
Loading