Skip to content

fix(api-server): add total supply endpoint#1020

Merged
glottologist merged 6 commits into
masterfrom
jason/token_supply_endpoint
Nov 22, 2025
Merged

fix(api-server): add total supply endpoint#1020
glottologist merged 6 commits into
masterfrom
jason/token_supply_endpoint

Conversation

@glottologist

Copy link
Copy Markdown
Contributor

Describe the changes
Before:
No HTTP endpoint existed to query total token supply (genesis allocation + emissions).

After:
Implements /v1/supply GET endpoint returning total supply, genesis allocation, emitted tokens, and inflation progress. Supports two modes: actual (sums block rewards from canonical chain) and estimated (uses exponential decay formula).

Changes

API Server (crates/api-server)

  • Added src/routes/supply.rs (195 lines) with supply calculation logic

Supply Endpoint (src/routes/supply.rs)

  • Implements supply() handler with query parameter ?estimate=true|false (default: false)
  • Returns SupplyResponse with camelCase JSON fields:
    • totalSupply, totalSupplyBillions
    • genesisSupply, genesisSupplyBillions
    • emittedSupply, emittedSupplyBillions
    • timestampMillis, blockHeight
    • inflationCap, inflationCapBillions
    • inflationProgressPercent
    • calculationMethod ("actual" or "estimated")

Calculation Modes

  • Actual mode (default): Iterates canonical chain, sums reward_amount from all blocks (O(n), 100% accurate)
  • Estimated mode (?estimate=true): Uses HalvingCurve::total_emitted_estimated() formula (O(1), relatively accurate)

API Usage

 # Actual supply (default) - sums all block rewards from canonical chain
curl http://localhost:3000/v1/supply

# Estimated supply - uses exponential decay formula (faster)
curl http://localhost:3000/v1/supply?estimate=true

# Pretty print JSON response
curl http://localhost:3000/v1/supply | jq

Example Response:

{
  "totalSupply": "10500000000000000000000000000",
  "totalSupplyBillions": "10.500000",
  "genesisSupply": "10000000000000000000000000000",
  "genesisSupplyBillions": "10.000000",
  "emittedSupply": "500000000000000000000000000",
  "emittedSupplyBillions": "0.500000",
  "timestampMillis": 1732217580000,
  "blockHeight": 12345,
  "inflationCap": "1300000000000000000000000000",
  "inflationCapBillions": "1.300000",
  "inflationProgressPercent": "38.46",
  "calculationMethod": "actual"
}

Related Issue(s)
Please link to the issue(s) that will be closed with this PR.

Checklist

  • Tests have been added/updated for the changes.
  • Documentation has been updated for the changes (if applicable).
  • The code follows Rust's style guidelines.

Additional Context
Add any other context about the pull request here.

@glottologist glottologist marked this pull request as ready for review November 21, 2025 20:14
Comment thread crates/api-server/src/routes/supply.rs Outdated

use crate::ApiState;

const TOKEN_DECIMALS: u32 = 18;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to have this value scattered across the codebase.

const TOKEN_SCALE_NATIVE: u64 = 1_000_000_000_000_000_000_u64;

Probably needs to be pulled into a consensus param. Fine to hardcode now for expediency.

@DanMacDonald DanMacDonald left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

estimation code should be tweaked to be block height based. Also, I think the GenesisRelativeTimestamp type was removed as rewards really shouldn't be timestamp based.

Comment thread crates/api-server/src/routes/supply.rs Outdated
});

let (emitted_amount, calculation_method) = if use_estimate {
let elapsed =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is no longer timestamp based...

let previous_height = prev_block_header.height();
let target_block_time_seconds = self
.inner()
.config
.consensus
.difficulty_adjustment
.block_time;
// Use height * target_block_time for consistent reward curve positioning
let previous_block_seconds = (previous_height * target_block_time_seconds) as u128;
let current_block_seconds = previous_block_seconds + target_block_time_seconds as u128;
let reward_amount = self
.inner()
.reward_curve
.reward_between(previous_block_seconds, current_block_seconds)?;
Ok(reward_amount)

^ it should be block height based, this is how we simulate it.

Comment thread crates/api-server/src/routes/supply.rs Outdated
#[serde(rename_all = "camelCase")]
pub struct SupplyResponse {
pub total_supply: String,
pub total_supply_billions: String,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need these billion fields, I'd just remove them

Comment thread crates/api-server/src/routes/supply.rs Outdated
#[derive(Debug, Deserialize)]
pub struct SupplyQuery {
#[serde(default)]
pub estimate: bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this to default to the estimate

Comment thread crates/api-server/src/routes/supply.rs Outdated
pub genesis_supply_billions: String,
pub emitted_supply: String,
pub emitted_supply_billions: String,
pub timestamp_millis: u128,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also don't need this field, as it's not time-based

@glottologist glottologist force-pushed the jason/token_supply_endpoint branch from 9447027 to 5c89cea Compare November 22, 2025 11:05
@JesseTheRobot JesseTheRobot force-pushed the jason/token_supply_endpoint branch from 6a0ee91 to 5c89cea Compare November 22, 2025 12:20

@JesseTheRobot JesseTheRobot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but this needs approval from @DanMacDonald before merging.

@DanMacDonald DanMacDonald left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though I suspect the merge with master will be a mildly annoying refactor.

@glottologist glottologist merged commit 35711f2 into master Nov 22, 2025
16 of 17 checks passed
@glottologist glottologist deleted the jason/token_supply_endpoint branch November 22, 2025 18:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants