diff --git a/src/app/collective-rewards/rewards/EstimatedRewards.tsx b/src/app/collective-rewards/rewards/EstimatedRewards.tsx new file mode 100644 index 00000000..81234cac --- /dev/null +++ b/src/app/collective-rewards/rewards/EstimatedRewards.tsx @@ -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 = ({ gauge, token: { id, symbol }, currency = 'USD' }) => { + const { [id]: tokenRewards } = useGetPerTokenRewards() + const [rewards, setRewards] = useState(0n) + const [rewardsError, setRewardsError] = useState(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 = ({ data: { rif, rbtc }, ...rest }) => { + return ( + + + + + + ) +} diff --git a/src/app/collective-rewards/rewards/MyRewards.tsx b/src/app/collective-rewards/rewards/MyRewards.tsx index 4897ae78..f70c3698 100644 --- a/src/app/collective-rewards/rewards/MyRewards.tsx +++ b/src/app/collective-rewards/rewards/MyRewards.tsx @@ -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' @@ -28,7 +29,7 @@ export const Rewards: FC<{ builder: Address; gauge: Address }> = ({ builder, gau symbol: 'RIF', }, rbtc: { - address: getCoinBaseAddress(), + address: getCoinbaseAddress(), symbol: 'RBTC', }, } @@ -48,7 +49,7 @@ export const Rewards: FC<{ builder: Address; gauge: Address }> = ({ builder, gau -
Estimated Rewards
+
All time share
{ + const { data, isLoading, error } = useReadContract({ + address: gauge, + abi: GaugeAbi, + functionName: 'rewardShares', + query: { + refetchInterval: AVERAGE_BLOCKTIME, + }, + }) + + return { + data, + isLoading, + error, + } +} diff --git a/src/app/collective-rewards/rewards/hooks/getTotalPotentialRewards.ts b/src/app/collective-rewards/rewards/hooks/getTotalPotentialRewards.ts new file mode 100644 index 00000000..1c7017ec --- /dev/null +++ b/src/app/collective-rewards/rewards/hooks/getTotalPotentialRewards.ts @@ -0,0 +1,21 @@ +import { BackersManagerAbi } from '@/lib/abis/v2/BackersManagerAbi' +import { AVERAGE_BLOCKTIME } from '@/lib/constants' +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: AVERAGE_BLOCKTIME, + }, + }) + + return { + data, + isLoading, + error, + } +} diff --git a/src/app/collective-rewards/rewards/hooks/index.ts b/src/app/collective-rewards/rewards/hooks/index.ts index fe0a563d..d5ae0147 100644 --- a/src/app/collective-rewards/rewards/hooks/index.ts +++ b/src/app/collective-rewards/rewards/hooks/index.ts @@ -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' diff --git a/src/app/collective-rewards/rewards/hooks/useGetPerTokenRewards.ts b/src/app/collective-rewards/rewards/hooks/useGetPerTokenRewards.ts new file mode 100644 index 00000000..21fe0610 --- /dev/null +++ b/src/app/collective-rewards/rewards/hooks/useGetPerTokenRewards.ts @@ -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 +> => ({ + rif: useGetRewardsERC20(), + rbtc: useGetRewardsCoinbase(), +}) diff --git a/src/app/collective-rewards/rewards/hooks/useGetRewardDistributedLogs.ts b/src/app/collective-rewards/rewards/hooks/useGetRewardDistributedLogs.ts index 21674a39..f2e849b0 100644 --- a/src/app/collective-rewards/rewards/hooks/useGetRewardDistributedLogs.ts +++ b/src/app/collective-rewards/rewards/hooks/useGetRewardDistributedLogs.ts @@ -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) => { diff --git a/src/app/collective-rewards/rewards/hooks/useGetRewardsCoinbase.ts b/src/app/collective-rewards/rewards/hooks/useGetRewardsCoinbase.ts new file mode 100644 index 00000000..073be48a --- /dev/null +++ b/src/app/collective-rewards/rewards/hooks/useGetRewardsCoinbase.ts @@ -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, + }, + }) +} diff --git a/src/app/collective-rewards/rewards/hooks/useGetRewardsERC20.ts b/src/app/collective-rewards/rewards/hooks/useGetRewardsERC20.ts new file mode 100644 index 00000000..36572490 --- /dev/null +++ b/src/app/collective-rewards/rewards/hooks/useGetRewardsERC20.ts @@ -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, + }, + }) +} diff --git a/src/app/collective-rewards/rewards/index.ts b/src/app/collective-rewards/rewards/index.ts index a556b0c7..c7a24d6c 100644 --- a/src/app/collective-rewards/rewards/index.ts +++ b/src/app/collective-rewards/rewards/index.ts @@ -5,3 +5,4 @@ export * from './MyRewards' export * from './LastCycleRewards' export * from './ClaimableRewards' export * from './AllTimeRewards' +export * from './EstimatedRewards' diff --git a/src/app/collective-rewards/utils/getCoinBaseAddress.ts b/src/app/collective-rewards/utils/getCoinbaseAddress.ts similarity index 71% rename from src/app/collective-rewards/utils/getCoinBaseAddress.ts rename to src/app/collective-rewards/utils/getCoinbaseAddress.ts index b663633a..d8ee32c7 100644 --- a/src/app/collective-rewards/utils/getCoinBaseAddress.ts +++ b/src/app/collective-rewards/utils/getCoinbaseAddress.ts @@ -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 diff --git a/src/app/collective-rewards/utils/getLastCycleRewards.ts b/src/app/collective-rewards/utils/getLastCycleRewards.ts index 13a25aaf..d91238d5 100644 --- a/src/app/collective-rewards/utils/getLastCycleRewards.ts +++ b/src/app/collective-rewards/utils/getLastCycleRewards.ts @@ -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 diff --git a/src/app/collective-rewards/utils/index.ts b/src/app/collective-rewards/utils/index.ts index 821fa7f4..5abf3f6e 100644 --- a/src/app/collective-rewards/utils/index.ts +++ b/src/app/collective-rewards/utils/index.ts @@ -1,2 +1,2 @@ export * from './handleErrors' -export * from './getCoinBaseAddress' +export * from './getCoinbaseAddress'