Skip to content

Commit 9df625e

Browse files
committed
Merge branch 'main' of github.com:Concordium/concordium-scan into lma/fix/contracts_prev_and_next
2 parents 0b037b0 + 0cda867 commit 9df625e

7 files changed

+125
-21
lines changed

backend-rust/.sqlx/query-2dcfc5856b53004f9b540c16fe88a8de67a8485e03e290da063dc7c0499dfc79.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend-rust/.sqlx/query-5421d1d2f9a076d85a10bdb368c467f9b6698e942b8e29e62bd69f688010fc6d.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend-rust/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Database schema version: 2
88

99
### Added
1010

11+
- Add `delegator_count`, `delegated_stake`, `total_stake`, and `total_stake_percentage` to baker pool query.
12+
- Add index over accounts that delegate stake to a given baker pool.
1113
- Add `database_schema_version` and `api_supported_database_schema_version` to `versions` endpoint.
1214
- Add database schema version 2 with index over blocks with no `cumulative_finalization_time`, to improve indexing performance.
1315

backend-rust/src/graphql_api/baker.rs

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ impl Baker {
110110

111111
async fn baker_id(&self) -> BakerId { self.id }
112112

113-
async fn state<'a>(&'a self) -> ApiResult<BakerState<'a>> {
113+
async fn state<'a>(&'a self, ctx: &Context<'a>) -> ApiResult<BakerState<'a>> {
114+
let pool = get_pool(ctx)?;
115+
114116
let transaction_commission = self
115117
.transaction_commission
116118
.map(u32::try_from)
@@ -127,17 +129,53 @@ impl Baker {
127129
.transpose()?
128130
.map(|c| AmountFraction::new_unchecked(c).into());
129131

132+
let total_stake: i64 =
133+
sqlx::query_scalar!("SELECT total_staked FROM blocks ORDER BY height DESC LIMIT 1")
134+
.fetch_one(pool)
135+
.await?;
136+
137+
let row = sqlx::query!(
138+
"
139+
SELECT
140+
COUNT(*) AS delegator_count,
141+
SUM(delegated_stake)::BIGINT AS delegated_stake
142+
FROM accounts
143+
WHERE delegated_target_baker_id = $1
144+
",
145+
self.id.0
146+
)
147+
.fetch_one(pool)
148+
.await?;
149+
150+
let delegated_stake = row.delegated_stake.unwrap_or(0);
151+
152+
// The total amount staked in this baker pool includes the baker stake
153+
// and the delegated stake.
154+
let total_pool_stake = self.staked + delegated_stake;
155+
156+
// Division by 0 is not possible because `total_staked` is always a positive
157+
// number.
158+
let total_stake_percentage = (rust_decimal::Decimal::from(total_pool_stake)
159+
* rust_decimal::Decimal::from(100))
160+
.checked_div(rust_decimal::Decimal::from(total_stake))
161+
.ok_or_else(|| ApiError::InternalError("Division by zero".to_string()))?
162+
.into();
163+
130164
let out = BakerState::ActiveBakerState(ActiveBakerState {
131165
staked_amount: Amount::try_from(self.staked)?,
132166
restake_earnings: self.restake_earnings,
133167
pool: BakerPool {
134-
open_status: self.open_status,
168+
open_status: self.open_status,
135169
commission_rates: CommissionRates {
136170
transaction_commission,
137171
baking_commission,
138172
finalization_commission,
139173
},
140-
metadata_url: self.metadata_url.as_deref(),
174+
metadata_url: self.metadata_url.as_deref(),
175+
total_stake_percentage,
176+
total_stake: total_pool_stake.try_into()?,
177+
delegated_stake: delegated_stake.try_into()?,
178+
delegator_count: row.delegator_count.unwrap_or(0),
141179
},
142180
pending_change: None, // This is not used starting from P7.
143181
});
@@ -338,29 +376,28 @@ enum BakerSort {
338376

339377
#[derive(SimpleObject)]
340378
struct BakerPool<'a> {
341-
// /// Total stake of the baker pool as a percentage of all CCDs in existence.
342-
// /// Value may be null for brand new bakers where statistics have not
343-
// /// been calculated yet. This should be rare and only a temporary
344-
// /// condition.
345-
// total_stake_percentage: Decimal,
379+
/// Total stake of the baker pool as a percentage of all CCDs in existence.
380+
/// Includes both baker stake and delegated stake.
381+
total_stake_percentage: Decimal,
382+
/// The total amount staked in this baker pool. Includes both baker stake
383+
/// and delegated stake.
384+
total_stake: Amount,
385+
/// The total amount staked by delegators to this baker pool.
386+
delegated_stake: Amount,
387+
/// The number of delegators that delegate to this baker pool.
388+
delegator_count: i64,
346389
// lottery_power: Decimal,
347390
// payday_commission_rates: CommissionRates,
348-
open_status: Option<BakerPoolOpenStatus>,
349-
commission_rates: CommissionRates,
350-
metadata_url: Option<&'a str>,
351-
// /// The total amount staked by delegation to this baker pool.
352-
// delegated_stake: Amount,
353-
// /// The maximum amount that may be delegated to the pool, accounting for
354-
// /// leverage and stake limits.
355-
// delegated_stake_cap: Amount,
356-
// /// The total amount staked in this baker pool. Includes both baker stake
357-
// /// and delegated stake.
358-
// total_stake: Amount,
359-
// delegator_count: i32,
360391
// /// Ranking of the baker pool by total staked amount. Value may be null for
361392
// /// brand new bakers where statistics have not been calculated yet. This
362393
// /// should be rare and only a temporary condition.
363394
// ranking_by_total_stake: Ranking,
395+
// /// The maximum amount that may be delegated to the pool, accounting for
396+
// /// leverage and stake limits.
397+
// delegated_stake_cap: Amount,
398+
open_status: Option<BakerPoolOpenStatus>,
399+
commission_rates: CommissionRates,
400+
metadata_url: Option<&'a str>,
364401
// TODO: apy(period: ApyPeriod!): PoolApy!
365402
// TODO: delegators("Returns the first _n_ elements from the list." first: Int "Returns the
366403
// elements in the list that come after the specified cursor." after: String "Returns the last

backend-rust/src/migrations/m0001-initialize.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ CREATE TABLE accounts(
262262
-- Starting at 1 to count the transaction that made the account.
263263
DEFAULT 1,
264264
-- The total delegated stake of this account in micro CCD.
265+
-- An account can delegate stake to at most one baker pool.
265266
delegated_stake
266267
BIGINT
267268
NOT NULL
@@ -271,6 +272,7 @@ CREATE TABLE accounts(
271272
BOOLEAN
272273
NULL,
273274
-- Target id of the baker When this is null it means that we are using passive delegation.
275+
-- An account can delegate stake to at most one baker pool.
274276
delegated_target_baker_id
275277
BIGINT
276278
NULL
@@ -319,7 +321,7 @@ CREATE TABLE bakers(
319321
BIGINT
320322
PRIMARY KEY
321323
REFERENCES accounts,
322-
-- Amount staked at present.
324+
-- Amount staked at present in this baker pool by the baker (no delegated stake included).
323325
staked
324326
BIGINT
325327
NOT NULL,

backend-rust/src/migrations/m0002-block-cumulative-fin-time-index.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
CREATE INDEX blocks_height_null_cumulative_finalization_time ON blocks (height)
44
WHERE blocks.cumulative_finalization_time IS NULL
55
AND blocks.finalization_time IS NOT NULL;
6+
7+
-- Important for quickly calculating the delegated stake to a baker pool.
8+
CREATE INDEX delegated_target_baker_id_index ON accounts(delegated_target_baker_id);

backend-rust/src/scalar_types.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use anyhow::Context;
22
use async_graphql::{scalar, InputValueError, InputValueResult, Scalar, ScalarType, Value};
3+
use rust_decimal::prelude::FromPrimitive;
34

45
pub type Amount = UnsignedLong;
56
pub type TokenId = String;
@@ -132,6 +133,17 @@ impl From<concordium_rust_sdk::types::AmountFraction> for Decimal {
132133
}
133134
}
134135

136+
impl TryFrom<f64> for Decimal {
137+
type Error = anyhow::Error;
138+
139+
fn try_from(fraction: f64) -> Result<Self, Self::Error> {
140+
Ok(Self(
141+
rust_decimal::Decimal::from_f64(fraction)
142+
.ok_or(anyhow::anyhow!("Failed to convert f64 fraction to Decimal"))?,
143+
))
144+
}
145+
}
146+
135147
/// The `TimeSpan` scalar represents an ISO-8601 compliant duration type.
136148
#[derive(serde::Serialize, serde::Deserialize, Clone)]
137149
#[repr(transparent)]

0 commit comments

Comments
 (0)