Skip to content

Commit

Permalink
refactor: updated Taostats api call endpoints (#1206)
Browse files Browse the repository at this point in the history
* refactor: updated taostats api call endpoints

* fix: object key to match new api response "hot_key" -> "hotkey"

* fix: apr type, add default
  • Loading branch information
UrbanWill authored Oct 30, 2024
1 parent ec1d0f0 commit e8421b5
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { useState } from 'react'
import { useRecoilValue_TRANSITION_SUPPORT_UNSTABLE as useRecoilValue } from 'recoil'

import { useDelegatesStats } from '@/domains/staking/subtensor/hooks/useDelegatesStats'

import type { Delegate } from '../../../../domains/staking/subtensor/atoms/delegates'
import { useNativeTokenAmountState } from '../../../../domains/chains'
import { DEFAULT_DELEGATE, type Delegate } from '../../../../domains/staking/subtensor/atoms/delegates'
import { DEFAULT_DELEGATE } from '../../../../domains/staking/subtensor/atoms/delegates'
import { useAllDelegateInfos } from '../../../../domains/staking/subtensor/hooks/useAllDelegateInfos'
import { useDelegates } from '../../../../domains/staking/subtensor/hooks/useDelegates'
import StakeTargetSelectorDialog from '../../../recipes/StakeTargetSelectorDialog'
import { useDelegatesStats } from '@/domains/staking/subtensor/hooks/useDelegatesStats'
import { useState } from 'react'
import { useRecoilValue_TRANSITION_SUPPORT_UNSTABLE as useRecoilValue } from 'recoil'

type DelegateSelectorDialogProps = {
selected?: Delegate
Expand Down Expand Up @@ -36,8 +39,8 @@ export const DelegateSelectorDialog = (props: DelegateSelectorDialogProps) => {
(b.props.balancePlanck ?? 0n) === (a.props.balancePlanck ?? 0n)
? 0
: (b.props.balancePlanck ?? 0n) - (a.props.balancePlanck ?? 0n) < 0
? -1
: 1,
? -1
: 1,
'Number of stakers': (a, b) =>
parseInt(b.props.count?.toString?.() ?? '0') - parseInt(a.props.count?.toString?.() ?? '0'),
'Estimated APR': (a, b) =>
Expand All @@ -53,7 +56,7 @@ export const DelegateSelectorDialog = (props: DelegateSelectorDialogProps) => {
>
{Object.values(delegates).map(delegate => {
const formattedApr = Number(
delegatesStats.find(stat => stat.hot_key.ss58 === delegate.address)?.apr
delegatesStats.find(stat => stat.hotkey.ss58 === delegate.address)?.apr
).toLocaleString(undefined, { style: 'percent', maximumFractionDigits: 2 })

return (
Expand Down
60 changes: 36 additions & 24 deletions apps/portal/src/domains/staking/subtensor/atoms/taostats.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { ValidatorsData } from '../types'
import { delegatesAtom } from './delegates'
import { atom } from 'jotai'
import { atomFamily } from 'jotai/utils'

import { ValidatorsData } from '../types'
import { delegatesAtom } from './delegates'

const TAOSTATS_API_KEY = import.meta.env.REACT_APP_TAOSTATS_API_KEY
const TAOSTATS_API_URL = 'https://api.taostats.io/api/v1'
const TAOSTATS_API_URL = 'https://api-prod-v2.taostats.io/api'
const MAX_PAGE_SIZE = 100

const fetchTaoStats = async ({ page = 1, limit = 200 }: { page: number; limit: number }): Promise<ValidatorsData> => {
const fetchTaoStats = async ({
page = 1,
limit = MAX_PAGE_SIZE,
}: {
page: number
limit: number
}): Promise<ValidatorsData> => {
try {
return await (
await fetch(`${TAOSTATS_API_URL}/validator?page=${page}&limit=${limit}`, {
await fetch(`${TAOSTATS_API_URL}/validator/latest/v1?page=${page}&limit=${limit}`, {
method: 'GET',
headers: {
Authorization: TAOSTATS_API_KEY,
Expand All @@ -23,14 +31,24 @@ const fetchTaoStats = async ({ page = 1, limit = 200 }: { page: number; limit: n
}

export const taostatsAtom = atom(async () => {
const stats: ValidatorsData = { count: 0, validators: [] }

const stats: ValidatorsData = {
pagination: {
current_page: 0,
per_page: 0,
total_items: 0,
total_pages: 0,
next_page: null,
prev_page: null,
},
data: [],
}
let page = 1
while (stats.count === 0 || stats.count > stats.validators.length) {
const taoStats = await fetchTaoStats({ page: page, limit: 200 })
stats.count = taoStats.count
stats.validators.push(...taoStats.validators)
page++

while (!stats.data.length || stats.pagination.current_page < stats.pagination.total_pages) {
const taoStats = await fetchTaoStats({ page: page, limit: MAX_PAGE_SIZE })
stats.pagination = taoStats.pagination
stats.data.push(...taoStats.data)
page = taoStats.pagination.current_page + 1
}

return stats
Expand All @@ -42,34 +60,28 @@ export const activeTaoDelegatesStatsAtom = atom(async get => {

const activeDelegatesHotKeys = Object.keys(delegates)

const activeDelegates = taostats.validators.filter(validator =>
activeDelegatesHotKeys.includes(validator.hot_key.ss58)
)
const activeDelegates = taostats.data.filter(validator => activeDelegatesHotKeys.includes(validator.hotkey.ss58))

return activeDelegates
})

export const highestAprTaoValidatorAtom = atom(async get => {
const activeDelegatesStats = await get(activeTaoDelegatesStatsAtom)
const highestAprValidatorStats = activeDelegatesStats.reduce((acc, validator) => {
if (parseFloat(validator.apr) > parseFloat(acc.apr)) {
acc = validator
}
return acc
})

const highestAprValidatorStats = activeDelegatesStats[0]

return highestAprValidatorStats
})

export const taoDelegateStatsAtomFamily = atomFamily((hotKey: string) =>
atom(async get => {
const activeDelegatesStats = await get(activeTaoDelegatesStatsAtom)
return activeDelegatesStats.find(validator => validator.hot_key.ss58 === hotKey)
return activeDelegatesStats.find(validator => validator.hotkey.ss58 === hotKey)
})
)

export const taoTotalStakedTaoAtom = atom(async get => {
const { system_total_stake } = await get(highestAprTaoValidatorAtom)
const { system_stake = '0' } = (await get(highestAprTaoValidatorAtom)) ?? {}

return system_total_stake
return system_stake
})
5 changes: 3 additions & 2 deletions apps/portal/src/domains/staking/subtensor/hooks/useApr.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useAtomValue } from 'jotai'

import { highestAprTaoValidatorAtom } from '../atoms/taostats'
import { useDelegateStats } from './useDelegateStats'
import { useAtomValue } from 'jotai'

export const useHighestAprFormatted = () => {
const { apr } = useAtomValue(highestAprTaoValidatorAtom)
const { apr = '0' } = useAtomValue(highestAprTaoValidatorAtom) ?? {}

return Number(apr).toLocaleString(undefined, { style: 'percent', maximumFractionDigits: 2 })
}
Expand Down
74 changes: 45 additions & 29 deletions apps/portal/src/domains/staking/subtensor/types.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,59 @@
export type ValidatorsData = {
count: number
validators: Validator[]
type Pagination = {
current_page: number
per_page: number
total_items: number
total_pages: number
next_page: number | null
prev_page: number | null
}

// Key Type
type Key = {
ss58: string
hex: string
}

type Validator = {
// Subnet Dominance Type
type SubnetDominance = {
netuid: number
dominance: string
family_stake: string
}

// Validator Data Type
type ValidatorData = {
hotkey: Key
coldkey: Key
name: string
block_number: number
timestamp: string
registered_at_time: string
registered_at_block: number
validator_stake: string
amount: string
timestamp: string // ISO date format string
rank: number
nominators: number
amount_change: string
nominator_change: number
registrations: number[]
validator_permits: number[]
nominators_24_hr_change: number
system_stake: string
stake: string
stake_24_hr_change: string
dominance: string
validator_stake: string
take: string
total_daily_return: string
validator_return: string
nominator_return_per_k: string
apr: string
nominator_return_per_k_7_day_average: string
nominator_return_per_k_30_day_average: string
apr_7_day_average: string
apr_30_day_average: string
hot_key: Key
cold_key: Key
take: string
rank: number
dominance: string
system_total_stake: string
nominator_7day_average: string
nominator_30day_average: string
pending_emission: string
blocks_until_next_reward: number
last_reward_block: number
registrations: number[]
permits: number[]
subnet_dominance: SubnetDominance[]
}

type Key = {
ss58: string
hex: string
}

type SubnetDominance = {
netuid: number
dominance: string
// API Response Type
export type ValidatorsData = {
pagination: Pagination
data: ValidatorData[]
}

0 comments on commit e8421b5

Please sign in to comment.