poseidon_client/rpc_client/
rpc.rs

1use crate::{Cluster, Commitment, PoseidonResult, RpcTxError, SendTxResponse, Transaction};
2use borsh::{BorshDeserialize, BorshSerialize};
3use json::JsonValue;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug)]
7pub struct RpcClient {
8    cluster: Cluster,
9    headers: Vec<(String, String)>,
10    body: JsonValue,
11    commitment: Commitment,
12}
13
14impl RpcClient {
15    pub fn new() -> Self {
16        RpcClient {
17            cluster: Cluster::default(),
18            headers: vec![("Content-Type".to_owned(), "application/json".to_owned())],
19            body: JsonValue::Null,
20            commitment: Commitment::Finalized,
21        }
22    }
23
24    pub fn add_header(&mut self, key: &str, value: &str) -> &mut Self {
25        self.headers.push((key.to_owned(), value.to_owned()));
26
27        self
28    }
29
30    pub fn add_body(&mut self, body: JsonValue) -> &mut Self {
31        self.body = body;
32
33        self
34    }
35
36    pub fn add_commitment(&mut self, commitment: Commitment) -> &mut Self {
37        self.commitment = commitment;
38
39        self
40    }
41
42    pub fn common_methods(&mut self, body: JsonValue) -> &mut Self {
43        self.add_body(body);
44
45        self
46    }
47
48    pub fn prepare_transaction(&mut self, transaction: &Transaction) -> PoseidonResult<&mut Self> {
49        let commitment: &str = self.commitment.into();
50
51        let body = json::object! {
52            jsonrpc: "2.0",
53            id: 1u8,
54            method: "sendTransaction",
55            params: json::array![
56                transaction.to_base58()?,
57                json::object!{commitment: commitment }
58            ]
59        };
60
61        self.body = body;
62
63        Ok(self)
64    }
65
66    pub fn send(&self) -> smol::Task<PoseidonResult<minreq::Response>> {
67        let cluster_url = self.cluster.url();
68        let body = self.body.clone().to_string();
69        let headers = self.headers.clone();
70
71        smol::spawn(async move {
72            let mut request = minreq::post(cluster_url).with_body(body);
73
74            for header in headers {
75                request = request.with_header(&header.0, &header.1);
76            }
77
78            Ok(request.send()?)
79        })
80    }
81}
82
83#[derive(Debug, Clone)]
84pub enum TxSendOutcome {
85    Success(SendTxResponse),
86    Failure(RpcTxError),
87}
88
89impl TxSendOutcome {
90    pub fn parse_tx(response: minreq::Response) -> PoseidonResult<TxSendOutcome> {
91        let first_response = serde_json::from_str::<SendTxResponse>(&response.as_str()?);
92
93        match first_response {
94            Ok(value) => Ok(TxSendOutcome::Success(value)),
95            Err(first_error) => {
96                let err_response = serde_json::from_str::<RpcTxError>(&response.as_str()?);
97                match err_response {
98                    Ok(value) => Ok(TxSendOutcome::Failure(value)),
99                    Err(_) => Err(first_error.into()),
100                }
101            }
102        }
103    }
104}
105
106#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
107#[serde(rename = "camelCase")]
108pub struct RpcResponse<T> {
109    pub jsonrpc: String,
110    pub id: u8,
111    pub result: T,
112}
113
114#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
115#[serde(rename = "camelCase")]
116pub struct RpcResponseWithResult<T> {
117    pub jsonrpc: String,
118    pub id: u8,
119    pub result: RpcResult<T>,
120}
121
122#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
123#[serde(rename = "camelCase")]
124pub struct RpcResult<T> {
125    pub context: Context,
126    pub value: T,
127}
128
129#[derive(
130    Debug,
131    PartialEq,
132    Eq,
133    Ord,
134    PartialOrd,
135    Clone,
136    Deserialize,
137    Serialize,
138    BorshSerialize,
139    BorshDeserialize,
140)]
141#[serde(rename_all = "camelCase")]
142pub struct Context {
143    pub api_version: String,
144    pub slot: u64,
145}
146
147pub(crate) async fn request<T: serde::de::DeserializeOwned>(
148    body: json::JsonValue,
149) -> PoseidonResult<RpcResponse<T>> {
150    let mut rpc = RpcClient::new();
151    rpc.common_methods(body);
152    let response = rpc.send().await?;
153    let deser_response: RpcResponse<T> = serde_json::from_str(response.as_str()?)?;
154
155    Ok(deser_response)
156}
157
158pub(crate) async fn request_with_result<T: serde::de::DeserializeOwned>(
159    body: json::JsonValue,
160) -> PoseidonResult<RpcResponseWithResult<T>> {
161    let mut rpc = RpcClient::new();
162    rpc.common_methods(body);
163    let response = rpc.send().await?;
164    dbg!(&response.as_str()?);
165    let deser_response: RpcResponseWithResult<T> = serde_json::from_str(response.as_str()?)?;
166
167    Ok(deser_response)
168}