Skip to content

Commit

Permalink
fixed bonus rewards apr by considering sponsored supply
Browse files Browse the repository at this point in the history
  • Loading branch information
Ncookiez committed Sep 19, 2024
1 parent d46369d commit 0336794
Show file tree
Hide file tree
Showing 12 changed files with 211 additions and 18 deletions.
2 changes: 1 addition & 1 deletion packages/hyperstructure-client-js/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@generationsoftware/hyperstructure-client-js",
"description": "Lightweight library for interacting with PoolTogether contracts",
"version": "1.19.0",
"version": "1.20.0",
"license": "MIT",
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
Expand Down
20 changes: 20 additions & 0 deletions packages/hyperstructure-client-js/src/Vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,26 @@ export class Vault {
return this.owner
}

/**
* Returns the vault's total delegate supply (total supply - sponsored supply)
* @returns
*/
async getTotalDelegateSupply(): Promise<bigint> {
const source = 'Vault [getTotalDelegateSupply]'
await validateClientNetwork(this.chainId, this.publicClient, source)

const twabControllerAddress = await this.getTWABController()

const totalDelegateSupply = await this.publicClient.readContract({
address: twabControllerAddress,
abi: twabControllerABI,
functionName: 'totalSupplyDelegateBalance',
args: [this.address]
})

return totalDelegateSupply
}

/* ============================== Write Functions ============================== */

/**
Expand Down
39 changes: 39 additions & 0 deletions packages/hyperstructure-client-js/src/Vaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getVaultExchangeRates,
getVaultId,
getVaultsByChainId,
getVaultTotalDelegateSupplies,
getVaultUnderlyingTokenAddresses,
validateAddress,
validateClientNetwork
Expand Down Expand Up @@ -437,4 +438,42 @@ export class Vaults {

return this.underlyingTokenAddresses
}

/**
* Returns each vault's total delegate supply (total supply - sponsored supply)
* @returns
*/
async getTotalDelegateSupplies(): Promise<{ [vaultId: string]: bigint }> {
const totalDelegateSupplies: { [vaultId: string]: bigint } = {}

await Promise.allSettled(
this.chainIds.map((chainId) =>
(async () => {
const vaultAddresses = this.vaultAddresses[chainId]
if (!!vaultAddresses?.length) {
const client = this.publicClients[chainId]
if (!!client) {
const source = `Vaults [getTotalDelegateSupplies] [${chainId}]`
await validateClientNetwork(chainId, client, source)
const chainVaults = getVaultsByChainId(chainId, this.allVaultInfo)
const twabControllerAddress = await this.vaults[
getVaultId({ chainId, address: vaultAddresses[0] })
].getTWABController()
const chainTotalDelegateSupplies = await getVaultTotalDelegateSupplies(
client,
chainVaults,
twabControllerAddress
)

Object.keys(chainTotalDelegateSupplies).forEach((vaultId) => {
totalDelegateSupplies[vaultId] = chainTotalDelegateSupplies[vaultId]
})
}
}
})()
)
)

return totalDelegateSupplies
}
}
3 changes: 3 additions & 0 deletions packages/hyperstructure-react-hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ yarn add @generationsoftware/hyperstructure-react-hooks
- `useAllVaultTokenAddresses`
- `useAllVaultTokenData`
- `useAllVaultTokenPrices`
- `useAllVaultTotalDelegateSupplies`
- `useAllVaultTotalSupplyTwabs`
- `useSortedVaults`
- `useUserClaimableRewards`
Expand All @@ -199,7 +200,9 @@ yarn add @generationsoftware/hyperstructure-react-hooks
- `useVaultTokenAddress`
- `useVaultTokenData`
- `useVaultTokenPrice`
- `useVaultTotalDelegateSupply`
- `useVaultTotalSupplyTwab`
- `useVaultTwabController`
- `useVaultYieldSource`

### Zap Hooks
Expand Down
2 changes: 1 addition & 1 deletion packages/hyperstructure-react-hooks/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@generationsoftware/hyperstructure-react-hooks",
"description": "React hooks library for interacting with PoolTogether contracts",
"version": "1.24.0",
"version": "1.25.0",
"license": "MIT",
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions packages/hyperstructure-react-hooks/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const QUERY_KEYS = {
vaultShareData: 'vaultShareData',
vaultTokenAddresses: 'vaultTokenAddresses',
vaultTokenData: 'vaultTokenData',
vaultTotalDelegateSupplies: 'vaultTotalDelegateSupplies',
vaultTotalSupplyTwabs: 'vaultTotalSupplyTwabs',
vaultTwabController: 'vaultTwabController',
vaultYieldSources: 'vaultYieldSources',
Expand Down
2 changes: 2 additions & 0 deletions packages/hyperstructure-react-hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export * from './vaults/useAllVaultSharePrices'
export * from './vaults/useAllVaultTokenAddresses'
export * from './vaults/useAllVaultTokenData'
export * from './vaults/useAllVaultTokenPrices'
export * from './vaults/useAllVaultTotalDelegateSupplies'
export * from './vaults/useAllVaultTotalSupplyTwabs'
export * from './vaults/useSortedVaults'
export * from './vaults/useUserClaimableRewards'
Expand All @@ -164,6 +165,7 @@ export * from './vaults/useVaultSharePrice'
export * from './vaults/useVaultTokenAddress'
export * from './vaults/useVaultTokenData'
export * from './vaults/useVaultTokenPrice'
export * from './vaults/useVaultTotalDelegateSupply'
export * from './vaults/useVaultTotalSupplyTwab'
export * from './vaults/useVaultTwabController'
export * from './vaults/useVaultYieldSource'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { TokenWithPrice } from '@shared/types'
import { getSecondsSinceEpoch, lower, SECONDS_PER_YEAR } from '@shared/utilities'
import { useMemo } from 'react'
import { Address, formatUnits } from 'viem'
import { useAllVaultPromotions, useAllVaultSharePrices, useTokenPrices, useTokens } from '..'
import {
useAllVaultPromotions,
useAllVaultSharePrices,
useAllVaultTotalDelegateSupplies,
useTokenPrices,
useTokens
} from '..'

/**
* Returns each vault's bonus rewards APR
Expand Down Expand Up @@ -52,6 +58,12 @@ export const useAllVaultPromotionsApr = (
tokenAddresses
)

const {
data: allTotalDelegateSupplies,
isFetched: isFetchedAllTotalDelegateSupplies,
refetch: refetchAllTotalDelegateSupplies
} = useAllVaultTotalDelegateSupplies(vaults)

const data = useMemo(() => {
const vaultPromotionAprs: { [vaultId: string]: number } = {}

Expand All @@ -60,21 +72,22 @@ export const useAllVaultPromotionsApr = (
!!allVaultPromotions?.[chainId] &&
!!allShareTokens &&
!!rewardTokenPrices &&
!!rewardTokenData
!!rewardTokenData &&
!!allTotalDelegateSupplies
) {
const currentTimestamp = getSecondsSinceEpoch()

Object.values(vaults.vaults)
.filter((vault) => vault.chainId === chainId)
.forEach((vault) => {
const shareToken = allShareTokens[vault.id]
const totalDelegateSupply = allTotalDelegateSupplies[vault.id]

if (shareToken?.totalSupply === 0n || shareToken?.price === 0) {
if (totalDelegateSupply === 0n || shareToken?.price === 0) {
vaultPromotionAprs[vault.id] = 0
} else if (!!shareToken?.price) {
} else if (!!totalDelegateSupply && !!shareToken?.price) {
const tvl =
parseFloat(formatUnits(shareToken.totalSupply, shareToken.decimals)) *
shareToken.price
parseFloat(formatUnits(totalDelegateSupply, shareToken.decimals)) * shareToken.price

tokenAddresses.forEach((address) => {
const rewardToken: TokenWithPrice = {
Expand Down Expand Up @@ -129,17 +142,25 @@ export const useAllVaultPromotionsApr = (
}

return vaultPromotionAprs
}, [allVaultPromotions, allShareTokens, rewardTokenPrices, rewardTokenData])
}, [
allVaultPromotions,
allShareTokens,
rewardTokenPrices,
rewardTokenData,
allTotalDelegateSupplies
])

const isFetched =
isFetchedAllVaultPromotions &&
isFetchedAllShareTokens &&
isFetchedRewardTokenPrices &&
isFetchedRewardTokenData
isFetchedRewardTokenData &&
isFetchedAllTotalDelegateSupplies

const refetch = () => {
refetchAllVaultPromotions()
refetchAllShareTokens()
refetchAllTotalDelegateSupplies()
}

return { data, isFetched, refetch }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Vaults } from '@generationsoftware/hyperstructure-client-js'
import { NO_REFETCH } from '@shared/generic-react-hooks'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { populateCachePerId } from '..'
import { QUERY_KEYS } from '../constants'

/**
* Returns each vault's total delegate supply (total supply - sponsored supply)
* @param vaults instance of the `Vaults` class
* @returns
*/
export const useAllVaultTotalDelegateSupplies = (vaults: Vaults) => {
const queryClient = useQueryClient()

const vaultIds = !!vaults ? Object.keys(vaults.vaults) : []
const getQueryKey = (val: (string | number)[]) => [QUERY_KEYS.vaultTotalDelegateSupplies, val]

return useQuery({
queryKey: getQueryKey(vaultIds),
queryFn: async () => {
const totalDelegateSupply = await vaults.getTotalDelegateSupplies()

populateCachePerId(queryClient, getQueryKey, totalDelegateSupply)

return totalDelegateSupply
},
enabled: !!vaults,
...NO_REFETCH
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { TokenWithPrice } from '@shared/types'
import { getSecondsSinceEpoch, lower, SECONDS_PER_YEAR } from '@shared/utilities'
import { useMemo } from 'react'
import { Address, formatUnits } from 'viem'
import { useTokenPrices, useTokens, useVaultPromotions, useVaultSharePrice } from '..'
import {
useTokenPrices,
useTokens,
useVaultPromotions,
useVaultSharePrice,
useVaultTotalDelegateSupply
} from '..'

/**
* Returns a vault's bonus rewards APR
Expand Down Expand Up @@ -45,21 +51,27 @@ export const useVaultPromotionsApr = (
tokenAddresses
)

const {
data: totalDelegateSupply,
isFetched: isFetchedTotalDelegateSupply,
refetch: refetchTotalDelegateSupply
} = useVaultTotalDelegateSupply(vault)

const data = useMemo(() => {
if (shareToken?.totalSupply === 0n || shareToken?.price === 0) {
if (totalDelegateSupply === 0n || shareToken?.price === 0) {
return { apr: 0, tokens: [] }
}

if (
!!vaultPromotions &&
!!shareToken &&
!!shareToken.price &&
!!shareToken?.price &&
!!rewardTokenPrices &&
!!rewardTokenData
!!rewardTokenData &&
!!totalDelegateSupply
) {
const currentTimestamp = getSecondsSinceEpoch()
const tvl =
parseFloat(formatUnits(shareToken.totalSupply, shareToken.decimals)) * shareToken.price
parseFloat(formatUnits(totalDelegateSupply, shareToken.decimals)) * shareToken.price

const relevantTokenAddresses = new Set<Address>()
let apr = 0
Expand Down Expand Up @@ -116,17 +128,19 @@ export const useVaultPromotionsApr = (

return { apr, tokens: promotionTokens }
}
}, [vaultPromotions, shareToken, rewardTokenPrices, rewardTokenData])
}, [vaultPromotions, shareToken, rewardTokenPrices, rewardTokenData, totalDelegateSupply])

const isFetched =
isFetchedVaultPromotions &&
isFetchedShareToken &&
isFetchedRewardTokenPrices &&
isFetchedRewardTokenData
isFetchedRewardTokenData &&
isFetchedTotalDelegateSupply

const refetch = () => {
refetchVaultPromotions()
refetchShareToken()
refetchTotalDelegateSupply()
}

return { data, isFetched, refetch }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Vault } from '@generationsoftware/hyperstructure-client-js'
import { NO_REFETCH } from '@shared/generic-react-hooks'
import { useQuery, UseQueryResult } from '@tanstack/react-query'
import { QUERY_KEYS } from '../constants'

/**
* Returns the vault's total delegate supply (total supply - sponsored supply)
* @param vault instance of the `Vault` class
* @returns
*/
export const useVaultTotalDelegateSupply = (vault: Vault): UseQueryResult<bigint> => {
const queryKey = [QUERY_KEYS.vaultTotalDelegateSupplies, vault?.id]

return useQuery({
queryKey,
queryFn: async () => await vault.getTotalDelegateSupply(),
enabled: !!vault,
...NO_REFETCH
})
}
43 changes: 43 additions & 0 deletions shared/utilities/utils/vaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,46 @@ export const getVaultUnderlyingTokenAddresses = async (

return tokenAddresses
}

/**
* Returns each vault's total delegate supply (total supply - sponsored supply)
* @param publicClient a public Viem client for the chain that should be queried
* @param vaults vaults to query through
* @param twabController the address of the TWAB controller to query through
* @returns
*/
export const getVaultTotalDelegateSupplies = async (
publicClient: PublicClient,
vaults: VaultInfo[],
twabController: Address
): Promise<{
[vaultId: string]: bigint
}> => {
const vaultTotalDelegateSupplies: { [vaultId: string]: bigint } = {}
const chainId = await publicClient.getChainId()
const filteredVaults = !!chainId
? vaults.filter((vault) => vault.chainId === chainId && vault.decimals !== undefined)
: []

if (filteredVaults.length > 0) {
const calls = filteredVaults.map(({ address }) => ({
functionName: 'totalSupplyDelegateBalance',
args: [address]
}))

const multicallResults = await getSimpleMulticallResults(
publicClient,
twabController,
twabControllerABI,
calls
)

filteredVaults.forEach((vaultInfo, i) => {
const result = multicallResults[i]
const vaultId = getVaultId(vaultInfo)
vaultTotalDelegateSupplies[vaultId] = result
})
}

return vaultTotalDelegateSupplies
}

0 comments on commit 0336794

Please sign in to comment.