use crate::{
agent::build_ic_agent, ic_registry::MAINNET_GOVERNANCE_CANISTER_ID, nns::NnsSourceRequest,
};
use candid::{CandidType, Deserialize, Principal};
use thiserror::Error as ThisError;
#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
pub enum NnsGovernanceQueryError {
#[error("failed to build IC agent for {endpoint}: {reason}")]
AgentBuild {
endpoint: String,
reason: String,
},
#[error("NNS Governance agent call {method} failed: {reason}")]
AgentCall {
method: &'static str,
reason: String,
},
#[error("failed to encode Candid {message}: {reason}")]
CandidEncode {
message: &'static str,
reason: String,
},
#[error("failed to decode Candid {message}: {reason}")]
CandidDecode {
message: &'static str,
reason: String,
},
}
pub async fn query_nns_governance<Arg, Response>(
request: &NnsSourceRequest,
method: &'static str,
request_message: &'static str,
response_message: &'static str,
arg: &Arg,
) -> Result<Response, NnsGovernanceQueryError>
where
Arg: CandidType + Sync,
Response: for<'de> Deserialize<'de> + CandidType,
{
let arg = candid::encode_one(arg).map_err(|error| NnsGovernanceQueryError::CandidEncode {
message: request_message,
reason: error.to_string(),
})?;
let bytes = query_nns_governance_bytes(request, method, arg).await?;
decode_response(&bytes, response_message)
}
pub async fn query_nns_governance_no_args<Response>(
request: &NnsSourceRequest,
method: &'static str,
response_message: &'static str,
) -> Result<Response, NnsGovernanceQueryError>
where
Response: for<'de> Deserialize<'de> + CandidType,
{
let arg = candid::encode_args(()).map_err(|error| NnsGovernanceQueryError::CandidEncode {
message: "()",
reason: error.to_string(),
})?;
let bytes = query_nns_governance_bytes(request, method, arg).await?;
decode_response(&bytes, response_message)
}
async fn query_nns_governance_bytes(
request: &NnsSourceRequest,
method: &'static str,
arg: Vec<u8>,
) -> Result<Vec<u8>, NnsGovernanceQueryError> {
let agent = build_ic_agent(&request.endpoint, |reason| {
NnsGovernanceQueryError::AgentBuild {
endpoint: request.endpoint.clone(),
reason,
}
})?;
let canister = Principal::from_text(MAINNET_GOVERNANCE_CANISTER_ID).map_err(|error| {
NnsGovernanceQueryError::CandidDecode {
message: "governance_canister_id",
reason: error.to_string(),
}
})?;
agent
.query(&canister, method)
.with_arg(arg)
.call()
.await
.map_err(|error| NnsGovernanceQueryError::AgentCall {
method,
reason: error.to_string(),
})
}
fn decode_response<Response>(
bytes: &[u8],
response_message: &'static str,
) -> Result<Response, NnsGovernanceQueryError>
where
Response: for<'de> Deserialize<'de> + CandidType,
{
candid::decode_one(bytes).map_err(|error| NnsGovernanceQueryError::CandidDecode {
message: response_message,
reason: error.to_string(),
})
}