Skip to content

Commit 138d204

Browse files
authored
Merge pull request #373 from anoma/brent/circ-supply
Circulating supply
2 parents c95c923 + c246d83 commit 138d204

File tree

6 files changed

+122
-3
lines changed

6 files changed

+122
-3
lines changed

swagger.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,24 @@ paths:
798798
application/json:
799799
schema:
800800
$ref: "#/components/schemas/TokenSupply"
801+
/api/v1/chain/circulating-supply:
802+
get:
803+
summary: Get the circulating supply of the native token at the given epoch
804+
description: Calculates circulating supply by subtracting locked amounts from multiple excluded addresses from the effective supply
805+
parameters:
806+
- in: query
807+
name: epoch
808+
schema:
809+
type: integer
810+
minimum: 0
811+
description: Epoch to query
812+
responses:
813+
"200":
814+
description: Circulating supply of the native token
815+
content:
816+
application/json:
817+
schema:
818+
$ref: "#/components/schemas/CirculatingSupply"
801819
/api/v1/chain/parameters:
802820
get:
803821
summary: Get chain parameters
@@ -1295,6 +1313,13 @@ components:
12951313
type: string
12961314
effectiveSupply:
12971315
type: string
1316+
CirculatingSupply:
1317+
type: object
1318+
required: [circulatingSupply]
1319+
properties:
1320+
circulatingSupply:
1321+
type: string
1322+
description: Circulating supply of the native token (effective supply minus locked supply)
12981323
Parameters:
12991324
type: object
13001325
required:

webserver/src/app.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ impl ApplicationServer {
144144
"/chain/token-supply",
145145
get(chain_handlers::get_token_supply),
146146
)
147+
.route(
148+
"/chain/circulating-supply",
149+
get(chain_handlers::get_circulating_supply),
150+
)
147151
.route(
148152
"/chain/block/latest",
149153
get(chain_handlers::get_last_processed_block),

webserver/src/dto/chain.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,10 @@ pub struct TokenSupply {
88
pub epoch: Option<i32>,
99
pub address: String,
1010
}
11+
12+
#[derive(Clone, Serialize, Deserialize, Validate)]
13+
#[serde(rename_all = "camelCase")]
14+
pub struct CirculatingSupply {
15+
#[validate(range(min = 0))]
16+
pub epoch: Option<i32>,
17+
}

webserver/src/handler/chain.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ use axum_extra::extract::Query;
1010
use futures::Stream;
1111
use tokio_stream::StreamExt;
1212

13-
use crate::dto::chain::TokenSupply as TokenSupplyDto;
13+
use crate::dto::chain::{
14+
CirculatingSupply as CirculatingSupplyDto, TokenSupply as TokenSupplyDto,
15+
};
1416
use crate::error::api::ApiError;
1517
use crate::response::chain::{
16-
LastProcessedBlock, LastProcessedEpoch, Parameters, RpcUrl, Token,
18+
CirculatingSupply as CirculatingSupplyRsp, LastProcessedBlock,
19+
LastProcessedEpoch, Parameters, RpcUrl, Token,
1720
TokenSupply as TokenSupplyRsp,
1821
};
1922
use crate::state::common::CommonState;
@@ -113,3 +116,14 @@ pub async fn get_token_supply(
113116
.await?;
114117
Ok(Json(supply))
115118
}
119+
120+
pub async fn get_circulating_supply(
121+
Query(query): Query<CirculatingSupplyDto>,
122+
State(state): State<CommonState>,
123+
) -> Result<Json<CirculatingSupplyRsp>, ApiError> {
124+
let supply = state
125+
.chain_service
126+
.get_circulating_supply(query.epoch)
127+
.await?;
128+
Ok(Json(supply))
129+
}

webserver/src/response/chain.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,9 @@ pub struct TokenSupply {
113113
pub total_supply: String,
114114
pub effective_supply: Option<String>,
115115
}
116+
117+
#[derive(Clone, Debug, Deserialize, Serialize)]
118+
#[serde(rename_all = "camelCase")]
119+
pub struct CirculatingSupply {
120+
pub circulating_supply: String,
121+
}

webserver/src/service/chain.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use bigdecimal::BigDecimal;
12
use shared::id::Id;
23
use shared::token::{IbcToken, Token};
34

45
use crate::appstate::AppState;
56
use crate::error::chain::ChainError;
7+
use crate::repository::balance::{BalanceRepo, BalanceRepoTrait};
68
use crate::repository::chain::{ChainRepository, ChainRepositoryTrait};
7-
use crate::response::chain::{Parameters, TokenSupply};
9+
use crate::response::chain::{CirculatingSupply, Parameters, TokenSupply};
810

911
#[derive(Clone)]
1012
pub struct ChainService {
@@ -83,4 +85,65 @@ impl ChainService {
8385
effective_supply: supply.effective.map(|s| s.to_string()),
8486
}))
8587
}
88+
89+
pub async fn get_circulating_supply(
90+
&self,
91+
epoch: Option<i32>,
92+
) -> Result<CirculatingSupply, ChainError> {
93+
// Native token address and addresses to exclude from circulating supply
94+
let native_token_address =
95+
"tnam1q9gr66cvu4hrzm0sd5kmlnjje82gs3xlfg3v6nu7";
96+
let excluded_addresses = vec![
97+
"tnam1qxdzup2hcvhswcgw5kerd5lfkf04t64y3scgqm5v",
98+
"tnam1qxt7uxhj9r00mfm4u870e7ghz6j20jrdz58gm5kj",
99+
"tnam1qyez9fd9nkaxfj4u2f2k0vavr8mm69azcgds45rr",
100+
"tnam1qqp69rzwsgnqdm0d4qfhw4qa4s6v3tlzm5069f4j",
101+
"tnam1qrucghh3hw2zq8xtqzdj44nh5nrmnkn0usqng8yq",
102+
];
103+
104+
// Get total supply of native token
105+
let total_supply_result = self
106+
.chain_repo
107+
.get_token_supply(native_token_address.to_string(), epoch)
108+
.await
109+
.map_err(ChainError::Database)?;
110+
111+
let total_supply = total_supply_result.ok_or_else(|| {
112+
ChainError::Unknown("Native token supply not found".to_string())
113+
})?;
114+
115+
// Get balances for all excluded addresses and sum them up
116+
let balance_repo = BalanceRepo::new(self.chain_repo.app_state.clone());
117+
let mut total_locked_amount = BigDecimal::from(0);
118+
119+
for address in excluded_addresses {
120+
let balances = balance_repo
121+
.get_address_balances(address.to_string())
122+
.await
123+
.map_err(ChainError::Database)?;
124+
125+
// Find the balance of the native token for this excluded address
126+
let locked_amount = balances
127+
.iter()
128+
.find(|balance| balance.token == native_token_address)
129+
.map(|balance| balance.raw_amount.clone())
130+
.unwrap_or_else(|| BigDecimal::from(0));
131+
132+
total_locked_amount += locked_amount;
133+
}
134+
135+
// Calculate circulating supply = effective supply - total locked amount
136+
let effective_supply_amount =
137+
total_supply.effective.ok_or_else(|| {
138+
ChainError::Unknown(
139+
"Effective supply not found for native token".to_string(),
140+
)
141+
})?;
142+
let circulating_amount =
143+
&effective_supply_amount - &total_locked_amount;
144+
145+
Ok(CirculatingSupply {
146+
circulating_supply: circulating_amount.to_string(),
147+
})
148+
}
86149
}

0 commit comments

Comments
 (0)