Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supply API updates #110

Merged
merged 3 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 54 additions & 18 deletions src/app/api/stats/supply/[token]/route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import { NextRequest, NextResponse } from "next/server"
import {
HNT_MAX_SUPPLY,
IOT_MAX_SUPPLY,
MOBILE_MAX_SUPPLY,
} from "@/app/stats/utils/emissions"
import { fetchMint } from "@/app/stats/utils/fetchMint"
import { HNT_MINT, MOBILE_MINT, IOT_MINT, toNumber } from "@helium/spl-utils"
import {
getRemainingEmissions,
getDailyEmisisons,
MAX_DAILY_NET_EMISSIONS,
getDailyEmisisons,
getRemainingEmissions,
} from "@/app/stats/utils/remainingEmissions"
import { db } from "@/knex/db"
import { MaxSupply } from "@/knex/maxSupply"
import { HNT_MINT, IOT_MINT, MOBILE_MINT, toNumber } from "@helium/spl-utils"
import { NextRequest, NextResponse } from "next/server"

enum SupplyToken {
HNT = "hnt",
MOBILE = "mobile",
IOT = "iot",
}

type SupplyToken = "hnt" | "mobile" | "iot"
type SupplyType = "circulating" | "total" | "max"
enum SupplyType {
CIRCULATING = "circulating",
TOTAL = "total",
LIMIT = "limit",
MAX = "max",
}

export async function GET(
request: NextRequest,
Expand All @@ -19,33 +36,41 @@ export async function GET(
const type = searchParams.get("type") as SupplyType

if (
!["hnt", "mobile", "iot"].includes(token) ||
!["circulating", "total", "max"].includes(type)
!Object.values(SupplyToken).includes(token) ||
!Object.values(SupplyType).includes(type)
) {
return new NextResponse(null, { status: 400 })
}

const mintInfo = await fetchMint(
{
hnt: HNT_MINT,
mobile: MOBILE_MINT,
iot: IOT_MINT,
[SupplyToken.HNT]: HNT_MINT,
[SupplyToken.MOBILE]: MOBILE_MINT,
[SupplyToken.IOT]: IOT_MINT,
}[token]
)

if (type === "circulating") {
if (type === SupplyType.CIRCULATING || type === SupplyType.TOTAL) {
const circulatingSupply = mintInfo.info?.info.supply!

return NextResponse.json(
toNumber(circulatingSupply, mintInfo?.info?.info.decimals || 0)
)
} else if (type === "total" || type === "max") {
// Return the same thing for total and max as they are functionally the same for us
let remainingEmissions = Math.ceil(getRemainingEmissions(new Date(), token))
} else if (type === SupplyType.LIMIT) {
let remainingEmissions = 0
let supply = mintInfo.info?.info.supply!

if (token === "hnt") {
if (token === SupplyToken.HNT) {
// Due to Net Emissions, assume the max amount will be re-emitted
remainingEmissions += Math.ceil(MAX_DAILY_NET_EMISSIONS)
remainingEmissions = Math.ceil(MAX_DAILY_NET_EMISSIONS)

// using existing supply limit logic to avoid repeating edge case logic
const maxSupplyDb = new MaxSupply(db)
const supplyLimit = (await maxSupplyDb.getLatest({ withBurn: false }))
?.max_supply!
supply = supplyLimit
} else {
remainingEmissions += Math.ceil(getRemainingEmissions(new Date(), token))
}

// Add the daily emissions for today to be conservative
Expand All @@ -54,12 +79,23 @@ export async function GET(
remainingEmissions += Math.ceil(dailyEmissions)

const totalSupply =
mintInfo.info?.info.supply! +
supply +
BigInt(remainingEmissions) *
BigInt(Math.pow(10, mintInfo?.info?.info.decimals || 0))

return NextResponse.json(
toNumber(totalSupply, mintInfo?.info?.info.decimals || 0)
)
} else if (type === SupplyType.MAX) {
switch (token) {
case SupplyToken.HNT:
return NextResponse.json(HNT_MAX_SUPPLY)
case SupplyToken.MOBILE:
return NextResponse.json(MOBILE_MAX_SUPPLY)
case SupplyToken.IOT:
return NextResponse.json(IOT_MAX_SUPPLY)
default:
return new NextResponse(null, { status: 400 })
}
}
}
15 changes: 13 additions & 2 deletions src/app/stats/components/HntInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
fetchHntBurn,
fetchHntEmissions,
} from "../utils/dune/fetchHntEmissions"
import { HNT_MAX_SUPPLY } from "../utils/emissions"
import { fetchHntGovernanceStats } from "../utils/fetchGovernanceMetrics"
import { fetchMint } from "../utils/fetchMint"
import { getNextHalvening } from "../utils/getNextHalvening"
Expand Down Expand Up @@ -127,7 +128,7 @@ export const HntInfo = async () => {
}}
/>
<StatItem
label="Max Supply"
label="Supply Limit"
value={`~${humanReadableBigint(
maxSupplyRecord.max_supply,
hntMint?.info?.info.decimals || 8,
Expand All @@ -136,10 +137,20 @@ export const HntInfo = async () => {
tooltip={{
description:
"Maximum supply of HNT derived by summing current supply, remaining emissions, and today's burned HNT (which are re-emitted via net emissions). This is an upper limit that will not be reached and does not consider future HNT burn. Accurate within 1643 HNT.",
cadence: `Every 8h (last run ${format(
cadence: `Daily (last run ${format(
maxSupplyRecord.recorded_at,
DATE_FORMAT
)})`,
id: "HNT Supply Limit",
}}
/>
<StatItem
label="Max Supply"
value={HNT_MAX_SUPPLY.toLocaleString()}
tooltip={{
description:
"Maximum supply of HNT that can ever exist as defined in HIPs. This is an upper limit that will not be reached and does not consider past or future HNT burn.",
cadence: "Fixed",
id: "HNT Max Supply",
}}
/>
Expand Down
20 changes: 17 additions & 3 deletions src/app/stats/components/SubDaoInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
toNumber,
} from "@helium/spl-utils"
import { PublicKey } from "@solana/web3.js"
import { IOT_MAX_SUPPLY, MOBILE_MAX_SUPPLY } from "../utils/emissions"
import { fetchSubDaoGovernanceStats } from "../utils/fetchGovernanceMetrics"
import { fetchMint } from "../utils/fetchMint"
import { fetchSubDaoEpochInfo } from "../utils/fetchSubDaoEpochInfo"
Expand All @@ -31,6 +32,7 @@ type SubDaoType = {
subDaoMint: PublicKey
maxDescription: string
activeDetails: string
maxSupply: number
}

const MOBILE_INFO: SubDaoType = {
Expand All @@ -43,6 +45,7 @@ const MOBILE_INFO: SubDaoType = {
maxDescription:
"This is an upper limit that will not be reached and does not consider future MOBILE burn. Reason: Daily emissions are currently only 86% of scheduled emissions, as not all rewardable entities (service providers, and oracles) exist or currently receive rewards.",
activeDetails: " This exclusively includes active gateways (not radios).",
maxSupply: MOBILE_MAX_SUPPLY,
}

const IOT_INFO: SubDaoType = {
Expand All @@ -55,6 +58,7 @@ const IOT_INFO: SubDaoType = {
maxDescription:
"This is an upper limit that will not be reached and does not consider future IOT burn. Reason: Daily emissions are currently only 93% of scheduled emissions, as oracles do not currently receive rewards.",
activeDetails: "",
maxSupply: IOT_MAX_SUPPLY,
}

export const SubDaoInfo = async ({ subDao }: { subDao: SubDao }) => {
Expand All @@ -67,6 +71,7 @@ export const SubDaoInfo = async ({ subDao }: { subDao: SubDao }) => {
icon,
subDaoMint,
maxDescription,
maxSupply,
} = subDao === "mobile" ? MOBILE_INFO : IOT_INFO
const [activeCount, mintInfo, epochInfo, treasuryInfo, governanceMetrics] =
await Promise.all([
Expand All @@ -86,7 +91,7 @@ export const SubDaoInfo = async ({ subDao }: { subDao: SubDao }) => {
const remainingEmissions = Math.round(
getRemainingEmissions(new Date(), subDao)
)
const maxSupply =
const supplyLimit =
mintInfo.info?.info.supply! + BigInt(remainingEmissions) * BigInt(1000000)

const supplyStaked = governanceMetrics.total.hnt
Expand Down Expand Up @@ -173,15 +178,24 @@ export const SubDaoInfo = async ({ subDao }: { subDao: SubDao }) => {
}}
/>
<StatItem
label="Max Supply"
label="Supply Limit"
value={humanReadableBigint(
maxSupply,
supplyLimit,
mintInfo?.info?.info.decimals || 0,
0
)}
tooltip={{
description: `Maximum supply of ${title} derived by current supply plus remaining emissions. ${maxDescription}`,
cadence: "Live",
id: `${title} Supply Limit`,
}}
/>
<StatItem
label="Max Supply"
value={maxSupply.toLocaleString()}
tooltip={{
description: `Maximum supply of ${title} that can ever exist as defined in HIPs. This is an upper limit that will not be reached and does not consider ${title} past or future burn.`,
cadence: "Fixed",
id: `${title} Max Supply`,
}}
/>
Expand Down
3 changes: 3 additions & 0 deletions src/app/stats/utils/emissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const HNT_MAX_SUPPLY = 223_000_000
export const IOT_MAX_SUPPLY = 200_000_000_000
export const MOBILE_MAX_SUPPLY = 200_000_000_000
2 changes: 1 addition & 1 deletion src/knex/maxSupply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class MaxSupply {
this.knex = knex
}

private async getLatest({
async getLatest({
withBurn,
}: {
withBurn: boolean
Expand Down