Skip to content

Commit

Permalink
feat(cr_v2): estimated rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscotobar authored and jurajpiar committed Nov 12, 2024
1 parent 7f9d548 commit 1b628c5
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 8 deletions.
97 changes: 97 additions & 0 deletions src/app/collective-rewards/rewards/EstimatedRewards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useCycleContext } from '@/app/collective-rewards/metrics/context/CycleContext'
import {
formatMetrics,
getLastCycleRewards,
MetricsCard,
MetricsCardTitle,
MetricsCardWithSpinner,
TokenMetricsCardRow,
useGetNotifyRewardLogs,
useGetRewardsCoinbase,
useGetRewardsERC20,
useGetRewardShares,
useGetTotalPotentialReward,
} from '@/app/collective-rewards/rewards'
import { useHandleErrors } from '@/app/collective-rewards/utils'
import { formatBalanceToHuman } from '@/app/user/Balances/balanceUtils'
import { usePricesContext } from '@/shared/context/PricesContext'
import { FC, useEffect, useState } from 'react'
import { Address } from 'viem'
import { useGetPerTokenRewards } from './hooks/useGetPerTokenRewards'
import { withSpinner } from '@/components/LoadingSpinner/withLoadingSpinner'

type Token = {
symbol: string
address: Address
}

type TokenRewardsProps = {
gauge: Address
currency?: string
token: Token & { id: 'rif' | 'rbtc' }
}

const TokenRewards: FC<TokenRewardsProps> = ({ gauge, token: { id, symbol }, currency = 'USD' }) => {
const { [id]: tokenRewards } = useGetPerTokenRewards()
const [rewards, setRewards] = useState<bigint>(0n)
const [rewardsError, setRewardsError] = useState<Error | null>(null)
const [isRewardsLoading, setIsRewardsLoading] = useState(true)

useEffect(() => {
if (tokenRewards && symbol in tokenRewards) {
setRewards(tokenRewards.data ?? 0n)
}
if (tokenRewards && tokenRewards.error) {
setRewardsError(tokenRewards.error)
}
if (tokenRewards) {
setIsRewardsLoading(tokenRewards.isLoading)
}
}, [tokenRewards, symbol])

const {
data: totalPotentialRewards,
isLoading: totalPotentialRewardsLoading,
error: totalPotentialRewardsError,
} = useGetTotalPotentialReward()
const {
data: rewardShares,
isLoading: rewardSharesLoading,
error: rewardSharesError,
} = useGetRewardShares(gauge)

const error = rewardsError ?? totalPotentialRewardsError ?? rewardSharesError
useHandleErrors({ error, title: 'Error loading estimated rewards' })

const { prices } = usePricesContext()

const estimatedRewards =
rewards && rewardShares && totalPotentialRewards ? (rewards * rewardShares) / totalPotentialRewards : 0n
const estimatedRewardsInHuman = Number(formatBalanceToHuman(estimatedRewards))
const price = prices[symbol]?.price ?? 0
const { amount, fiatAmount } = formatMetrics(estimatedRewardsInHuman, price, symbol, currency)

return withSpinner(TokenMetricsCardRow)({
amount,
fiatAmount,
isLoading: isRewardsLoading || totalPotentialRewardsLoading || rewardSharesLoading,
})
}

type EstimatedRewardsProps = {
gauge: Address
currency?: string
data: {
[token: string]: Token
}
}

export const EstimatedRewards: FC<EstimatedRewardsProps> = ({ data: { rif, rbtc }, ...rest }) => {
return (
<MetricsCard borderless>
<MetricsCardTitle title="Estimated rewards" data-testid="EstimatedRewards" />
<TokenRewards {...rest} token={{ ...rif, id: 'rif' }} />
<TokenRewards {...rest} token={{ ...rbtc, id: 'rbtc' }} />
</MetricsCard>
)
}
7 changes: 4 additions & 3 deletions src/app/collective-rewards/rewards/MyRewards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import {
LastCycleRewards,
ClaimableRewards,
AllTimeRewards,
EstimatedRewards,
} from '@/app/collective-rewards/rewards'
import { CycleContextProvider } from '@/app/collective-rewards/metrics'
import { PricesContextProvider } from '@/shared/context/PricesContext'
import { getCoinBaseAddress } from '@/app/collective-rewards/utils'
import { getCoinbaseAddress } from '@/app/collective-rewards/utils'
import { tokenContracts } from '@/lib/contracts'
import { Popover } from '@/components/Popover'
import { Button } from '@/components/Button'
Expand All @@ -28,7 +29,7 @@ export const Rewards: FC<{ builder: Address; gauge: Address }> = ({ builder, gau
symbol: 'RIF',
},
rbtc: {
address: getCoinBaseAddress(),
address: getCoinbaseAddress(),
symbol: 'RBTC',
},
}
Expand All @@ -48,7 +49,7 @@ export const Rewards: FC<{ builder: Address; gauge: Address }> = ({ builder, gau
<PricesContextProvider>
<ClaimableRewards builder={builder} gauge={gauge} data={data} />
<LastCycleRewards gauge={gauge} data={data} />
<div>Estimated Rewards</div>
<EstimatedRewards gauge={gauge} data={data} />
<AllTimeRewards gauge={gauge} data={data} />
<div>All time share</div>
<Popover
Expand Down
21 changes: 21 additions & 0 deletions src/app/collective-rewards/rewards/hooks/getRewardShares.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GaugeAbi } from '@/lib/abis/v2/GaugeAbi'
import { Address } from 'viem'
import { useReadContract } from 'wagmi'

export const useGetRewardShares = (gauge?: Address) => {
const { data, isLoading, error } = useReadContract({
address: gauge!,
abi: GaugeAbi,
functionName: 'rewardShares',
query: {
refetchInterval: 30_000,
enabled: !!gauge,
},
})

return {
data,
isLoading,
error,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BackersManagerAbi } from '@/lib/abis/v2/BackersManagerAbi'
import { BackersManagerAddress } from '@/lib/contracts'
import { useReadContract } from 'wagmi'

export const useGetTotalPotentialReward = () => {
const { data, isLoading, error } = useReadContract({
address: BackersManagerAddress,
abi: BackersManagerAbi,
functionName: 'totalPotentialReward',
query: {
refetchInterval: 30_000,
},
})

return {
data,
isLoading,
error,
}
}
4 changes: 4 additions & 0 deletions src/app/collective-rewards/rewards/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export * from './useGetBuilderRewardsClaimedLogs'
export * from './useGetRewardDistributedLogs'
export * from './useGetTokenProjectedReward'
export * from './useGetNotifyRewardLogs'
export * from './getRewardShares'
export * from './getTotalPotentialRewards'
export * from './useGetRewardsCoinbase'
export * from './useGetRewardsERC20'
12 changes: 12 additions & 0 deletions src/app/collective-rewards/rewards/hooks/useGetPerTokenRewards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BackersManagerAbi } from '@/lib/abis/v2/BackersManagerAbi'
import { UseReadContractReturnType } from 'wagmi'
import { useGetRewardsCoinbase } from './useGetRewardsCoinbase'
import { useGetRewardsERC20 } from './useGetRewardsERC20'

export const useGetPerTokenRewards = (): Record<
'rif' | 'rbtc',
UseReadContractReturnType<typeof BackersManagerAbi, 'rewardsERC20' | 'rewardsCoinbase'>
> => ({
rif: useGetRewardsERC20(),
rbtc: useGetRewardsCoinbase(),
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query'
import { fetchRewardDistributedCached } from '@/app/collective-rewards/actions'
import { Address, isAddressEqual, parseEventLogs } from 'viem'
import { SimplifiedRewardDistributorAbi } from '@/lib/abis/SimplifiedRewardDistributorAbi'
import { resolveCollectiveRewardToken } from '@/app/collective-rewards/utils/getCoinBaseAddress'
import { resolveCollectiveRewardToken } from '@/app/collective-rewards/utils/getCoinbaseAddress'
import { AVERAGE_BLOCKTIME } from '@/lib/constants'

export const useGetRewardDistributedLogs = (rewardToken?: Address, builder?: Address) => {
Expand Down
15 changes: 15 additions & 0 deletions src/app/collective-rewards/rewards/hooks/useGetRewardsCoinbase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BackersManagerAbi } from '@/lib/abis/v2/BackersManagerAbi'
import { AVERAGE_BLOCKTIME } from '@/lib/constants'
import { BackersManagerAddress } from '@/lib/contracts'
import { useReadContract, UseReadContractReturnType } from 'wagmi'

export const useGetRewardsCoinbase = () => {
return useReadContract({
address: BackersManagerAddress,
abi: BackersManagerAbi,
functionName: 'rewardsCoinbase',
query: {
refetchInterval: AVERAGE_BLOCKTIME,
},
})
}
16 changes: 16 additions & 0 deletions src/app/collective-rewards/rewards/hooks/useGetRewardsERC20.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BackersManagerAbi } from '@/lib/abis/v2/BackersManagerAbi'
import { AVERAGE_BLOCKTIME } from '@/lib/constants'
import { BackersManagerAddress } from '@/lib/contracts'
import { ReadContractReturnType } from 'viem/actions'
import { useReadContract, UseReadContractReturnType } from 'wagmi'

export const useGetRewardsERC20 = () => {
return useReadContract({
address: BackersManagerAddress,
abi: BackersManagerAbi,
functionName: 'rewardsERC20',
query: {
refetchInterval: AVERAGE_BLOCKTIME,
},
})
}
1 change: 1 addition & 0 deletions src/app/collective-rewards/rewards/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './MyRewards'
export * from './LastCycleRewards'
export * from './ClaimableRewards'
export * from './AllTimeRewards'
export * from './EstimatedRewards'
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getAddress, keccak256, toUtf8Bytes, ZeroAddress } from 'ethers'
import { Address } from 'viem'

export const getCoinBaseAddress = () => {
export const getCoinbaseAddress = () => {
return getAddress(keccak256(toUtf8Bytes('COINBASE_ADDRESS')).slice(26)) as Address
}

export const resolveCollectiveRewardToken = (rewardToken: Address) =>
rewardToken === ZeroAddress ? getCoinBaseAddress() : rewardToken
rewardToken === ZeroAddress ? getCoinbaseAddress() : rewardToken
2 changes: 1 addition & 1 deletion src/app/collective-rewards/utils/getLastCycleRewards.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Address, isAddressEqual, parseEventLogs } from 'viem'
import { SimplifiedRewardDistributorAbi } from '@/lib/abis/SimplifiedRewardDistributorAbi'
import { getPreviousCycle } from '@/app/collective-rewards/utils/getPreviousCycle'
import { resolveCollectiveRewardToken } from '@/app/collective-rewards/utils/getCoinBaseAddress'
import { resolveCollectiveRewardToken } from '@/app/collective-rewards/utils/getCoinbaseAddress'

type EventLog = ReturnType<
typeof parseEventLogs<typeof SimplifiedRewardDistributorAbi, true, 'RewardDistributed'>
Expand Down
2 changes: 1 addition & 1 deletion src/app/collective-rewards/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './handleErrors'
export * from './getCoinBaseAddress'
export * from './getCoinbaseAddress'

0 comments on commit 1b628c5

Please sign in to comment.