1use std::sync::Arc;
2
3use bindy::Result;
4use chia_protocol::{Bytes32, SpendBundle};
5use chia_sdk_coinset::{
6 AdditionsAndRemovalsResponse, BlockchainStateResponse, ChiaRpcClient,
7 GetBlockRecordByHeightResponse, GetBlockRecordResponse, GetBlockRecordsResponse,
8 GetBlockResponse, GetBlockSpendsResponse, GetBlocksResponse, GetCoinRecordResponse,
9 GetCoinRecordsResponse, GetMempoolItemResponse, GetMempoolItemsResponse,
10 GetNetworkInfoResponse, GetPuzzleAndSolutionResponse, PushTxResponse,
11};
12use serde::{Serialize, de::DeserializeOwned};
13
14#[cfg(any(feature = "napi", feature = "pyo3"))]
15use chia_protocol::Bytes;
16
17enum RpcClientImpl {
18 Coinset(chia_sdk_coinset::CoinsetClient),
19 #[cfg(any(feature = "napi", feature = "pyo3"))]
20 FullNode(chia_sdk_coinset::FullNodeClient),
21}
22
23impl ChiaRpcClient for RpcClientImpl {
24 type Error = reqwest::Error;
25
26 fn base_url(&self) -> &str {
27 match self {
28 RpcClientImpl::Coinset(client) => client.base_url(),
29 #[cfg(any(feature = "napi", feature = "pyo3"))]
30 RpcClientImpl::FullNode(client) => client.base_url(),
31 }
32 }
33
34 async fn make_post_request<R, B>(
35 &self,
36 endpoint: &str,
37 body: B,
38 ) -> std::result::Result<R, Self::Error>
39 where
40 B: Serialize + Send,
41 R: DeserializeOwned + Send,
42 {
43 match self {
44 RpcClientImpl::Coinset(client) => client.make_post_request(endpoint, body).await,
45 #[cfg(any(feature = "napi", feature = "pyo3"))]
46 RpcClientImpl::FullNode(client) => client.make_post_request(endpoint, body).await,
47 }
48 }
49}
50
51#[derive(Clone)]
52pub struct RpcClient(Arc<RpcClientImpl>);
53
54impl RpcClient {
55 pub fn new(base_url: String) -> Result<Self> {
56 Ok(Self(Arc::new(RpcClientImpl::Coinset(
57 chia_sdk_coinset::CoinsetClient::new(base_url),
58 ))))
59 }
60
61 pub fn testnet11() -> Result<Self> {
62 Ok(Self(Arc::new(RpcClientImpl::Coinset(
63 chia_sdk_coinset::CoinsetClient::testnet11(),
64 ))))
65 }
66
67 pub fn mainnet() -> Result<Self> {
68 Ok(Self(Arc::new(RpcClientImpl::Coinset(
69 chia_sdk_coinset::CoinsetClient::mainnet(),
70 ))))
71 }
72
73 #[cfg(any(feature = "napi", feature = "pyo3"))]
74 pub fn local(cert_bytes: Bytes, key_bytes: Bytes) -> Result<Self> {
75 Ok(Self(Arc::new(RpcClientImpl::FullNode(
76 chia_sdk_coinset::FullNodeClient::new(&cert_bytes, &key_bytes)?,
77 ))))
78 }
79
80 #[cfg(any(feature = "napi", feature = "pyo3"))]
81 pub fn local_with_url(base_url: String, cert_bytes: Bytes, key_bytes: Bytes) -> Result<Self> {
82 Ok(Self(Arc::new(RpcClientImpl::FullNode(
83 chia_sdk_coinset::FullNodeClient::with_base_url(base_url, &cert_bytes, &key_bytes)?,
84 ))))
85 }
86
87 pub async fn get_blockchain_state(&self) -> Result<BlockchainStateResponse> {
88 Ok(self.0.get_blockchain_state().await?)
89 }
90
91 pub async fn get_additions_and_removals(
92 &self,
93 header_hash: Bytes32,
94 ) -> Result<AdditionsAndRemovalsResponse> {
95 Ok(self.0.get_additions_and_removals(header_hash).await?)
96 }
97
98 pub async fn get_block(&self, header_hash: Bytes32) -> Result<GetBlockResponse> {
99 Ok(self.0.get_block(header_hash).await?)
100 }
101
102 pub async fn get_block_record(&self, header_hash: Bytes32) -> Result<GetBlockRecordResponse> {
103 Ok(self.0.get_block_record(header_hash).await?)
104 }
105
106 pub async fn get_block_record_by_height(
107 &self,
108 height: u32,
109 ) -> Result<GetBlockRecordByHeightResponse> {
110 Ok(self.0.get_block_record_by_height(height).await?)
111 }
112
113 pub async fn get_block_records(&self, start: u32, end: u32) -> Result<GetBlockRecordsResponse> {
114 Ok(self.0.get_block_records(start, end).await?)
115 }
116
117 pub async fn get_blocks(
118 &self,
119 start: u32,
120 end: u32,
121 exclude_header_hash: bool,
122 exclude_reorged: bool,
123 ) -> Result<GetBlocksResponse> {
124 Ok(self
125 .0
126 .get_blocks(start, end, exclude_header_hash, exclude_reorged)
127 .await?)
128 }
129
130 pub async fn get_block_spends(&self, header_hash: Bytes32) -> Result<GetBlockSpendsResponse> {
131 Ok(self.0.get_block_spends(header_hash).await?)
132 }
133
134 pub async fn get_coin_record_by_name(&self, name: Bytes32) -> Result<GetCoinRecordResponse> {
135 Ok(self.0.get_coin_record_by_name(name).await?)
136 }
137
138 pub async fn get_coin_records_by_hint(
139 &self,
140 hint: Bytes32,
141 start_height: Option<u32>,
142 end_height: Option<u32>,
143 include_spent_coins: Option<bool>,
144 cursor: Option<String>,
145 ) -> Result<GetCoinRecordsResponse> {
146 Ok(self
147 .0
148 .get_coin_records_by_hint(hint, start_height, end_height, include_spent_coins, cursor)
149 .await?)
150 }
151
152 pub async fn get_coin_records_by_hints(
153 &self,
154 hints: Vec<Bytes32>,
155 start_height: Option<u32>,
156 end_height: Option<u32>,
157 include_spent_coins: Option<bool>,
158 cursor: Option<String>,
159 ) -> Result<GetCoinRecordsResponse> {
160 Ok(self
161 .0
162 .get_coin_records_by_hints(hints, start_height, end_height, include_spent_coins, cursor)
163 .await?)
164 }
165
166 pub async fn get_coin_records_by_names(
167 &self,
168 names: Vec<Bytes32>,
169 start_height: Option<u32>,
170 end_height: Option<u32>,
171 include_spent_coins: Option<bool>,
172 cursor: Option<String>,
173 ) -> Result<GetCoinRecordsResponse> {
174 Ok(self
175 .0
176 .get_coin_records_by_names(names, start_height, end_height, include_spent_coins, cursor)
177 .await?)
178 }
179
180 pub async fn get_coin_records_by_parent_ids(
181 &self,
182 parent_ids: Vec<Bytes32>,
183 start_height: Option<u32>,
184 end_height: Option<u32>,
185 include_spent_coins: Option<bool>,
186 cursor: Option<String>,
187 ) -> Result<GetCoinRecordsResponse> {
188 Ok(self
189 .0
190 .get_coin_records_by_parent_ids(
191 parent_ids,
192 start_height,
193 end_height,
194 include_spent_coins,
195 cursor,
196 )
197 .await?)
198 }
199
200 pub async fn get_coin_records_by_puzzle_hash(
201 &self,
202 puzzle_hash: Bytes32,
203 start_height: Option<u32>,
204 end_height: Option<u32>,
205 include_spent_coins: Option<bool>,
206 cursor: Option<String>,
207 ) -> Result<GetCoinRecordsResponse> {
208 Ok(self
209 .0
210 .get_coin_records_by_puzzle_hash(
211 puzzle_hash,
212 start_height,
213 end_height,
214 include_spent_coins,
215 cursor,
216 )
217 .await?)
218 }
219
220 pub async fn get_coin_records_by_puzzle_hashes(
221 &self,
222 puzzle_hashes: Vec<Bytes32>,
223 start_height: Option<u32>,
224 end_height: Option<u32>,
225 include_spent_coins: Option<bool>,
226 cursor: Option<String>,
227 ) -> Result<GetCoinRecordsResponse> {
228 Ok(self
229 .0
230 .get_coin_records_by_puzzle_hashes(
231 puzzle_hashes,
232 start_height,
233 end_height,
234 include_spent_coins,
235 cursor,
236 )
237 .await?)
238 }
239
240 pub async fn get_puzzle_and_solution(
241 &self,
242 coin_id: Bytes32,
243 height: Option<u32>,
244 ) -> Result<GetPuzzleAndSolutionResponse> {
245 Ok(self.0.get_puzzle_and_solution(coin_id, height).await?)
246 }
247
248 pub async fn push_tx(&self, spend_bundle: SpendBundle) -> Result<PushTxResponse> {
249 Ok(self.0.push_tx(spend_bundle).await?)
250 }
251
252 pub async fn get_network_info(&self) -> Result<GetNetworkInfoResponse> {
253 Ok(self.0.get_network_info().await?)
254 }
255
256 pub async fn get_mempool_item_by_tx_id(
257 &self,
258 tx_id: Bytes32,
259 ) -> Result<GetMempoolItemResponse> {
260 Ok(self.0.get_mempool_item_by_tx_id(tx_id).await?)
261 }
262
263 pub async fn get_mempool_items_by_coin_name(
264 &self,
265 coin_name: Bytes32,
266 ) -> Result<GetMempoolItemsResponse> {
267 Ok(self.0.get_mempool_items_by_coin_name(coin_name).await?)
268 }
269}