sui_indexer_alt_jsonrpc/api/
governance.rsuse anyhow::Context as _;
use diesel::{ExpressionMethods, QueryDsl};
use jsonrpsee::{core::RpcResult, http_client::HttpClient, proc_macros::rpc};
use sui_indexer_alt_schema::schema::kv_epoch_starts;
use sui_json_rpc_api::GovernanceReadApiClient;
use sui_json_rpc_types::{DelegatedStake, ValidatorApys};
use sui_open_rpc::Module;
use sui_open_rpc_macros::open_rpc;
use sui_types::{
base_types::{ObjectID, SuiAddress},
dynamic_field::{derive_dynamic_field_id, Field},
sui_serde::BigInt,
sui_system_state::{
sui_system_state_inner_v1::SuiSystemStateInnerV1,
sui_system_state_inner_v2::SuiSystemStateInnerV2,
sui_system_state_summary::SuiSystemStateSummary, SuiSystemStateTrait,
SuiSystemStateWrapper,
},
TypeTag, SUI_SYSTEM_STATE_OBJECT_ID,
};
use crate::{
config::NodeConfig,
context::Context,
data::load_live_deserialized,
error::{client_error_to_error_object, rpc_bail, RpcError},
};
use super::rpc_module::RpcModule;
#[open_rpc(namespace = "suix", tag = "Governance API")]
#[rpc(server, namespace = "suix")]
trait GovernanceApi {
#[method(name = "getReferenceGasPrice")]
async fn get_reference_gas_price(&self) -> RpcResult<BigInt<u64>>;
#[method(name = "getLatestSuiSystemState")]
async fn get_latest_sui_system_state(&self) -> RpcResult<SuiSystemStateSummary>;
}
#[open_rpc(namespace = "suix", tag = "Delegation Governance API")]
#[rpc(server, namespace = "suix")]
trait DelegationGovernanceApi {
#[method(name = "getStakesByIds")]
async fn get_stakes_by_ids(
&self,
staked_sui_ids: Vec<ObjectID>,
) -> RpcResult<Vec<DelegatedStake>>;
#[method(name = "getStakes")]
async fn get_stakes(&self, owner: SuiAddress) -> RpcResult<Vec<DelegatedStake>>;
#[method(name = "getValidatorsApy")]
async fn get_validators_apy(&self) -> RpcResult<ValidatorApys>;
}
pub(crate) struct Governance(pub Context);
pub(crate) struct DelegationGovernance(HttpClient);
impl DelegationGovernance {
pub fn new(fullnode_rpc_url: url::Url, config: NodeConfig) -> anyhow::Result<Self> {
let client = config.client(fullnode_rpc_url)?;
Ok(Self(client))
}
}
#[async_trait::async_trait]
impl GovernanceApiServer for Governance {
async fn get_reference_gas_price(&self) -> RpcResult<BigInt<u64>> {
Ok(rgp_response(&self.0).await?)
}
async fn get_latest_sui_system_state(&self) -> RpcResult<SuiSystemStateSummary> {
Ok(latest_sui_system_state_response(&self.0).await?)
}
}
#[async_trait::async_trait]
impl DelegationGovernanceApiServer for DelegationGovernance {
async fn get_stakes_by_ids(
&self,
staked_sui_ids: Vec<ObjectID>,
) -> RpcResult<Vec<DelegatedStake>> {
let Self(client) = self;
client
.get_stakes_by_ids(staked_sui_ids)
.await
.map_err(client_error_to_error_object)
}
async fn get_stakes(&self, owner: SuiAddress) -> RpcResult<Vec<DelegatedStake>> {
let Self(client) = self;
client
.get_stakes(owner)
.await
.map_err(client_error_to_error_object)
}
async fn get_validators_apy(&self) -> RpcResult<ValidatorApys> {
let Self(client) = self;
client
.get_validators_apy()
.await
.map_err(client_error_to_error_object)
}
}
impl RpcModule for Governance {
fn schema(&self) -> Module {
GovernanceApiOpenRpc::module_doc()
}
fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
self.into_rpc()
}
}
impl RpcModule for DelegationGovernance {
fn schema(&self) -> Module {
DelegationGovernanceApiOpenRpc::module_doc()
}
fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
self.into_rpc()
}
}
async fn rgp_response(ctx: &Context) -> Result<BigInt<u64>, RpcError> {
use kv_epoch_starts::dsl as e;
let mut conn = ctx
.pg_reader()
.connect()
.await
.context("Failed to connect to the database")?;
let rgp: i64 = conn
.first(
e::kv_epoch_starts
.select(e::reference_gas_price)
.order(e::epoch.desc()),
)
.await
.context("Failed to fetch the reference gas price")?;
Ok((rgp as u64).into())
}
async fn latest_sui_system_state_response(
ctx: &Context,
) -> Result<SuiSystemStateSummary, RpcError> {
let wrapper: SuiSystemStateWrapper = load_live_deserialized(ctx, SUI_SYSTEM_STATE_OBJECT_ID)
.await
.context("Failed to fetch system state wrapper object")?;
let inner_id = derive_dynamic_field_id(
SUI_SYSTEM_STATE_OBJECT_ID,
&TypeTag::U64,
&bcs::to_bytes(&wrapper.version).context("Failed to serialize system state version")?,
)
.context("Failed to derive inner system state field ID")?;
Ok(match wrapper.version {
1 => load_live_deserialized::<Field<u64, SuiSystemStateInnerV1>>(ctx, inner_id)
.await
.context("Failed to fetch inner system state object")?
.value
.into_sui_system_state_summary(),
2 => load_live_deserialized::<Field<u64, SuiSystemStateInnerV2>>(ctx, inner_id)
.await
.context("Failed to fetch inner system state object")?
.value
.into_sui_system_state_summary(),
v => rpc_bail!("Unexpected inner system state version: {v}"),
})
}