Skip to content

Commit

Permalink
feat(neuron-ui): add global apy estimation (#1092)
Browse files Browse the repository at this point in the history
* feat(neuron-ui): add global apy estimation

* test(neuron-ui): fix the test of calculateGlobalAPY
  • Loading branch information
Keith-CY authored Nov 13, 2019
1 parent 4eb367f commit dc82cbb
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/neuron-ui/src/components/NervosDAO/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import appState from 'states/initStates/app'
import { AppActions, StateWithDispatch } from 'states/stateProvider/reducer'
import { updateNervosDaoData, clearNervosDaoData } from 'states/stateProvider/actionCreators'

import calculateGlobalAPY from 'utils/calculateGlobalAPY'
import calculateFee from 'utils/calculateFee'
import { shannonToCKBFormatter, CKBToShannonFormatter } from 'utils/formatters'
import { MIN_DEPOSIT_AMOUNT, MEDIUM_FEE_RATE, CapacityUnit } from 'utils/const'
Expand All @@ -28,6 +29,7 @@ const NervosDAO = ({
loadings: { sending = false },
tipBlockNumber,
tipBlockHash,
tipBlockTimestamp,
epoch,
},
wallet,
Expand All @@ -41,6 +43,7 @@ const NervosDAO = ({
const [activeRecord, setActiveRecord] = useState<State.NervosDAORecord | null>(null)
const [errorMessage, setErrorMessage] = useState('')
const [withdrawList, setWithdrawList] = useState<(string | null)[]>([])
const [globalAPY, setGlobalAPY] = useState(0)

const clearGeneratedTx = useCallback(() => {
dispatch({
Expand Down Expand Up @@ -92,6 +95,14 @@ const NervosDAO = ({
}
}, [clearGeneratedTx, dispatch, updateDepositValue, wallet.id])

useEffect(() => {
if (tipBlockTimestamp) {
calculateGlobalAPY(tipBlockTimestamp).then(apy => {
setGlobalAPY(apy)
})
}
}, [tipBlockTimestamp])

const onDepositDialogDismiss = () => {
setShowDepositDialog(false)
setDepositValue(`${MIN_DEPOSIT_AMOUNT}`)
Expand Down Expand Up @@ -278,9 +289,10 @@ const NervosDAO = ({
<Text as="span" variant="small" block>{`Epoch number: ${epochInfo.number}`}</Text>
<Text as="span" variant="small" block>{`Epoch index: ${epochInfo.index}`}</Text>
<Text as="span" variant="small" block>{`Epoch length: ${epochInfo.length}`}</Text>
<Text as="span" variant="small" block>{`APY: ~${globalAPY}%`}</Text>
</Stack>
)
}, [epoch])
}, [epoch, globalAPY])

return (
<>
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-ui/src/containers/Main/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const useSyncChainData = ({ chainURL, dispatch }: { chainURL: string; dis
payload: {
tipBlockNumber: `${BigInt(header.number)}`,
tipBlockHash: header.hash,
tipBlockTimestamp: +header.timestamp,
chain: chainInfo.chain,
difficulty: `${BigInt(chainInfo.difficulty)}`,
epoch: chainInfo.epoch,
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-ui/src/states/initStates/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CapacityUnit } from 'utils/const'
const appState: State.App = {
tipBlockNumber: '',
tipBlockHash: '',
tipBlockTimestamp: 0,
chain: '',
difficulty: '',
epoch: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default {
'return 0 if the genesis block is not loaded': {
currentTime: Date.now(),
genesisTime: undefined,
expectAPY: 0,
},
'one period and one handrand days': {
currentTime: new Date('2023-04-10').getTime(),
genesisTime: new Date('2019-01-01').getTime(),
expectAPY: 0.02369552868619654,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import calculateGlobalAPY from 'utils/calculateGlobalAPY'
import fixtures from './fixtures'

describe('calculate the global apy', () => {
const fixtureTable = Object.entries(fixtures).map(([title, { currentTime, genesisTime, expectAPY }]) => [
title,
currentTime,
genesisTime,
expectAPY,
])

test.each(fixtureTable)(`%s`, async (_title, currentTime, genesisTime, expectAPY) => {
const apy = await calculateGlobalAPY(currentTime, genesisTime)
expect(apy).toBe(expectAPY === 0 ? 0 : +expectAPY.toFixed(2))
})
})
1 change: 1 addition & 0 deletions packages/neuron-ui/src/types/App/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ declare namespace State {
interface App {
tipBlockNumber: string
tipBlockHash: string
tipBlockTimestamp: number
chain: string
difficulty: string
epoch: string
Expand Down
48 changes: 48 additions & 0 deletions packages/neuron-ui/src/utils/calculateGlobalAPY.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getBlockByNumber } from '../services/chain'

const INITIAL_OFFER = BigInt(33600000000)
const SECONDARY_OFFER = BigInt(1344000000)
const DAYS_PER_PERIOD = 365 * 4 * 1
const MILLI_SECONDS_PER_DAY = 24 * 3600 * 1000
const PERIOD_LENGTH = DAYS_PER_PERIOD * MILLI_SECONDS_PER_DAY

let cachedGenesisTimestamp: number | undefined

export default async (now: number, initialTimestamp: number | undefined = cachedGenesisTimestamp) => {
let genesisTimestamp = initialTimestamp
if (genesisTimestamp === undefined) {
genesisTimestamp = await getBlockByNumber('0x0')
.then(b => {
cachedGenesisTimestamp = +b.header.timestamp
return cachedGenesisTimestamp
})
.catch(() => undefined)
}
if (genesisTimestamp === undefined || now <= genesisTimestamp) {
return 0
}

const pastPeriods = BigInt(now - genesisTimestamp) / BigInt(PERIOD_LENGTH)
const pastDays = Math.ceil(((now - genesisTimestamp) % PERIOD_LENGTH) / MILLI_SECONDS_PER_DAY)

const realSecondaryOffer =
BigInt(4) * SECONDARY_OFFER * pastPeriods +
(BigInt(4) * SECONDARY_OFFER * BigInt(pastDays)) / BigInt(DAYS_PER_PERIOD)

let realPrimaryOffer = BigInt(0)

let PRIMARY_OFFER = INITIAL_OFFER
for (let i = 0; i < Number(pastPeriods); i++) {
PRIMARY_OFFER /= BigInt(2)
const offer = PRIMARY_OFFER
realPrimaryOffer += offer
}

PRIMARY_OFFER /= BigInt(2)

const primaryOfferFraction = (BigInt(pastDays) * PRIMARY_OFFER) / BigInt(DAYS_PER_PERIOD)
realPrimaryOffer += primaryOfferFraction

const totalOffer = INITIAL_OFFER + realPrimaryOffer + realSecondaryOffer
return +(Number(SECONDARY_OFFER) / Number(totalOffer)).toFixed(2)
}

0 comments on commit dc82cbb

Please sign in to comment.