Skip to content

Commit

Permalink
feat: calc apr
Browse files Browse the repository at this point in the history
  • Loading branch information
cgilbe27 committed Apr 29, 2024
1 parent dabe094 commit f66a2ab
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 0 deletions.
225 changes: 225 additions & 0 deletions src/sdk/utils/math.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import BigNumber from "bignumber.js"
import { calculateEpochMintProvision, computeAPR, polynomial } from "./math"
import { Params } from "src/protojs/nibiru/inflation/v1/genesis"
import Long from "long"

describe("polynomial", () => {
interface TestCase {
name: string
in: { factors: string[]; x: BigNumber }
expected: BigNumber
shouldFail?: boolean
}

const tests: TestCase[] = [
{
name: "zero",
in: { factors: ["0"], x: BigNumber(0) },
expected: BigNumber("0"),
},
{
name: "real",
in: {
factors: [
"-0.000147085524000000",
"0.074291982762000000",
"-18.867415611180000000",
"3128.641926954698000000",
"-334834.740631598223000000",
"17827464.906540066004000000",
],
x: BigNumber(0),
},
expected: BigNumber("17827464.906540066004000000"),
},
{
name: "noarray",
in: {
factors: [],
x: BigNumber(0),
},
expected: BigNumber("0"),
},
]

test.each(tests)("%o", (tt) => {
let failed = false
try {
const res = polynomial(tt.in.factors, tt.in.x)
expect(res.eq(tt.expected)).toBe(true)
} catch (e) {
if (!tt.shouldFail) {
console.error(`Test ${tt.name} failed with error: ${e}`)
}
failed = true
}
expect(failed).toBe(!!tt.shouldFail)
})
})

describe("calculateEpochMintProvision", () => {
interface TestCase {
name: string
in: { params: Params; period: BigNumber }
expected: BigNumber
shouldFail?: boolean
}

const tests: TestCase[] = [
{
name: "real",
in: {
params: {
inflationEnabled: true,
polynomialFactors: [
"-0.000147085524000000",
"0.074291982762000000",
"-18.867415611180000000",
"3128.641926954698000000",
"-334834.740631598223000000",
"17827464.906540066004000000",
],
inflationDistribution: {
stakingRewards: "0.281250000000000000",
communityPool: "0.354825000000000000",
strategicReserves: "0.363925000000000000",
},
epochsPerPeriod: new Long(30),
periodsPerYear: new Long(12),
maxPeriod: new Long(96),
hasInflationStarted: true,
},
period: BigNumber(0),
},
expected: BigNumber("17827464.906540066004"),
},
{
name: "zero",
in: {
params: {
inflationEnabled: true,
polynomialFactors: [],
inflationDistribution: {
stakingRewards: "0",
communityPool: "0",
strategicReserves: "0",
},
epochsPerPeriod: new Long(0),
periodsPerYear: new Long(0),
maxPeriod: new Long(0),
hasInflationStarted: true,
},
period: BigNumber(0),
},
expected: BigNumber(0),
},
]

test.each(tests)("%o", (tt) => {
let failed = false
try {
const res = calculateEpochMintProvision(tt.in.params, tt.in.period)
expect(res.eq(tt.expected)).toBe(true)
} catch (e) {
if (!tt.shouldFail) {
console.error(`Test ${tt.name} failed with error: ${e}`)
}
failed = true
}
expect(failed).toBe(!!tt.shouldFail)
})
})

describe("computeAPR", () => {
interface TestCase {
name: string
in: {
myStake: number
totalStaked: number
params: Params
period: number
}
expected: number
shouldFail?: boolean
}

const tests: TestCase[] = [
{
name: "real",
in: {
myStake: 10,
totalStaked: 10_000_000,
params: {
inflationEnabled: true,
polynomialFactors: [
"-0.000147085524000000",
"0.074291982762000000",
"-18.867415611180000000",
"3128.641926954698000000",
"-334834.740631598223000000",
"17827464.906540066004000000",
],
inflationDistribution: {
stakingRewards: "0.281250000000000000",
communityPool: "0.354825000000000000",
strategicReserves: "0.363925000000000000",
},
epochsPerPeriod: new Long(30),
periodsPerYear: new Long(12),
maxPeriod: new Long(96),
hasInflationStarted: true,
},
period: 0,
},
expected: 6.016763389193883,
},
{
name: "NaN",
in: {
myStake: 0,
totalStaked: 0,
params: {
inflationEnabled: true,
polynomialFactors: [
"-0.000147085524000000",
"0.074291982762000000",
"-18.867415611180000000",
"3128.641926954698000000",
"-334834.740631598223000000",
"17827464.906540066004000000",
],
inflationDistribution: {
stakingRewards: "0.281250000000000000",
communityPool: "0.354825000000000000",
strategicReserves: "0.363925000000000000",
},
epochsPerPeriod: new Long(30),
periodsPerYear: new Long(12),
maxPeriod: new Long(96),
hasInflationStarted: true,
},
period: 0,
},
expected: NaN,
},
]

test.each(tests)("%o", (tt) => {
let failed = false
try {
const res = computeAPR(
tt.in.myStake,
tt.in.totalStaked,
tt.in.params,
tt.in.period
)
expect(res).toEqual(tt.expected)
} catch (e) {
if (!tt.shouldFail) {
console.error(`Test ${tt.name} failed with error: ${e}`)
}
failed = true
}
expect(failed).toBe(!!tt.shouldFail)
})
})
46 changes: 46 additions & 0 deletions src/sdk/utils/math.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import BigNumber from "bignumber.js"
import { Params } from "src/protojs/nibiru/inflation/v1/genesis"

export const polynomial = (factors: string[], x: BigNumber) => {
let result = BigNumber(0)
for (let i = 0; i < factors.length; i++) {
result = result.plus(
BigNumber(factors[i]).times(x.pow(factors.length - i - 1))
)
}

// Multiply by 1 million to get the value in a specific unit
return result
}

export const calculateEpochMintProvision = (
params: Params,
period: BigNumber
) => {
// Calculate the value of the polynomial at x
const polynomialValue = polynomial(params.polynomialFactors, period)

return polynomialValue.lt(0) ||
params.epochsPerPeriod.eq(0) ||
params.maxPeriod.lt(period.toString())
? BigNumber(0)
: polynomialValue
}

export const computeAPR = (
myStake: number,
totalStaked: number,
params: Params,
period: number
) => {
// get epoch mint
const annualReward = calculateEpochMintProvision(params, BigNumber(period))
.times(params.inflationDistribution?.stakingRewards ?? 0)
.times(12)

return BigNumber(myStake)
.div(myStake + totalStaked)
.times(annualReward)
.div(myStake)
.toNumber()
}

0 comments on commit f66a2ab

Please sign in to comment.