1use async_trait::async_trait;
2use serde::{de::DeserializeOwned, Deserialize, Serialize};
3use std::time::Duration;
4use std::time::SystemTime;
5use std::{fmt, sync::Arc};
6
7pub mod error;
8pub mod types;
9pub use error::Error;
10
11pub mod utils;
12pub use utils::now_millis;
13
14pub mod blocks;
15
16
17pub const ACCUMULATE_MAINNET_RPC_URL: &str = "https://mainnet.accumulatenetwork.io";
21pub const ACCUMULATE_TESTNET_KERMIT_RPC_URL: &str = "https://api-gateway.accumulate.defidevs.io";
22pub const ACCUMULATE_TESTNET_FOZZIE_RPC_URL: &str = "https://fozzie.accumulatenetwork.io";
23pub const ACCUMULATE_EDGE_RPC_URL: &str = "https://api.accumulate.chainfold.io";
24pub const ACCUMULATE_DEFAULT_RPC_URL: &str = "http://127.0.0.1:4467"; pub const ACCUMULATE_API_VER: u8 = 3; pub const DEFAULT_TIMEOUT: u64 = 120;
31
32pub const JSON_RPC: &str = "2.0";
34
35pub type Result<T = ()> = std::result::Result<T, Error>;
40
41#[derive(Clone, Debug)]
42pub struct Client {
43 base_url: String,
44 version: u8,
45 client: reqwest::Client,
46}
47
48impl Default for Client {
49 fn default() -> Self {
50 Self::new_with_base_url(ACCUMULATE_DEFAULT_RPC_URL.to_string())
51 }
52}
53
54impl Client {
55 pub fn new() -> Self {
56 let client = reqwest::Client::builder()
57 .timeout(Duration::from_secs(DEFAULT_TIMEOUT))
59 .build()
60 .unwrap();
61 Self {
62 base_url: ACCUMULATE_DEFAULT_RPC_URL.to_string(),
63 version: ACCUMULATE_API_VER,
64 client,
65 }
66 }
67
68 pub fn new_with_base_url(base_url: String) -> Self {
72 Self::new_with_timeout(base_url, ACCUMULATE_API_VER, DEFAULT_TIMEOUT)
73 }
74
75 pub fn new_with_timeout(base_url: String, version: u8, timeout: u64) -> Self {
79 let client = reqwest::Client::builder()
80 .timeout(Duration::from_secs(timeout))
82 .build()
83 .unwrap();
84 Self {
85 base_url,
86 version,
87 client,
88 }
89 }
90
91 pub fn new_with_version(base_url: String, version: u8) -> Self {
92 let client = reqwest::Client::builder()
93 .timeout(Duration::from_secs(DEFAULT_TIMEOUT))
95 .build()
96 .unwrap();
97 Self {
98 base_url,
99 version,
100 client,
101 }
102 }
103
104 async fn post<T: DeserializeOwned, D: Serialize>(&self, path: &str, data: D) -> Result<T> {
105 #[derive(Clone, Serialize, Deserialize, Debug)]
106 #[serde(untagged)]
107 pub(crate) enum Response<T> {
108 Data { result: T, id: String },
109 Error { id: String, error: Error },
110 }
111
112 #[derive(Clone, Serialize, Deserialize, Debug)]
113 pub(crate) struct Error {
114 message: String,
115 code: isize,
116 }
117
118 let request_url = format!("{}{}", self.base_url, path);
119 println!("1// request URL: {}", request_url);
120 let request = self.client.post(&request_url).json(&data);
121 let requestV = self.client.post(&request_url).json(&data);
122 println!("2//");
123
124 match requestV.build() {
125 Ok(req) => {
126 println!("{:?}", req);
128 }
134 Err(e) => {
135 eprintln!("Error building the request: {:?}", e);
137 }
138 }
139
140 let response = request.send().await?;
141 println!("3//");
142 let body = response.text().await?;
143 println!("body: {}", body);
144 let v: Response<T> = serde_json::from_str(&body)?;
145 match v {
146 Response::Data { result, .. } => Ok(result),
147 Response::Error { error, .. } => Err(error::Error::ApiError(error.message, error.code)),
148 }
149 }
150}
151
152#[derive(Clone, Deserialize, Debug, Serialize)]
153#[serde(tag = "method")]
154enum Method {
156 #[serde(rename(serialize = "metrics"))]
157 Metrics,
158 #[serde(rename(serialize = "consensus-status"))]
159 ConsensusStatus,
160 #[serde(rename(serialize = "find-service"))]
161 FindService,
162 #[serde(rename(serialize = "query"))]
163 MajorBlocksGet { params: QueryBlockMajorListParams },
164 #[serde(rename(serialize = "query"))]
165 MinorBlocksGet { params: QueryBlockMinorListParams },
166
167 #[serde(rename(serialize = "query"))]
168 MajorBlockGet { params: BlockParams },
169 #[serde(rename(serialize = "query"))]
170 MinorBlockGet { params: BlockParams },
171
172 #[serde(rename(serialize = "query"))]
173 Query { params: QueryParams },
174}
175
176#[derive(Clone, Deserialize, Debug, Serialize)]
177pub(crate) struct ApiCall {
178 jsonrpc: String,
179 id: String,
180 #[serde(flatten)]
181 method: Method,
182}
183
184impl ApiCall {
185 fn new(request: Method) -> ApiCall {
186 ApiCall {
187 jsonrpc: JSON_RPC.to_string(),
188 id: "0".to_string(), method: request,
190 }
191 }
192
193 pub(crate) fn query_major_blocks(url: String, start: u64, count: u64, expand: bool, from_end: bool) -> Self {
194 Self::new(Method::MajorBlocksGet {
195 params: QueryBlockMajorListParams {
196 scope: url,
197 query: {
198 BlockMajorListParams {
199 query_type: "block".to_string(),
200 major_range: BlockListRangeParams {
201 start,
202 count,
203 expand,
204 from_end,
205 },
206 }
207 },
208 },
209 })
210 }
211
212 pub(crate) fn query_major_block(url: String, height: u64) -> Self {
213 Self::new(Method::MajorBlockGet {
214 params: BlockParams { scope: url, height },
215 })
216 }
217
218 pub(crate) fn query_minor_blocks(url: String, start: u64, count: u64, expand: bool, from_end: bool) -> Self {
219 Self::new(Method::MinorBlocksGet {
220 params: QueryBlockMinorListParams {
221 scope: url,
222 query: {
223 BlockMinorListParams {
224 query_type: "block".to_string(),
225 major_range: BlockListRangeParams {
226 start,
227 count,
228 expand,
229 from_end,
230 },
231 }
232 },
233 },
234 })
235 }
236}
237
238#[derive(Clone, Deserialize, Debug, Serialize)]
239struct QueryParams {
240 scope: String,
241}
242
243#[derive(Clone, Deserialize, Debug, Serialize)]
244struct BlockParams {
245 scope: String,
246 height: u64,
247}
248
249#[derive(Clone, Deserialize, Debug, Serialize)]
250struct QueryBlockMajorListParams {
251 scope: String,
252 query: BlockMajorListParams,
253}
254
255#[derive(Clone, Deserialize, Debug, Serialize)]
256struct BlockMajorListParams {
257 #[serde(rename(serialize = "queryType"))]
258 query_type: String,
259 #[serde(rename(serialize = "majorRange"))]
260 major_range: BlockListRangeParams,
261}
262
263#[derive(Clone, Deserialize, Debug, Serialize)]
264struct BlockListRangeParams {
265 start: u64,
266 count: u64,
267 expand: bool,
268 #[serde(rename(serialize = "fromEnd"))]
269 from_end: bool,
270}
271
272#[derive(Clone, Deserialize, Debug, Serialize)]
273struct QueryBlockMinorListParams {
274 scope: String,
275 query: BlockMinorListParams,
276}
277
278#[derive(Clone, Deserialize, Debug, Serialize)]
279struct BlockMinorListParams {
280 #[serde(rename(serialize = "queryType"))]
281 query_type: String,
282 #[serde(rename(serialize = "minorRange"))]
283 major_range: BlockListRangeParams,
284}
285
286#[cfg(test)]
287mod test {
288 use super::*;
289 use tokio::test;
290
291 #[test]
292 async fn get_last_major_block() {
293 let client = Client::new_with_version(ACCUMULATE_MAINNET_RPC_URL.to_string(), 3);
294 let resp = blocks::get_range_major(&client, "acc://bvn-apollo.acme", 1, 100, true, true).await;
295
296 if let Err(err) = resp {
297 println!("{}", err);
299 panic!("{err}"); }
301
302 assert!(1 > 0);
303 }
304}