Skip to content

Commit

Permalink
using reward balance from the subgraph, which automatically udpates a…
Browse files Browse the repository at this point in the history
…fter rebase
  • Loading branch information
aalavandhan committed Dec 18, 2024
1 parent f76eba6 commit 9042731
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 142 deletions.
14 changes: 6 additions & 8 deletions frontend/src/components/GeyserFirst/GeyserStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@ import { DAY_IN_SEC, TOTAL_REWARDS_MSG } from '../../constants'

export const GeyserStats = () => {
const {
geyserStats: { duration, totalDeposit, totalRewards },
geyserStats: { duration, totalDepositVal, totalRewards, totalRewardsVal },
} = useContext(StatsContext)
const {
selectedGeyserInfo: {
rewardTokenInfo: { symbol: rewardTokenSymbol, price: rewardTokenPrice },
rewardTokenInfo: { symbol: rewardTokenSymbol },
},
} = useContext(GeyserContext)

const baseRewards = totalRewards * rewardTokenPrice

return (
<GeyserStatsContainer>
<Header>Geyser Stats</Header>
Expand All @@ -34,7 +32,7 @@ export const GeyserStats = () => {
<GeyserStatsBoxContainer>
<GeyserStatsBox
name="Total Deposits"
value={totalDeposit}
value={totalDepositVal}
units="USD"
interpolate={(val) => safeNumeral(val, '0,0')}
/>
Expand All @@ -43,7 +41,7 @@ export const GeyserStats = () => {
<GeyserStatsBox
containerClassName="w-full"
name="Total Rewards"
value={baseRewards}
value={totalRewardsVal}
units="USD"
interpolate={(val) => safeNumeral(val, '0,0')}
tooltipMessage={{
Expand All @@ -55,12 +53,12 @@ export const GeyserStats = () => {
rows={[
{
label: `${rewardTokenSymbol} (${safeNumeral(totalRewards, '0,0')})`,
value: `${safeNumeral(baseRewards, '0,0.00')} USD`,
value: `${safeNumeral(totalRewardsVal, '0,0.00')} USD`,
},
{ label: 'bonus (0)', value: `${safeNumeral(0, '0,0.00')} USD` },
]}
totalLabel="Total"
totalValue={`${safeNumeral(baseRewards, '0,0.00')} USD`}
totalValue={`${safeNumeral(totalRewardsVal, '0,0.00')} USD`}
/>
</div>
),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/GeyserFirst/MyStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const MyStats = () => {
setFinalAPY(Math.min(geyserAPYNew + lpAPYNew, 100000))
}, [selectedGeyser, apy])

// NOTE: removed bonus tokens
// TODO: handle bonus tokens
const baseRewards = currentReward * rewardTokenPrice
return (
<MyStatsContainer>
Expand Down
15 changes: 6 additions & 9 deletions frontend/src/components/GeyserFirst/UnstakeSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,15 @@ export const UnstakeSummary: React.FC<Props> = ({ userInput, parsedUserInput })
stakingTokenInfo: { symbol: stakingTokenSymbol, price: stakingTokenPrice },
},
} = useContext(GeyserContext)
const {
geyserStats: { bonusRewards },
computeRewardsFromUnstake,
computeRewardsShareFromUnstake,
} = useContext(StatsContext)
const { computeRewardsFromUnstake, computeRewardsShareFromUnstake } = useContext(StatsContext)

const [rewardAmount, setRewardAmount] = useState<number>(0.0)
const [rewardsShare, setRewardsShare] = useState<number>(0.0)

const unstakeUSD = parseFloat(userInput) * stakingTokenPrice
const rewardUSD = rewardAmount * rewardTokenPrice + bonusRewards.reduce((m, b) => m + rewardsShare * b.value, 0)

const rewardUSD = rewardAmount * rewardTokenPrice
// TODO: handle bonus rewards
// bonusRewards.reduce((m, b) => m + rewardsShare * b.value, 0)
useEffect(() => {
let isMounted = true
;(async () => {
Expand Down Expand Up @@ -78,12 +75,12 @@ export const UnstakeSummary: React.FC<Props> = ({ userInput, parsedUserInput })
<span>{rewardTokenSymbol}</span>
</Value>

{bonusRewards.map((b) => (
{/* {bonusRewards.map((b) => (
<Value key={b.symbol}>
<Amount>{safeNumeral(rewardsShare * b.balance, '0.000')} </Amount>
<span>{b.symbol}</span>
</Value>
))}
))} */}
</Content>
</SummaryCard>
</Container>
Expand Down
17 changes: 2 additions & 15 deletions frontend/src/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ import { Loader } from 'styling/styles'
import { toChecksumAddress } from 'web3-utils'
import TokenIcons from 'components/TokenIcons'
import { safeNumeral } from 'utils/numeral'
import { getGeyserTotalDeposit } from 'utils/stats'
import { formatUnits } from 'ethers/lib/utils'
import { getGeyserTotalDeposit, getGeyserTotalRewards } from 'utils/stats'
import { getPlatformConfig } from 'config/app'

const nowInSeconds = () => Math.round(Date.now() / 1000)

export const Home = () => {
const { geysers, getGeyserConfig, allTokensInfos, stakeAPYs } = useContext(GeyserContext)
const { selectedVault } = useContext(VaultContext)
const navigate = useNavigate()
const stakedGeysers = selectedVault ? selectedVault.locks.map((l) => l.geyser) : []
const now = nowInSeconds()
const tokensByAddress = allTokensInfos.reduce((acc, t) => {
acc[toChecksumAddress(t.address)] = t
return acc
Expand All @@ -39,16 +35,7 @@ export const Home = () => {
const apy = lpAPY + geyserAPY
const programName = extractProgramName(config.name)
const platform = getPlatformConfig(config)

let rewards = 0
if (rewardTokenInfo) {
// NOTE: This math doesn't work if AMPL is the reward token as it doesn't account for rebasing!
const rewardAmt = g.rewardSchedules
.filter((s) => parseInt(s.start, 10) + parseInt(s.duration, 10) > now)
.reduce((m, s) => m + parseFloat(formatUnits(s.rewardAmount, rewardTokenInfo.decimals)), 0)
rewards = rewardAmt * rewardTokenInfo.price
}

const rewards = rewardTokenInfo ? getGeyserTotalRewards(g, rewardTokenInfo) : 0
const isStablePool = config.name.includes('USDC') && config.name.includes('SPOT')
const poolType = `${isStablePool ? 'Stable' : 'Vol'}[${stakingTokens.join('/')}]`
return {
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export type StakingTokenInfo = TokenInfo & {

export type RewardTokenInfo = TokenInfo & {
price: number
getTotalRewards: (rewardSchedules: RewardSchedule[]) => Promise<number>
}

export type BonusTokenInfo = TokenInfo & {
Expand All @@ -114,10 +113,13 @@ export type RewardStats = {
export type GeyserStats = {
duration: number
totalDeposit: number
totalDepositVal: number
totalRewards: number
totalRewardVal: number
calcPeriodInDays: number
unlockedRewards: number
bonusRewards: RewardStats[]
bonusRewardsVal: number
}

export type VaultTokenBalance = TokenInfo & {
Expand Down
107 changes: 6 additions & 101 deletions frontend/src/utils/rewardToken.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,14 @@
import { Contract } from 'ethers'
import { formatUnits } from 'ethers/lib/utils'
import { RewardToken, MIN_IN_MS } from '../constants'
import { RewardSchedule, RewardTokenInfo, SignerOrProvider } from '../types'
import { UFRAGMENTS_ABI } from './abis/UFragments'
import { UFRAGMENTS_POLICY_ABI } from './abis/UFragmentsPolicy'
import { XC_AMPLE_ABI } from './abis/XCAmple'
import { XC_CONTROLLER_ABI } from './abis/XCController'
import { computeAMPLRewardShares } from './ampleforth'
import { RewardTokenInfo, SignerOrProvider } from '../types'
import { defaultTokenInfo, getTokenInfo } from './token'
import { getCurrentPrice } from './price'
import * as ls from './cache'

const cacheTimeMs = 30 * MIN_IN_MS

const nowInSeconds = () => Math.round(Date.now() / 1000)
function filterActiveRewardSchedules(rewardSchedules) {
const now = nowInSeconds()
return rewardSchedules.filter((s) => parseInt(s.start, 10) + parseInt(s.duration, 10) > now)
}

export const defaultRewardTokenInfo = (): RewardTokenInfo => ({
...defaultTokenInfo(),
price: 1,
getTotalRewards: async () => 0,
})

export const getRewardTokenInfo = async (
Expand All @@ -36,7 +22,7 @@ export const getRewardTokenInfo = async (
case RewardToken.AMPL:
return getAMPLToken(tokenAddress, signerOrProvider)
case RewardToken.XCAMPLE:
return getXCAMPLToken(tokenAddress, signerOrProvider)
return getAMPLToken(tokenAddress, signerOrProvider)
case RewardToken.WAMPL:
return getBasicToken(tokenAddress, signerOrProvider)
case RewardToken.SPOT:
Expand All @@ -48,8 +34,8 @@ export const getRewardTokenInfo = async (
}
}

const getBasicToken = async (tokenAddress: string, signerOrProvider: SignerOrProvider): Promise<RewardTokenInfo> => {
const rewardTokenInfo = await ls.computeAndCache<RewardTokenInfo>(
const getBasicToken = async (tokenAddress: string, signerOrProvider: SignerOrProvider): Promise<RewardTokenInfo> =>
ls.computeAndCache<RewardTokenInfo>(
async function () {
const tokenInfo = await getTokenInfo(tokenAddress, signerOrProvider)
const price = await getCurrentPrice(tokenInfo.symbol)
Expand All @@ -58,95 +44,14 @@ const getBasicToken = async (tokenAddress: string, signerOrProvider: SignerOrPro
`rewardTokenInfo:${tokenAddress}`,
cacheTimeMs,
)
rewardTokenInfo.getTotalRewards = async (rewardSchedules: RewardSchedule[]) =>
filterActiveRewardSchedules(rewardSchedules).reduce(
(acc, schedule) => acc + parseFloat(formatUnits(schedule.rewardAmount, 0)),
0,
)
return rewardTokenInfo
}

// TODO: use subgraph to get AMPL supply history
const getAMPLToken = async (tokenAddress: string, signerOrProvider: SignerOrProvider): Promise<RewardTokenInfo> => {
const rewardTokenInfo = await ls.computeAndCache<RewardTokenInfo>(
async function () {
const tokenInfo = await getTokenInfo(tokenAddress, signerOrProvider)
const price = await getCurrentPrice('AMPL')
return { price, ...tokenInfo }
},
`rewardTokenInfo:${tokenAddress}`,
cacheTimeMs,
)

rewardTokenInfo.amplInfo = await ls.computeAndCache<any>(
async function () {
const contract = new Contract(tokenAddress, UFRAGMENTS_ABI, signerOrProvider)
const policyAddress: string = await contract.monetaryPolicy()
const policy = new Contract(policyAddress, UFRAGMENTS_POLICY_ABI, signerOrProvider)
const totalSupply = await contract.totalSupply()
const epoch = await policy.epoch()
return { policyAddress, epoch, totalSupply }
},
`amplRewardTokenInfo:${tokenAddress}`,
cacheTimeMs,
)

rewardTokenInfo.getTotalRewards = async (rewardSchedules: RewardSchedule[]) => {
const tokenInfo = rewardTokenInfo
const ampl = rewardTokenInfo.amplInfo
const totalRewardShares = await computeAMPLRewardShares(
filterActiveRewardSchedules(rewardSchedules),
tokenAddress,
ampl.policyAddress,
false,
parseInt(ampl.epoch, 10),
tokenInfo.decimals,
signerOrProvider,
)
return totalRewardShares * formatUnits(ampl.totalSupply, tokenInfo.decimals)
}

return rewardTokenInfo
}

const getXCAMPLToken = async (tokenAddress: string, signerOrProvider: SignerOrProvider): Promise<RewardTokenInfo> => {
const rewardTokenInfo = await ls.computeAndCache<RewardTokenInfo>(
const getAMPLToken = async (tokenAddress: string, signerOrProvider: SignerOrProvider): Promise<RewardTokenInfo> =>
ls.computeAndCache<RewardTokenInfo>(
async function () {
const tokenInfo = await getTokenInfo(tokenAddress, signerOrProvider)
const price = await getCurrentPrice('AMPL')
return { price, ...tokenInfo }
},
`rewardTokenInfo:${tokenAddress}`,
0,
)

rewardTokenInfo.amplInfo = await ls.computeAndCache<any>(
async function () {
const token = new Contract(tokenAddress, XC_AMPLE_ABI, signerOrProvider)
const controllerAddress: string = await token.controller()
const controller = new Contract(controllerAddress, XC_CONTROLLER_ABI, signerOrProvider)
const totalSupply = await token.globalAMPLSupply()
const epoch = await controller.globalAmpleforthEpoch()
return { epoch, totalSupply }
},
`xcAmplRewardTokenInfo:${tokenAddress}`,
cacheTimeMs,
)

rewardTokenInfo.getTotalRewards = async (rewardSchedules: RewardSchedule[]) => {
const tokenInfo = rewardTokenInfo
const ampl = rewardTokenInfo.amplInfo
const totalRewardShares = await computeAMPLRewardShares(
filterActiveRewardSchedules(rewardSchedules),
tokenAddress,
controllerAddress,
true,
parseInt(ampl.epoch, 10),
tokenInfo.decimals,
signerOrProvider,
)
return totalRewardShares * formatUnits(ampl.totalSupply, tokenInfo.decimals)
}

return rewardTokenInfo
}
31 changes: 24 additions & 7 deletions frontend/src/utils/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ export const defaultUserStats = (): UserStats => ({
export const defaultGeyserStats = (): GeyserStats => ({
duration: 0,
totalDeposit: 0,
totalDepositVal: 0,
totalRewards: 0,
totalRewardsVal: 0,
calcPeriodInDays: 0,
unlockedRewards: 0,
bonusRewards: [],
bonusRewardsVal: 0,
})

export const defaultVaultStats = (): VaultStats => ({
Expand Down Expand Up @@ -74,6 +77,13 @@ export const getGeyserTotalDeposit = (geyser: Geyser, stakingTokenInfo: StakingT
return stakingTokenAmount * stakingTokenInfo.price
}

export const getGeyserTotalRewards = (geyser: Geyser, rewardTokenInfo: RewardTokenInfo) => {
const { rewardBalance } = geyser
const { decimals } = rewardTokenInfo
const rewardAmt = parseFloat(formatUnits(rewardBalance, decimals))
return rewardAmt * rewardTokenInfo.price
}

export const getGeyserStats = async (
geyser: Geyser,
stakingTokenInfo: StakingTokenInfo,
Expand All @@ -83,16 +93,17 @@ export const getGeyserStats = async (
ls.computeAndCache<GeyserStats>(
async () => ({
duration: getGeyserDuration(geyser),
totalDeposit: getGeyserTotalDeposit(geyser, stakingTokenInfo),
totalRewards:
(await rewardTokenInfo.getTotalRewards(geyser.rewardSchedules)) / 10 ** (rewardTokenInfo.decimals || 1),
calcPeriodInDays: getCalcPeriod(geyser) / DAY_IN_SEC,
unlockedRewards: parseFloat(geyser.unlockedReward) / 10 ** (rewardTokenInfo.decimals || 1),
totalDeposit: parseFloat(formatUnits(geyser.totalStake, stakingTokenInfo.decimals)),
totalDepositVal: getGeyserTotalDeposit(geyser, stakingTokenInfo),
totalRewards: parseFloat(formatUnits(geyser.rewardBalance, rewardTokenInfo.decimals)),
totalRewardsVal: getGeyserTotalRewards(geyser, rewardTokenInfo),
unlockedRewards: parseFloat(formatUnits(geyser.unlockedReward, rewardTokenInfo.decimals)),
bonusRewards:
geyser.rewardPoolBalances.length === bonusTokensInfo.length
? geyser.rewardPoolBalances.map((b, i) => {
const info = bonusTokensInfo[i]
const balance = parseFloat(b.balance) / 10 ** info.decimals
const balance = parseFloat(formatUnits(b.balance, info.decimals))
return {
name: info.name,
symbol: info.symbol,
Expand All @@ -101,6 +112,14 @@ export const getGeyserStats = async (
}
})
: [],
bonusRewardsVal:
geyser.rewardPoolBalances.length === bonusTokensInfo.length
? geyser.rewardPoolBalances.reduce((m, b, i) => {
const info = bonusTokensInfo[i]
const balance = parseFloat(formatUnits(b.balance, info.decimals))
return m + info.price * balance
}, 0)
: 0,
}),
`${toChecksumAddress(geyser.id)}|stats`,
statsCacheTimeMs,
Expand Down Expand Up @@ -234,8 +253,6 @@ export const getUserAPY = async (

const rewardPool = parseFloat(geyser.rewardBalance) / 10 ** rewardTokenDecimals
const rewardShare = outflowReward / rewardPool

// TODO: data layer should guarantee that rewardPoolBalances and bonusTokensInfo are inline
const outflowWithBonus =
outflow +
geyser.rewardPoolBalances.reduce((m, b, i) => {
Expand Down

0 comments on commit 9042731

Please sign in to comment.