diff --git a/packages/contracts/src/constants.ts b/packages/contracts/src/constants.ts index 8d7b9ff66cb..6636c6ed87a 100644 --- a/packages/contracts/src/constants.ts +++ b/packages/contracts/src/constants.ts @@ -59,7 +59,7 @@ export const TS_AGGREGATOR_TOKEN_TRANSFER_PROXY_CONTRACT_MAINNET = // export const RFOX_PROXY_CONTRACT: Address = '0x1094c4a99fce60e69ffe75849309408f1262d304' // export const RFOX_LP_PROXY_CONTRACT: Address = '0x4843373F380aDf80C779cD4dEDC6452E414f7634' -export const RFOX_PROXY_CONTRACT: Address = '0xac2a4fd70bcd8bab0662960455c363735f0e2b56' +export const RFOX_PROXY_CONTRACT: Address = '0xaC2a4fD70BCD8Bab0662960455c363735f0e2b56' export const RFOX_LP_PROXY_CONTRACT: Address = '0x83B51B7605d2E277E03A7D6451B1efc0e5253A2F' export const RFOX_REWARD_RATE = 1n * 10n ** 27n export const RFOX_WAD = 1n * 10n ** 18n diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json index 2e805bed8f2..ade71b994fd 100644 --- a/src/assets/translations/en/main.json +++ b/src/assets/translations/en/main.json @@ -2645,15 +2645,15 @@ "noSupportedChainsDescription": "Add %{chainLabel} or connect to a wallet that supports it.", "confirmAndBridge": "Confirm & Bridge", "myStakedBalance": "My Staked Balance", - "myStakedBalanceHelper": "The amount of FOX you have staked", + "myStakedBalanceHelper": "The amount of %{symbol} you have staked", "pendingRewardsBalance": "Pending Rewards Balance", "pendingRewardsBalanceHelper": "This is only an estimate of your pending rewards based on epoch to date revenues. All accounting is done at epoch end and cannot be calculated exactly before the end of the month", "lifetimeRewards": "Lifetime Rewards", - "lifetimeRewardsHelper": "Lifetime rewards is the cumulative rewards from staking your FOX", + "lifetimeRewardsHelper": "The total rewards you have earned from staking your %{symbol} to date", "timeInPool": "Time in Pool", "timeInPoolHelper": "Time in pool is the longest living deposit into rFOX", "myPosition": "My Position", - "positionDetails": "Position details", + "positionDetails": "Position Details", "mySymbolPosition": "My %{symbol} Position", "faq": { "title": "FAQ", @@ -2687,10 +2687,11 @@ } }, "totals": "rFOX Totals", - "totalStaked": "Total FOX Staked", + "totalStaked": "Total %{symbol} Staked", + "totalStakedHelper": "The total amount of %{symbol} staked", "totalFeesCollected": "Total Fees Collected (This Epoch)", - "emissionsPool": "Emissions Pool (This Epoch)", - "emissionsPoolHelper": "Emissions Pool (This Epoch)", + "emissionsPool": "%{symbol} Emissions Pool (This Epoch)", + "emissionsPoolHelper": "The total amount of rewards to be distributed %{symbol} stakers", "foxBurnAmount": "FOX Burn Amount (This Epoch)", "pendingDistribution": "Pending Distribution" }, diff --git a/src/pages/Fox/components/RFOXSection.tsx b/src/pages/Fox/components/RFOXSection.tsx index 44477b1f649..695b1544a3a 100644 --- a/src/pages/Fox/components/RFOXSection.tsx +++ b/src/pages/Fox/components/RFOXSection.tsx @@ -144,6 +144,7 @@ export const RFOXSection = () => { isLoading: isStakingBalanceCryptoPrecisionQueryLoading, isFetching: isStakingBalanceCryptoPrecisionFetching, } = useStakingInfoQuery({ + stakingAssetId, stakingAssetAccountAddress, select: selectStakingBalanceCryptoPrecision, }) @@ -159,6 +160,7 @@ export const RFOXSection = () => { isLoading: isCurrentEpochRewardsCryptoBaseUnitQueryLoading, isFetching: isCurrentEpochRewardsCryptoBaseUnitFetching, } = useCurrentEpochRewardsQuery({ + stakingAssetId, stakingAssetAccountAddress, currentEpochMetadata: currentEpochMetadataResult.data, }) @@ -179,6 +181,7 @@ export const RFOXSection = () => { isLoading: isLifetimeRewardsQueryLoading, isFetching: isLifetimeRewardsFetching, } = useLifetimeRewardsQuery({ + stakingAssetId, stakingAssetAccountAddress, }) @@ -191,6 +194,7 @@ export const RFOXSection = () => { isLoading: isTimeInPoolQueryLoading, isFetching: isTimeInPoolFetching, } = useTimeInPoolQuery({ + stakingAssetId, stakingAssetAccountAddress, select: timeInPoolSeconds => timeInPoolSeconds === 0n ? 'N/A' : formatSecondsToDuration(Number(timeInPoolSeconds)), diff --git a/src/pages/Fox/components/RFOXSimulator.tsx b/src/pages/Fox/components/RFOXSimulator.tsx index 648422e1421..b994c9ea979 100644 --- a/src/pages/Fox/components/RFOXSimulator.tsx +++ b/src/pages/Fox/components/RFOXSimulator.tsx @@ -8,7 +8,7 @@ import { Amount } from 'components/Amount/Amount' import { Text } from 'components/Text' import { bnOrZero } from 'lib/bignumber/bignumber' import { fromBaseUnit } from 'lib/math' -import { selectLastEpoch } from 'pages/RFOX/helpers' +import { getStakingContract, selectLastEpoch } from 'pages/RFOX/helpers' import { useEpochHistoryQuery } from 'pages/RFOX/hooks/useEpochHistoryQuery' import { useTotalStakedQuery } from 'pages/RFOX/hooks/useGetTotalStaked' import { selectAssetById, selectUsdRateByAssetId } from 'state/slices/selectors' @@ -44,6 +44,7 @@ export const RFOXSimulator = ({ stakingAssetId }: RFOXSimulatorProps) => { const runeAsset = useAppSelector(state => selectAssetById(state, thorchainAssetId)) const totalStakedCryptoResult = useTotalStakedQuery({ + stakingAssetId, select: (totalStaked: bigint) => { return bnOrZero(fromBaseUnit(totalStaked.toString(), stakingAsset?.precision ?? 0)).toFixed(2) }, @@ -72,16 +73,18 @@ export const RFOXSimulator = ({ stakingAssetId }: RFOXSimulatorProps) => { const estimatedRewards = useMemo(() => { if (!lastEpoch) return - if (!stakingAsset) return if (!poolShare) return if (!runeUsdPrice) return + const distributionRate = + lastEpoch.detailsByStakingContract[getStakingContract(stakingAssetId)].distributionRate + return bnOrZero(shapeShiftRevenue) - .times(lastEpoch.distributionRate) + .times(distributionRate) .times(poolShare) .div(runeUsdPrice) .toFixed(2) - }, [lastEpoch, shapeShiftRevenue, runeUsdPrice, stakingAsset, poolShare]) + }, [lastEpoch, shapeShiftRevenue, runeUsdPrice, stakingAssetId, poolShare]) if (!(runeAsset && stakingAsset)) return null diff --git a/src/pages/RFOX/RFOX.tsx b/src/pages/RFOX/RFOX.tsx index e4f96693c44..be6741da3f4 100644 --- a/src/pages/RFOX/RFOX.tsx +++ b/src/pages/RFOX/RFOX.tsx @@ -1,9 +1,6 @@ import type { StackDirection } from '@chakra-ui/react' import { Stack } from '@chakra-ui/react' -import { foxOnArbitrumOneAssetId } from '@shapeshiftoss/caip' import { Main } from 'components/Layout/Main' -import { selectAssetById } from 'state/slices/selectors' -import { useAppSelector } from 'state/store' import { Faq } from './components/Faq/Faq' import { Overview } from './components/Overview/Overview' @@ -15,27 +12,19 @@ const direction: StackDirection = { base: 'column-reverse', xl: 'row' } const maxWidth = { base: '100%', md: '450px' } const mainPaddingBottom = { base: 16, md: 8 } -const stakingAssetId = foxOnArbitrumOneAssetId - const rFOXHeader = -export const RFOX: React.FC = () => { - const stakingAsset = useAppSelector(state => selectAssetById(state, stakingAssetId)) - - if (!stakingAsset) return null - - return ( -
- - - - - - - - - +export const RFOX: React.FC = () => ( +
+ + + + + + + + -
- ) -} +
+
+) diff --git a/src/pages/RFOX/components/AddressSelection.tsx b/src/pages/RFOX/components/AddressSelection.tsx index 067c501d224..d5a7fecd0d5 100644 --- a/src/pages/RFOX/components/AddressSelection.tsx +++ b/src/pages/RFOX/components/AddressSelection.tsx @@ -78,6 +78,7 @@ export const AddressSelection: FC = ({ const { stakingAssetId, stakingAssetAccountId } = useRFOXContext() const { data: currentRuneAddress } = useStakingInfoQuery({ + stakingAssetId, stakingAssetAccountAddress: stakingAssetAccountId ? fromAccountId(stakingAssetAccountId).account : undefined, diff --git a/src/pages/RFOX/components/ChangeAddress/ChangeAddressConfirm.tsx b/src/pages/RFOX/components/ChangeAddress/ChangeAddressConfirm.tsx index 11a5555fb61..7997e99f479 100644 --- a/src/pages/RFOX/components/ChangeAddress/ChangeAddressConfirm.tsx +++ b/src/pages/RFOX/components/ChangeAddress/ChangeAddressConfirm.tsx @@ -31,7 +31,7 @@ import { buildAndBroadcast, createBuildCustomTxInput, } from 'lib/utils/evm' -import { getRfoxProxyContract } from 'pages/RFOX/helpers' +import { getStakingContract } from 'pages/RFOX/helpers' import { selectAccountNumberByAccountId, selectAssetById, @@ -154,7 +154,7 @@ export const ChangeAddressConfirm: React.FC< adapter, data: callData, value: '0', - to: getRfoxProxyContract(confirmedQuote.stakingAssetId), + to: getStakingContract(confirmedQuote.stakingAssetId), wallet, }) diff --git a/src/pages/RFOX/components/Claim/ClaimConfirm.tsx b/src/pages/RFOX/components/Claim/ClaimConfirm.tsx index c5ca9e55d6b..cf79188cb65 100644 --- a/src/pages/RFOX/components/Claim/ClaimConfirm.tsx +++ b/src/pages/RFOX/components/Claim/ClaimConfirm.tsx @@ -35,7 +35,7 @@ import { buildAndBroadcast, createBuildCustomTxInput, } from 'lib/utils/evm' -import { getRfoxProxyContract } from 'pages/RFOX/helpers' +import { getStakingContract } from 'pages/RFOX/helpers' import { selectAccountNumberByAccountId, selectAssetById, @@ -133,7 +133,7 @@ export const ClaimConfirm: FC & ClaimCo adapter, data: callData, value: '0', - to: getRfoxProxyContract(stakingAsset.assetId), + to: getStakingContract(stakingAsset.assetId), wallet, }) @@ -156,7 +156,7 @@ export const ClaimConfirm: FC & ClaimCo const claimFeesQueryInput = useMemo( () => ({ - to: getRfoxProxyContract(claimQuote.stakingAssetId), + to: getStakingContract(claimQuote.stakingAssetId), from: stakingAssetAccountAddress, chainId: fromAssetId(claimQuote.stakingAssetId).chainId, accountNumber: stakingAssetAccountNumber, diff --git a/src/pages/RFOX/components/Overview/EmissionsPool.tsx b/src/pages/RFOX/components/Overview/EmissionsPool.tsx new file mode 100644 index 00000000000..8910eb9582a --- /dev/null +++ b/src/pages/RFOX/components/Overview/EmissionsPool.tsx @@ -0,0 +1,61 @@ +import type { AssetId } from '@shapeshiftoss/caip' +import { thorchainAssetId } from '@shapeshiftoss/caip' +import { bn } from '@shapeshiftoss/chain-adapters' +import { useMemo } from 'react' +import { useTranslate } from 'react-polyglot' +import { fromBaseUnit } from 'lib/math' +import { getStakingContract } from 'pages/RFOX/helpers' +import { useAffiliateRevenueQuery } from 'pages/RFOX/hooks/useAffiliateRevenueQuery' +import { useCurrentEpochMetadataQuery } from 'pages/RFOX/hooks/useCurrentEpochMetadataQuery' +import { selectAssetById, selectMarketDataByAssetIdUserCurrency } from 'state/slices/selectors' +import { useAppSelector } from 'state/store' + +import { StatItem } from './StatItem' + +type EmissionsPoolProps = { + stakingAssetId: AssetId +} + +export const EmissionsPool: React.FC = ({ stakingAssetId }) => { + const translate = useTranslate() + + const runeAsset = useAppSelector(state => selectAssetById(state, thorchainAssetId)) + const runeAssetMarketData = useAppSelector(state => + selectMarketDataByAssetIdUserCurrency(state, thorchainAssetId), + ) + + const stakingAsset = useAppSelector(state => selectAssetById(state, stakingAssetId)) + + const currentEpochMetadataResult = useCurrentEpochMetadataQuery() + + const affiliateRevenueResult = useAffiliateRevenueQuery({ + startTimestamp: currentEpochMetadataResult.data?.epochStartTimestamp, + endTimestamp: currentEpochMetadataResult.data?.epochEndTimestamp, + select: (totalRevenue: bigint) => { + return bn(fromBaseUnit(totalRevenue.toString(), runeAsset?.precision ?? 0)) + .times(runeAssetMarketData.price) + .toFixed(2) + }, + }) + + const emissionsPoolUserCurrency = useMemo(() => { + if (!affiliateRevenueResult.data) return + if (!currentEpochMetadataResult.data) return + + const distributionRate = + currentEpochMetadataResult.data.distributionRateByStakingContract[ + getStakingContract(stakingAssetId) + ] + + return bn(affiliateRevenueResult.data).times(distributionRate).toFixed(2) + }, [affiliateRevenueResult, currentEpochMetadataResult, stakingAssetId]) + + return ( + + ) +} diff --git a/src/pages/RFOX/components/Overview/Overview.tsx b/src/pages/RFOX/components/Overview/Overview.tsx index 5ffd8b58309..3a45471fb34 100644 --- a/src/pages/RFOX/components/Overview/Overview.tsx +++ b/src/pages/RFOX/components/Overview/Overview.tsx @@ -1,70 +1,37 @@ import { Card, CardBody, CardHeader, Divider } from '@chakra-ui/react' -import { foxEthLpArbitrumAssetId, fromAccountId } from '@shapeshiftoss/caip' +import { fromAccountId } from '@shapeshiftoss/caip' import { useMemo } from 'react' -import { selectStakingBalance } from 'pages/RFOX/helpers' +import { RFOX_STAKING_ASSET_IDS } from 'pages/RFOX/constants' import { useRFOXContext } from 'pages/RFOX/hooks/useRfoxContext' -import { useStakingInfoQuery } from 'pages/RFOX/hooks/useStakingInfoQuery' -import { selectAssetById } from 'state/slices/selectors' -import { useAppSelector } from 'state/store' import { StakingInfo } from './StakingInfo' import { Stats } from './Stats' export const Overview: React.FC = () => { - const { stakingAssetId, stakingAssetAccountId } = useRFOXContext() - const stakingAsset = useAppSelector(state => selectAssetById(state, stakingAssetId)) + const { stakingAssetAccountId } = useRFOXContext() const stakingAssetAccountAddress = useMemo( () => (stakingAssetAccountId ? fromAccountId(stakingAssetAccountId).account : undefined), [stakingAssetAccountId], ) - const stakingBalanceCryptoBaseUnitResult = useStakingInfoQuery({ - stakingAssetAccountAddress, - stakingAssetId, - select: selectStakingBalance, - }) - - const lpStakingBalanceCryptoBaseUnitResult = useStakingInfoQuery({ - stakingAssetAccountAddress, - stakingAssetId: foxEthLpArbitrumAssetId, - select: selectStakingBalance, - }) - - const stakingBalanceCryptoBaseUnitLoading = useMemo(() => { - return ( - stakingBalanceCryptoBaseUnitResult.isLoading || stakingBalanceCryptoBaseUnitResult.isFetching - ) - }, [stakingBalanceCryptoBaseUnitResult]) - - const lpStakingBalanceCryptoBaseUnitLoading = useMemo(() => { - return ( - lpStakingBalanceCryptoBaseUnitResult.isLoading || - lpStakingBalanceCryptoBaseUnitResult.isFetching - ) - }, [lpStakingBalanceCryptoBaseUnitResult]) - - if (!stakingAsset) return null - return ( - - - + {RFOX_STAKING_ASSET_IDS.map((stakingAssetId, i) => ( + <> + + {i < RFOX_STAKING_ASSET_IDS.length - 1 && ( + + )} + + ))} - + ) diff --git a/src/pages/RFOX/components/Overview/StakingInfo.tsx b/src/pages/RFOX/components/Overview/StakingInfo.tsx index 61eaef663bf..d542e3546e6 100644 --- a/src/pages/RFOX/components/Overview/StakingInfo.tsx +++ b/src/pages/RFOX/components/Overview/StakingInfo.tsx @@ -18,13 +18,16 @@ import { useTranslate } from 'react-polyglot' import { useHistory } from 'react-router' import { Amount } from 'components/Amount/Amount' import { AssetIcon } from 'components/AssetIcon' +import { HelperTooltip } from 'components/HelperTooltip/HelperTooltip' import { Text } from 'components/Text' import { fromBaseUnit } from 'lib/math' import { formatSecondsToDuration } from 'lib/utils/time' +import { selectStakingBalance } from 'pages/RFOX/helpers' import { useCurrentApyQuery } from 'pages/RFOX/hooks/useCurrentApyQuery' import { useCurrentEpochMetadataQuery } from 'pages/RFOX/hooks/useCurrentEpochMetadataQuery' import { useCurrentEpochRewardsQuery } from 'pages/RFOX/hooks/useCurrentEpochRewardsQuery' import { useLifetimeRewardsQuery } from 'pages/RFOX/hooks/useLifetimeRewardsQuery' +import { useStakingInfoQuery } from 'pages/RFOX/hooks/useStakingInfoQuery' import { useTimeInPoolQuery } from 'pages/RFOX/hooks/useTimeInPoolQuery' import { selectAssetById } from 'state/slices/selectors' import { useAppSelector } from 'state/store' @@ -36,8 +39,6 @@ const gridColumns = { base: 1, md: 2 } type StakingInfoProps = { stakingAssetId: AssetId stakingAssetAccountAddress: string | undefined - stakingBalanceCryptoBaseUnit: string | undefined - isStakingBalanceCryptoBaseUnitLoading: boolean } const pairProps = { @@ -47,51 +48,47 @@ const pairProps = { export const StakingInfo: React.FC = ({ stakingAssetId, stakingAssetAccountAddress, - stakingBalanceCryptoBaseUnit, - isStakingBalanceCryptoBaseUnitLoading, }) => { const history = useHistory() const translate = useTranslate() + const stakingAsset = useAppSelector(state => selectAssetById(state, stakingAssetId)) - const thorchainAsset = useAppSelector(state => selectAssetById(state, thorchainAssetId)) + const runeAsset = useAppSelector(state => selectAssetById(state, thorchainAssetId)) - const { - data: apy, - isLoading: isApyQueryLoading, - isFetching: isApyFetching, - } = useCurrentApyQuery({ stakingAssetId }) + const currentApyResult = useCurrentApyQuery({ stakingAssetId }) + const currentEpochMetadataResult = useCurrentEpochMetadataQuery() - const isApyLoading = useMemo(() => { - return isApyQueryLoading || isApyFetching - }, [isApyQueryLoading, isApyFetching]) + const stakingBalanceCryptoBaseUnitResult = useStakingInfoQuery({ + stakingAssetId, + stakingAssetAccountAddress, + select: selectStakingBalance, + }) const stakingBalanceCryptoPrecision = useMemo(() => { - if (!stakingBalanceCryptoBaseUnit) return - return fromBaseUnit(stakingBalanceCryptoBaseUnit, stakingAsset?.precision ?? 0) - }, [stakingAsset, stakingBalanceCryptoBaseUnit]) - - const currentEpochMetadataResult = useCurrentEpochMetadataQuery() + if (!stakingBalanceCryptoBaseUnitResult.data) return + return fromBaseUnit(stakingBalanceCryptoBaseUnitResult.data, stakingAsset?.precision ?? 0) + }, [stakingAsset, stakingBalanceCryptoBaseUnitResult]) - // @TODO: Add stakingAssetId to useCurrentEpochRewardsQuery const currentEpochRewardsCryptoBaseUnitResult = useCurrentEpochRewardsQuery({ + stakingAssetId, stakingAssetAccountAddress, currentEpochMetadata: currentEpochMetadataResult.data, }) - // @TODO: Add stakingAssetId to lifetimeRewardsQuery const lifetimeRewardsCryptoBaseUnitResult = useLifetimeRewardsQuery({ + stakingAssetId, stakingAssetAccountAddress, }) const timeInPoolHumanResult = useTimeInPoolQuery({ - stakingAssetAccountAddress, stakingAssetId, + stakingAssetAccountAddress, select: timeInPoolSeconds => timeInPoolSeconds === 0n ? 'N/A' : formatSecondsToDuration(Number(timeInPoolSeconds)), }) const handleGetAssetClick = useCallback( - (assetId: AssetId) => { + (assetId: AssetId) => () => { history.push(`/trade/${assetId}`) }, [history], @@ -112,11 +109,10 @@ export const StakingInfo: React.FC = ({ {translate('RFOX.mySymbolPosition', { symbol: stakingAsset?.symbol ?? '' })} - + {stakingBalanceCryptoPrecision === '0' ? (