sui_indexer_alt_jsonrpc/api/
governance.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::Context as _;
5use diesel::{ExpressionMethods, QueryDsl};
6
7use jsonrpsee::{core::RpcResult, http_client::HttpClient, proc_macros::rpc};
8use sui_indexer_alt_schema::schema::kv_epoch_starts;
9use sui_json_rpc_api::GovernanceReadApiClient;
10use sui_json_rpc_types::{DelegatedStake, ValidatorApys};
11use sui_open_rpc::Module;
12use sui_open_rpc_macros::open_rpc;
13use sui_types::{
14    SUI_SYSTEM_STATE_OBJECT_ID, TypeTag,
15    base_types::{ObjectID, SuiAddress},
16    dynamic_field::{Field, derive_dynamic_field_id},
17    sui_serde::BigInt,
18    sui_system_state::{
19        SuiSystemStateTrait, SuiSystemStateWrapper,
20        sui_system_state_inner_v1::SuiSystemStateInnerV1,
21        sui_system_state_inner_v2::SuiSystemStateInnerV2,
22        sui_system_state_summary::SuiSystemStateSummary,
23    },
24};
25
26use crate::{
27    context::Context,
28    data::load_live_deserialized,
29    error::{RpcError, client_error_to_error_object, rpc_bail},
30};
31
32use super::rpc_module::RpcModule;
33
34#[open_rpc(namespace = "suix", tag = "Governance API")]
35#[rpc(server, namespace = "suix")]
36trait GovernanceApi {
37    /// Return the reference gas price for the network as of the latest epoch.
38    #[method(name = "getReferenceGasPrice")]
39    async fn get_reference_gas_price(&self) -> RpcResult<BigInt<u64>>;
40
41    /// Return a summary of the latest version of the Sui System State object (0x5), on-chain.
42    #[method(name = "getLatestSuiSystemState")]
43    async fn get_latest_sui_system_state(&self) -> RpcResult<SuiSystemStateSummary>;
44}
45
46#[open_rpc(namespace = "suix", tag = "Delegation Governance API")]
47#[rpc(server, namespace = "suix")]
48trait DelegationGovernanceApi {
49    /// Return one or more [DelegatedStake]. If a Stake was withdrawn its status will be Unstaked.
50    #[method(name = "getStakesByIds")]
51    async fn get_stakes_by_ids(
52        &self,
53        staked_sui_ids: Vec<ObjectID>,
54    ) -> RpcResult<Vec<DelegatedStake>>;
55
56    /// Return all [DelegatedStake].
57    #[method(name = "getStakes")]
58    async fn get_stakes(&self, owner: SuiAddress) -> RpcResult<Vec<DelegatedStake>>;
59
60    /// Return the validator APY
61    #[method(name = "getValidatorsApy")]
62    async fn get_validators_apy(&self) -> RpcResult<ValidatorApys>;
63}
64
65pub(crate) struct Governance(pub Context);
66pub(crate) struct DelegationGovernance(HttpClient);
67
68impl DelegationGovernance {
69    pub(crate) fn new(client: HttpClient) -> Self {
70        Self(client)
71    }
72}
73
74#[async_trait::async_trait]
75impl GovernanceApiServer for Governance {
76    async fn get_reference_gas_price(&self) -> RpcResult<BigInt<u64>> {
77        Ok(rgp_response(&self.0).await?)
78    }
79
80    async fn get_latest_sui_system_state(&self) -> RpcResult<SuiSystemStateSummary> {
81        Ok(latest_sui_system_state_response(&self.0).await?)
82    }
83}
84
85#[async_trait::async_trait]
86impl DelegationGovernanceApiServer for DelegationGovernance {
87    async fn get_stakes_by_ids(
88        &self,
89        staked_sui_ids: Vec<ObjectID>,
90    ) -> RpcResult<Vec<DelegatedStake>> {
91        let Self(client) = self;
92
93        client
94            .get_stakes_by_ids(staked_sui_ids)
95            .await
96            .map_err(client_error_to_error_object)
97    }
98
99    async fn get_stakes(&self, owner: SuiAddress) -> RpcResult<Vec<DelegatedStake>> {
100        let Self(client) = self;
101
102        client
103            .get_stakes(owner)
104            .await
105            .map_err(client_error_to_error_object)
106    }
107
108    async fn get_validators_apy(&self) -> RpcResult<ValidatorApys> {
109        let Self(client) = self;
110
111        client
112            .get_validators_apy()
113            .await
114            .map_err(client_error_to_error_object)
115    }
116}
117
118impl RpcModule for Governance {
119    fn schema(&self) -> Module {
120        GovernanceApiOpenRpc::module_doc()
121    }
122
123    fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
124        self.into_rpc()
125    }
126}
127
128impl RpcModule for DelegationGovernance {
129    fn schema(&self) -> Module {
130        DelegationGovernanceApiOpenRpc::module_doc()
131    }
132
133    fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
134        self.into_rpc()
135    }
136}
137
138/// Load data and generate response for `getReferenceGasPrice`.
139async fn rgp_response(ctx: &Context) -> Result<BigInt<u64>, RpcError> {
140    use kv_epoch_starts::dsl as e;
141
142    let mut conn = ctx
143        .pg_reader()
144        .connect()
145        .await
146        .context("Failed to connect to the database")?;
147
148    let rgp: i64 = conn
149        .first(
150            e::kv_epoch_starts
151                .select(e::reference_gas_price)
152                .order(e::epoch.desc()),
153        )
154        .await
155        .context("Failed to fetch the reference gas price")?;
156
157    Ok((rgp as u64).into())
158}
159
160/// Load data and generate response for `getLatestSuiSystemState`.
161async fn latest_sui_system_state_response(
162    ctx: &Context,
163) -> Result<SuiSystemStateSummary, RpcError> {
164    let wrapper: SuiSystemStateWrapper = load_live_deserialized(ctx, SUI_SYSTEM_STATE_OBJECT_ID)
165        .await
166        .context("Failed to fetch system state wrapper object")?;
167
168    let inner_id = derive_dynamic_field_id(
169        SUI_SYSTEM_STATE_OBJECT_ID,
170        &TypeTag::U64,
171        &bcs::to_bytes(&wrapper.version).context("Failed to serialize system state version")?,
172    )
173    .context("Failed to derive inner system state field ID")?;
174
175    Ok(match wrapper.version {
176        1 => load_live_deserialized::<Field<u64, SuiSystemStateInnerV1>>(ctx, inner_id)
177            .await
178            .context("Failed to fetch inner system state object")?
179            .value
180            .into_sui_system_state_summary(),
181        2 => load_live_deserialized::<Field<u64, SuiSystemStateInnerV2>>(ctx, inner_id)
182            .await
183            .context("Failed to fetch inner system state object")?
184            .value
185            .into_sui_system_state_summary(),
186        v => rpc_bail!("Unexpected inner system state version: {v}"),
187    })
188}