avalanche_types/jsonrpc/
platformvm.rs

1//! JSON-RPC request and response types for the Platform VM API.
2use std::{
3    collections::HashMap,
4    io::{self, Error, ErrorKind},
5};
6
7use crate::{
8    codec::serde::hex_0x_utxo::Hex0xUtxo,
9    ids::{self, node},
10    jsonrpc,
11    key::bls,
12    platformvm, txs,
13};
14use serde::{Deserialize, Serialize};
15use serde_with::{serde_as, DisplayFromStr};
16
17/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformissuetx>
18#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
19pub struct IssueTxRequest {
20    pub jsonrpc: String,
21    pub id: u32,
22
23    pub method: String,
24
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub params: Option<IssueTxParams>,
27}
28
29impl Default for IssueTxRequest {
30    fn default() -> Self {
31        Self {
32            jsonrpc: String::from(super::DEFAULT_VERSION),
33            id: super::DEFAULT_ID,
34            method: String::new(),
35            params: None,
36        }
37    }
38}
39
40impl IssueTxRequest {
41    pub fn encode_json(&self) -> io::Result<String> {
42        serde_json::to_string(&self)
43            .map_err(|e| Error::new(ErrorKind::Other, format!("failed to serialize JSON {}", e)))
44    }
45}
46
47#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
48#[serde(rename_all = "camelCase")]
49pub struct IssueTxParams {
50    pub tx: String,
51    pub encoding: String,
52}
53
54#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
55pub struct IssueTxResponse {
56    pub jsonrpc: String,
57    pub id: u32,
58
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub result: Option<IssueTxResult>,
61
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub error: Option<super::ResponseError>,
64}
65
66impl Default for IssueTxResponse {
67    fn default() -> Self {
68        Self {
69            jsonrpc: "2.0".to_string(),
70            id: 1,
71            result: None,
72            error: None,
73        }
74    }
75}
76
77/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformissuetx>
78#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
79pub struct IssueTxResult {
80    #[serde(rename = "txID")]
81    pub tx_id: ids::Id,
82}
83
84impl Default for IssueTxResult {
85    fn default() -> Self {
86        Self {
87            tx_id: ids::Id::empty(),
88        }
89    }
90}
91
92/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_issue_tx --exact --show-output
93#[test]
94fn test_issue_tx() {
95    use std::str::FromStr;
96
97    let resp: IssueTxResponse = serde_json::from_str(
98        "
99    
100    {
101        \"jsonrpc\": \"2.0\",
102        \"result\": {
103            \"txID\": \"G3BuH6ytQ2averrLxJJugjWZHTRubzCrUZEXoheG5JMqL5ccY\"
104        },
105        \"id\": 1
106    }
107    
108    ",
109    )
110    .unwrap();
111
112    let expected = IssueTxResponse {
113        jsonrpc: "2.0".to_string(),
114        id: 1,
115        result: Some(IssueTxResult {
116            tx_id: ids::Id::from_str("G3BuH6ytQ2averrLxJJugjWZHTRubzCrUZEXoheG5JMqL5ccY").unwrap(),
117        }),
118        error: None,
119    };
120    assert_eq!(resp, expected);
121}
122
123/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain/#platformgettx>
124#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
125pub struct GetTxResponse {
126    pub jsonrpc: String,
127    pub id: u32,
128
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub result: Option<GetTxResult>,
131
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub error: Option<jsonrpc::ResponseError>,
134}
135
136impl Default for GetTxResponse {
137    fn default() -> Self {
138        Self {
139            jsonrpc: "2.0".to_string(),
140            id: 1,
141            result: None,
142            error: None,
143        }
144    }
145}
146
147/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain/#platformgettx>
148#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
149pub struct GetTxResult {
150    pub tx: platformvm::txs::Tx,
151    pub encoding: String,
152}
153
154/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_tx --exact --show-output
155#[test]
156fn test_get_tx() {
157    let parsed_resp: GetTxResponse = serde_json::from_str(
158        "
159
160{
161    \"jsonrpc\": \"2.0\",
162    \"result\": {
163        \"tx\": {
164            \"unsignedTx\": {
165                \"networkID\": 1000000,
166                \"blockchainID\": \"11111111111111111111111111111111LpoYY\",
167                \"outputs\": [
168                    {
169                        \"assetID\": \"u8aaQ7MxyW32iHuP2xMXgYPrWYAsSbh8RJV9C6p1UeuGvqR3\",
170                        \"fxID\": \"spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ\",
171                        \"output\": {
172                            \"addresses\": [
173                                \"P-custom12szthht8tnl455u4mz3ns3nvvkel8ezvw2n8cx\"
174                            ],
175                            \"amount\": 245952587549460688,
176                            \"locktime\": 0,
177                            \"threshold\": 1
178                        }
179                    }
180                ],
181                \"inputs\": [
182                    {
183                        \"txID\": \"nN5QsURgEpM8D3e9q8FonS4EE13mnaBDtnQmgSwwUfBZ6FSW1\",
184                        \"outputIndex\": 0,
185                        \"assetID\": \"u8aaQ7MxyW32iHuP2xMXgYPrWYAsSbh8RJV9C6p1UeuGvqR3\",
186                        \"fxID\": \"spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ\",
187                        \"input\": {
188                            \"amount\": 245952587649460688,
189                            \"signatureIndices\": [
190                                0
191                            ]
192                        }
193                    }
194                ],
195                \"memo\": \"0x\",
196                \"owner\": {
197                    \"addresses\": [
198                        \"P-custom12szthht8tnl455u4mz3ns3nvvkel8ezvw2n8cx\"
199                    ],
200                    \"locktime\": 0,
201                    \"threshold\": 1
202                }
203            },
204            \"credentials\": [
205                {
206                    \"signatures\": [
207                        \"0xcb356822dc8990672b5777ec50b57da91baf572240e7d4e9e38f26ec9dbdfd8e376fdc5f30769b842668cd8d81bd71db926dfbe326585137d363566ee500369f01\"
208                    ]
209                }
210            ]
211        },
212        \"encoding\": \"json\"
213    },
214    \"id\": 1
215}
216
217",
218    )
219    .unwrap();
220
221    assert_eq!(parsed_resp.jsonrpc, "2.0");
222    assert_eq!(parsed_resp.result.clone().unwrap().encoding, "json");
223}
224
225/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain/#platformgettxstatus>
226#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
227pub struct GetTxStatusResponse {
228    pub jsonrpc: String,
229    pub id: u32,
230
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub result: Option<GetTxStatusResult>,
233
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub error: Option<jsonrpc::ResponseError>,
236}
237
238impl Default for GetTxStatusResponse {
239    fn default() -> Self {
240        Self {
241            jsonrpc: "2.0".to_string(),
242            id: 1,
243            result: None,
244            error: None,
245        }
246    }
247}
248
249/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain/#platformgettxstatus>
250#[serde_as]
251#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
252pub struct GetTxStatusResult {
253    #[serde_as(as = "DisplayFromStr")]
254    pub status: platformvm::txs::status::Status,
255}
256
257impl Default for GetTxStatusResult {
258    fn default() -> Self {
259        Self {
260            status: platformvm::txs::status::Status::Unknown(String::new()),
261        }
262    }
263}
264
265/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_tx_status --exact --show-output
266#[test]
267fn test_get_tx_status() {
268    let resp: GetTxStatusResponse = serde_json::from_str(
269        "
270
271{
272    \"jsonrpc\": \"2.0\",
273    \"result\": {
274        \"status\": \"Committed\"
275    },
276    \"id\": 1
277}
278
279",
280    )
281    .unwrap();
282
283    let expected = GetTxStatusResponse {
284        jsonrpc: "2.0".to_string(),
285        id: 1,
286        result: Some(GetTxStatusResult {
287            status: platformvm::txs::status::Status::Committed,
288        }),
289        error: None,
290    };
291    assert_eq!(resp, expected);
292}
293
294/// ref. <https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetheight>
295#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
296pub struct GetHeightResponse {
297    pub jsonrpc: String,
298    pub id: u32,
299
300    #[serde(skip_serializing_if = "Option::is_none")]
301    pub result: Option<GetHeightResult>,
302
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub error: Option<jsonrpc::ResponseError>,
305}
306
307/// ref. <https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetheight>
308#[serde_as]
309#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
310pub struct GetHeightResult {
311    #[serde_as(as = "DisplayFromStr")]
312    pub height: u64,
313}
314
315/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_height --exact --show-output
316#[test]
317fn test_get_height() {
318    // ref. https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetheight
319    let resp: GetHeightResponse = serde_json::from_str(
320        "
321
322{
323    \"jsonrpc\": \"2.0\",
324    \"result\": {
325        \"height\": \"0\"
326    },
327    \"id\": 1
328}
329
330",
331    )
332    .unwrap();
333
334    let expected = GetHeightResponse {
335        jsonrpc: "2.0".to_string(),
336        id: 1,
337        result: Some(GetHeightResult { height: 0 }),
338        error: None,
339    };
340    assert_eq!(resp, expected);
341}
342
343/// ref. <https://docs.avax.network/build/avalanchego-apis/issuing-api-calls>
344#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
345pub struct GetUtxosRequest {
346    pub jsonrpc: String,
347    pub id: u32,
348
349    pub method: String,
350
351    #[serde(skip_serializing_if = "Option::is_none")]
352    pub params: Option<GetUtxosParams>,
353}
354
355impl Default for GetUtxosRequest {
356    fn default() -> Self {
357        Self {
358            jsonrpc: String::from(super::DEFAULT_VERSION),
359            id: super::DEFAULT_ID,
360            method: String::new(),
361            params: None,
362        }
363    }
364}
365
366impl GetUtxosRequest {
367    pub fn encode_json(&self) -> io::Result<String> {
368        serde_json::to_string(&self)
369            .map_err(|e| Error::new(ErrorKind::Other, format!("failed to serialize JSON {}", e)))
370    }
371}
372
373/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetutxos>
374#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
375#[serde(rename_all = "camelCase")]
376pub struct GetUtxosParams {
377    pub addresses: Vec<String>,
378    pub limit: u32,
379    pub encoding: String,
380}
381
382/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetutxos>
383#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
384pub struct GetUtxosResponse {
385    pub jsonrpc: String,
386    pub id: u32,
387
388    #[serde(skip_serializing_if = "Option::is_none")]
389    pub result: Option<GetUtxosResult>,
390
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub error: Option<super::ResponseError>,
393}
394
395/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetutxos>
396#[serde_as]
397#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
398#[serde(rename_all = "camelCase")]
399#[derive(Default)]
400pub struct GetUtxosResult {
401    #[serde_as(as = "DisplayFromStr")]
402    pub num_fetched: u32,
403
404    #[serde_as(as = "Option<Vec<Hex0xUtxo>>")]
405    #[serde(skip_serializing_if = "Option::is_none")]
406    pub utxos: Option<Vec<txs::utxo::Utxo>>,
407
408    #[serde(skip_serializing_if = "Option::is_none")]
409    pub end_index: Option<super::EndIndex>,
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub encoding: Option<String>,
412}
413
414/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_utxos_empty --exact --show-output
415#[test]
416fn test_get_utxos_empty() {
417    // ref. https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetutxos
418    let resp: GetUtxosResponse = serde_json::from_str(
419        "
420
421{
422    \"jsonrpc\": \"2.0\",
423    \"result\": {
424        \"numFetched\": \"0\",
425        \"utxos\": [],
426        \"endIndex\": {
427            \"address\": \"P-custom152qlr6zunz7nw2kc4lfej3cn3wk46u3002k4w5\",
428            \"utxo\": \"11111111111111111111111111111111LpoYY\"
429        },
430        \"encoding\":\"hex\"
431    },
432    \"id\": 1
433}
434
435",
436    )
437    .unwrap();
438
439    let expected = GetUtxosResponse {
440        jsonrpc: "2.0".to_string(),
441        id: 1,
442        result: Some(GetUtxosResult {
443            num_fetched: 0,
444            utxos: Some(Vec::new()),
445            end_index: Some(super::EndIndex {
446                address: String::from("P-custom152qlr6zunz7nw2kc4lfej3cn3wk46u3002k4w5"),
447                utxo: String::from("11111111111111111111111111111111LpoYY"),
448            }),
449            encoding: Some(String::from("hex")),
450        }),
451        error: None,
452    };
453    assert_eq!(resp, expected);
454}
455
456/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_utxos_non_empty --exact --show-output
457#[test]
458fn test_get_utxos_non_empty() {
459    // ref. https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetbalance
460    let resp: GetUtxosResponse = serde_json::from_str(
461        "
462
463{
464    \"jsonrpc\": \"2.0\",
465    \"result\": {
466        \"numFetched\": \"1\",
467        \"utxos\": [
468            \"0x000000000000000000000000000000000000000000000000000000000000000000000000000088eec2e099c6a528e689618e8721e04ae85ea574c7a15a7968644d14d54780140000000702c68af0bb1400000000000000000000000000010000000165844a05405f3662c1928142c6c2a783ef871de939b564db\"
469        ],
470        \"endIndex\": {
471            \"address\": \"X-avax1x459sj0ssujguq723cljfty4jlae28evjzt7xz\",
472            \"utxo\": \"LUC1cmcxnfNR9LdkACS2ccGKLEK7SYqB4gLLTycQfg1koyfSq\"
473        },
474        \"encoding\": \"hex\"
475    },
476    \"id\": 1
477}
478
479",
480    )
481    .unwrap();
482
483    let raw_utxo =  String::from("0x000000000000000000000000000000000000000000000000000000000000000000000000000088eec2e099c6a528e689618e8721e04ae85ea574c7a15a7968644d14d54780140000000702c68af0bb1400000000000000000000000000010000000165844a05405f3662c1928142c6c2a783ef871de939b564db");
484    let utxo = txs::utxo::Utxo::from_hex(&raw_utxo).unwrap();
485
486    let expected = GetUtxosResponse {
487        jsonrpc: "2.0".to_string(),
488        id: 1,
489        result: Some(GetUtxosResult {
490            num_fetched: 1,
491            utxos: Some(vec![utxo]),
492            end_index: Some(super::EndIndex {
493                address: String::from("X-avax1x459sj0ssujguq723cljfty4jlae28evjzt7xz"),
494                utxo: String::from("LUC1cmcxnfNR9LdkACS2ccGKLEK7SYqB4gLLTycQfg1koyfSq"),
495            }),
496            encoding: Some(String::from("hex")),
497        }),
498        error: None,
499    };
500    assert_eq!(resp, expected);
501}
502
503/// ref. <https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetbalance>
504#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
505pub struct GetBalanceResponse {
506    pub jsonrpc: String,
507    pub id: u32,
508
509    #[serde(skip_serializing_if = "Option::is_none")]
510    pub result: Option<GetBalanceResult>,
511
512    #[serde(skip_serializing_if = "Option::is_none")]
513    pub error: Option<jsonrpc::ResponseError>,
514}
515
516/// ref. <https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetbalance>
517#[serde_as]
518#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
519pub struct GetBalanceResult {
520    #[serde_as(as = "DisplayFromStr")]
521    pub balance: u64,
522    #[serde_as(as = "DisplayFromStr")]
523    pub unlocked: u64,
524
525    #[serde(rename = "lockedStakeable", skip_serializing_if = "Option::is_none")]
526    #[serde_as(as = "Option<DisplayFromStr>")]
527    pub locked_stakeable: Option<u64>,
528
529    #[serde(skip_serializing_if = "Option::is_none")]
530    #[serde_as(as = "Option<HashMap<_, DisplayFromStr>>")]
531    pub balances: Option<HashMap<String, u64>>,
532    #[serde(skip_serializing_if = "Option::is_none")]
533    #[serde_as(as = "Option<HashMap<_, DisplayFromStr>>")]
534    pub unlockeds: Option<HashMap<String, u64>>,
535
536    #[serde(rename = "lockedNotStakeable", skip_serializing_if = "Option::is_none")]
537    #[serde_as(as = "Option<DisplayFromStr>")]
538    pub locked_not_stakeable: Option<u64>,
539
540    #[serde(rename = "utxoIDs", skip_serializing_if = "Option::is_none")]
541    pub utxo_ids: Option<Vec<txs::utxo::Id>>,
542}
543
544/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_balance --exact --show-output
545#[test]
546fn test_get_balance() {
547    use crate::ids;
548    use std::str::FromStr;
549
550    // ref. https://docs.avax.network/build/avalanchego-apis/p-chain/#platformgetbalance
551    let resp: GetBalanceResponse = serde_json::from_str(
552        "
553
554{
555    \"jsonrpc\": \"2.0\",
556    \"result\": {
557        \"balance\": \"20000000000000000\",
558        \"unlocked\": \"10000000000000000\",
559        \"lockedStakeable\": \"10000000000000000\",
560        \"lockedNotStakeable\": \"0\",
561        \"balances\": {
562            \"2ZKbwERx36B5WrYesQGAeTV4NTo4dx6j8svkjwAEix89ZPencR\": \"147573952589676412\"
563        },
564        \"unlockeds\": {
565            \"2ZKbwERx36B5WrYesQGAeTV4NTo4dx6j8svkjwAEix89ZPencR\": \"147573952589676412\"
566        },
567        \"utxoIDs\": [
568            {
569                \"txID\": \"11111111111111111111111111111111LpoYY\",
570                \"outputIndex\": 1
571            },
572            {
573                \"txID\": \"11111111111111111111111111111111LpoYY\",
574                \"outputIndex\": 0
575            }
576        ]
577    },
578    \"id\": 1
579}
580
581",
582    )
583    .unwrap();
584
585    let mut h = HashMap::new();
586    h.insert(
587        "2ZKbwERx36B5WrYesQGAeTV4NTo4dx6j8svkjwAEix89ZPencR".to_string(),
588        147573952589676412_u64,
589    );
590
591    let expected = GetBalanceResponse {
592        jsonrpc: "2.0".to_string(),
593        id: 1,
594        result: Some(GetBalanceResult {
595            balance: 20000000000000000,
596            unlocked: 10000000000000000,
597
598            locked_stakeable: Some(10000000000000000),
599            locked_not_stakeable: Some(0),
600
601            balances: Some(h.clone()),
602            unlockeds: Some(h.clone()),
603
604            utxo_ids: Some(vec![
605                txs::utxo::Id {
606                    tx_id: ids::Id::from_str("11111111111111111111111111111111LpoYY").unwrap(),
607                    output_index: 1,
608                    ..txs::utxo::Id::default()
609                },
610                txs::utxo::Id {
611                    tx_id: ids::Id::from_str("11111111111111111111111111111111LpoYY").unwrap(),
612                    output_index: 0,
613                    ..txs::utxo::Id::default()
614                },
615            ]),
616        }),
617        error: None,
618    };
619    assert_eq!(resp, expected);
620}
621
622/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetcurrentvalidators>
623#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
624pub struct GetCurrentValidatorsResponse {
625    pub jsonrpc: String,
626    pub id: u32,
627
628    #[serde(skip_serializing_if = "Option::is_none")]
629    pub result: Option<GetCurrentValidatorsResult>,
630
631    #[serde(skip_serializing_if = "Option::is_none")]
632    pub error: Option<jsonrpc::ResponseError>,
633}
634
635impl Default for GetCurrentValidatorsResponse {
636    fn default() -> Self {
637        Self {
638            jsonrpc: "2.0".to_string(),
639            id: 1,
640            result: Some(GetCurrentValidatorsResult::default()),
641            error: None,
642        }
643    }
644}
645
646/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetcurrentvalidators>
647/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientPermissionlessValidator>
648#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
649pub struct GetCurrentValidatorsResult {
650    #[serde(skip_serializing_if = "Option::is_none")]
651    pub validators: Option<Vec<ApiPrimaryValidator>>,
652}
653
654/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetpendingvalidators>
655#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
656pub struct GetPendingValidatorsResponse {
657    pub jsonrpc: String,
658    pub id: u32,
659
660    #[serde(skip_serializing_if = "Option::is_none")]
661    pub result: Option<GetPendingValidatorsResult>,
662
663    #[serde(skip_serializing_if = "Option::is_none")]
664    pub error: Option<jsonrpc::ResponseError>,
665}
666
667impl Default for GetPendingValidatorsResponse {
668    fn default() -> Self {
669        Self {
670            jsonrpc: "2.0".to_string(),
671            id: 1,
672            result: Some(GetPendingValidatorsResult::default()),
673            error: None,
674        }
675    }
676}
677
678/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetpendingvalidators>
679/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientPermissionlessValidator>
680/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientStaker>
681#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
682pub struct GetPendingValidatorsResult {
683    pub validators: Vec<ApiPrimaryValidator>,
684    pub delegators: Vec<ApiPrimaryDelegator>,
685}
686
687/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetcurrentvalidators>
688/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientPermissionlessValidator>
689/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientStaker>
690#[serde_as]
691#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
692#[serde(rename_all = "camelCase")]
693pub struct ApiPrimaryValidator {
694    #[serde(rename = "txID")]
695    pub tx_id: ids::Id,
696
697    #[serde_as(as = "DisplayFromStr")]
698    pub start_time: u64,
699    #[serde_as(as = "DisplayFromStr")]
700    pub end_time: u64,
701
702    /// None for elastic Subnet validator
703    #[serde_as(as = "Option<DisplayFromStr>")]
704    #[serde(skip_serializing_if = "Option::is_none")]
705    pub weight: Option<u64>,
706
707    /// None for permissioned Subnet validator
708    #[serde_as(as = "Option<DisplayFromStr>")]
709    #[serde(skip_serializing_if = "Option::is_none")]
710    pub stake_amount: Option<u64>,
711
712    #[serde(rename = "nodeID")]
713    pub node_id: node::Id,
714
715    /// None for permissioned Subnet validator
716    #[serde(skip_serializing_if = "Option::is_none")]
717    pub validation_reward_owner: Option<ApiOwner>,
718    /// None for permissioned Subnet validator
719    #[serde(skip_serializing_if = "Option::is_none")]
720    pub delegation_reward_owner: Option<ApiOwner>,
721
722    /// None for permissioned Subnet validator
723    #[serde_as(as = "Option<DisplayFromStr>")]
724    pub potential_reward: Option<u64>,
725    /// None for permissioned Subnet validator
726    #[serde_as(as = "Option<DisplayFromStr>")]
727    #[serde(skip_serializing_if = "Option::is_none")]
728    pub delegation_fee: Option<f32>,
729
730    /// None for permissioned Subnet validator
731    #[serde_as(as = "Option<DisplayFromStr>")]
732    #[serde(skip_serializing_if = "Option::is_none")]
733    pub uptime: Option<f32>,
734    pub connected: bool,
735
736    /// None for permissioned Subnet validator
737    #[serde(skip_serializing_if = "Option::is_none")]
738    pub signer: Option<bls::ProofOfPossession>,
739
740    /// None for permissioned Subnet validator
741    #[serde_as(as = "Option<DisplayFromStr>")]
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub delegator_count: Option<u64>,
744    /// None for permissioned Subnet validator
745    #[serde_as(as = "Option<DisplayFromStr>")]
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub delegator_weight: Option<u64>,
748
749    /// None for permissioned Subnet validator
750    #[serde(skip_serializing_if = "Option::is_none")]
751    pub delegators: Option<Vec<ApiPrimaryDelegator>>,
752}
753
754impl Default for ApiPrimaryValidator {
755    fn default() -> Self {
756        Self {
757            tx_id: ids::Id::empty(),
758            start_time: 0,
759            end_time: 0,
760            weight: None,
761            stake_amount: None,
762            node_id: node::Id::empty(),
763            validation_reward_owner: None,
764            delegation_reward_owner: None,
765            potential_reward: None,
766            delegation_fee: None,
767            uptime: None,
768            connected: false,
769            signer: None,
770            delegator_count: None,
771            delegator_weight: None,
772            delegators: None,
773        }
774    }
775}
776
777/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#APIOwner>
778#[serde_as]
779#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
780pub struct ApiOwner {
781    #[serde_as(as = "DisplayFromStr")]
782    pub locktime: u64,
783    #[serde_as(as = "DisplayFromStr")]
784    pub threshold: u32,
785    pub addresses: Vec<String>,
786}
787
788/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetcurrentvalidators>
789/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientPermissionlessValidator>
790/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#ClientStaker>
791#[serde_as]
792#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
793#[serde(rename_all = "camelCase")]
794pub struct ApiPrimaryDelegator {
795    #[serde(rename = "txID")]
796    pub tx_id: ids::Id,
797
798    #[serde_as(as = "DisplayFromStr")]
799    pub start_time: u64,
800    #[serde_as(as = "DisplayFromStr")]
801    pub end_time: u64,
802
803    #[serde_as(as = "DisplayFromStr")]
804    pub stake_amount: u64,
805
806    #[serde(rename = "nodeID")]
807    pub node_id: node::Id,
808
809    #[serde(skip_serializing_if = "Option::is_none")]
810    pub reward_owner: Option<ApiOwner>,
811
812    #[serde_as(as = "Option<DisplayFromStr>")]
813    #[serde(rename = "potentialReward", skip_serializing_if = "Option::is_none")]
814    pub potential_reward: Option<u64>,
815}
816
817impl Default for ApiPrimaryDelegator {
818    fn default() -> Self {
819        Self {
820            tx_id: ids::Id::empty(),
821            start_time: 0,
822            end_time: 0,
823            stake_amount: 0,
824            node_id: node::Id::empty(),
825            reward_owner: None,
826            potential_reward: None,
827        }
828    }
829}
830
831/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_current_validators --exact --show-output
832#[test]
833fn test_get_current_validators() {
834    use std::str::FromStr;
835
836    // ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetcurrentvalidators>
837    let resp: GetCurrentValidatorsResponse = serde_json::from_str(
838        "
839{
840    \"jsonrpc\": \"2.0\",
841    \"result\": {
842        \"validators\": [
843            {
844                \"txID\": \"KPkPo9EerKZhSwrA8NfLTVWsgr16Ntu8Ei4ci7GF7t75szrcQ\",
845                \"startTime\": \"1648312635\",
846                \"endTime\": \"1679843235\",
847                \"stakeAmount\": \"100000000000000000\",
848                \"nodeID\": \"NodeID-5wVq6KkSK3p4wQFmiVHCDq2zdg8unchaE\",
849                \"validationRewardOwner\": {
850                    \"locktime\": \"0\",
851                    \"threshold\": \"1\",
852                    \"addresses\": [
853                        \"P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs\"
854                    ]
855                },
856                \"delegationRewardOwner\": {
857                    \"locktime\": \"0\",
858                    \"threshold\": \"1\",
859                    \"addresses\": [
860                        \"P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs\"
861                    ]
862                },
863                \"potentialReward\": \"79984390135364555\",
864                \"delegationFee\": \"6.2500\",
865                \"uptime\": \"100.0000\",
866                \"connected\": true,
867                \"delegatorCount\": \"0\",
868                \"delegatorWeight\": \"0\",
869                \"delegators\": null
870            },
871            {
872                \"txID\": \"EjKZm5eEajWu151cfPms7PvMjyaQk36qTSz1MfnZRU5x5bNxz\",
873                \"startTime\": \"1648312635\",
874                \"endTime\": \"1679848635\",
875                \"stakeAmount\": \"100000000000000000\",
876                \"nodeID\": \"NodeID-JLR7d6z9cwCbkoPcPsnjkm6gq4xz7c4oT\",
877                \"validationRewardOwner\": {
878                    \"locktime\": \"0\",
879                    \"threshold\": \"1\",
880                    \"addresses\": [
881                        \"P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs\"
882                    ]
883                },
884                \"delegationRewardOwner\": {
885                    \"locktime\": \"0\",
886                    \"threshold\": \"1\",
887                    \"addresses\": [
888                        \"P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs\"
889                    ]
890                },
891                \"potentialReward\": \"77148186230865960\",
892                \"delegationFee\": \"6.2500\",
893                \"uptime\": \"100.0000\",
894                \"connected\": true,
895                \"delegatorCount\": \"0\",
896                \"delegatorWeight\": \"0\",
897                \"delegators\": null
898            }
899        ]
900    },
901    \"id\": 1
902}
903
904",
905    )
906    .unwrap();
907
908    let expected = GetCurrentValidatorsResponse {
909        jsonrpc: "2.0".to_string(),
910        id: 1,
911        result: Some(GetCurrentValidatorsResult {
912            validators: Some(<Vec<ApiPrimaryValidator>>::from([
913                ApiPrimaryValidator {
914                    tx_id: ids::Id::from_str("KPkPo9EerKZhSwrA8NfLTVWsgr16Ntu8Ei4ci7GF7t75szrcQ")
915                        .unwrap(),
916                    start_time: 1648312635,
917                    end_time: 1679843235,
918                    stake_amount: Some(100000000000000000),
919                    node_id: node::Id::from_str("NodeID-5wVq6KkSK3p4wQFmiVHCDq2zdg8unchaE")
920                        .unwrap(),
921                    validation_reward_owner: Some(ApiOwner {
922                        locktime: 0,
923                        threshold: 1,
924                        addresses: vec![
925                            "P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs".to_string()
926                        ],
927                    }),
928                    delegation_reward_owner: Some(ApiOwner {
929                        locktime: 0,
930                        threshold: 1,
931                        addresses: vec![
932                            "P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs".to_string()
933                        ],
934                    }),
935                    potential_reward: Some(79984390135364555),
936                    delegation_fee: Some(6.25),
937                    uptime: Some(100.0),
938                    connected: true,
939                    delegator_count: Some(0),
940                    delegator_weight: Some(0),
941                    ..ApiPrimaryValidator::default()
942                },
943                ApiPrimaryValidator {
944                    tx_id: ids::Id::from_str("EjKZm5eEajWu151cfPms7PvMjyaQk36qTSz1MfnZRU5x5bNxz")
945                        .unwrap(),
946
947                    start_time: 1648312635,
948                    end_time: 1679848635,
949                    stake_amount: Some(100000000000000000),
950                    node_id: node::Id::from_str("NodeID-JLR7d6z9cwCbkoPcPsnjkm6gq4xz7c4oT")
951                        .unwrap(),
952                    validation_reward_owner: Some(ApiOwner {
953                        locktime: 0,
954                        threshold: 1,
955                        addresses: vec![
956                            "P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs".to_string()
957                        ],
958                    }),
959                    delegation_reward_owner: Some(ApiOwner {
960                        locktime: 0,
961                        threshold: 1,
962                        addresses: vec![
963                            "P-custom1vkzy5p2qtumx9svjs9pvds48s0hcw80f962vrs".to_string()
964                        ],
965                    }),
966                    potential_reward: Some(77148186230865960),
967                    delegation_fee: Some(6.25),
968                    uptime: Some(100.0),
969                    connected: true,
970                    delegator_count: Some(0),
971                    delegator_weight: Some(0),
972                    ..ApiPrimaryValidator::default()
973                },
974            ])),
975        }),
976        error: None,
977    };
978    assert_eq!(resp, expected);
979}
980
981/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_pending_validators --exact --show-output
982#[test]
983
984fn test_get_pending_validators() {
985    use std::str::FromStr;
986
987    // ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetcurrentvalidators>
988    let resp: GetPendingValidatorsResponse = serde_json::from_str(
989        r#"
990{
991  "jsonrpc": "2.0",
992  "result": {
993    "validators": [
994      {
995        "txID": "2NNkpYTGfTFLSGXJcHtVv6drwVU2cczhmjK2uhvwDyxwsjzZMm",
996        "startTime": "1600368632",
997        "endTime": "1602960455",
998        "stakeAmount": "200000000000",
999        "nodeID": "NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD",
1000        "delegationFee": "10.0000",
1001        "connected": false
1002      }
1003    ],
1004    "delegators": [
1005      {
1006        "txID": "Bbai8nzGVcyn2VmeYcbS74zfjJLjDacGNVuzuvAQkHn1uWfoV",
1007        "startTime": "1600368523",
1008        "endTime": "1602960342",
1009        "stakeAmount": "20000000000",
1010        "nodeID": "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
1011      }
1012    ]
1013  },
1014  "id": 1
1015}
1016        "#,
1017    )
1018    .unwrap();
1019
1020    let expected = GetPendingValidatorsResponse {
1021        jsonrpc: "2.0".to_string(),
1022        id: 1,
1023        result: Some(GetPendingValidatorsResult {
1024            validators: <Vec<ApiPrimaryValidator>>::from([ApiPrimaryValidator {
1025                tx_id: ids::Id::from_str("2NNkpYTGfTFLSGXJcHtVv6drwVU2cczhmjK2uhvwDyxwsjzZMm")
1026                    .unwrap(),
1027                start_time: 1600368632,
1028                end_time: 1602960455,
1029                stake_amount: Some(200000000000),
1030                node_id: node::Id::from_str("NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD").unwrap(),
1031                delegation_fee: Some(10.0),
1032                connected: false,
1033                ..ApiPrimaryValidator::default()
1034            }]),
1035            delegators: <Vec<ApiPrimaryDelegator>>::from([ApiPrimaryDelegator {
1036                tx_id: ids::Id::from_str("Bbai8nzGVcyn2VmeYcbS74zfjJLjDacGNVuzuvAQkHn1uWfoV")
1037                    .unwrap(),
1038                start_time: 1600368523,
1039                end_time: 1602960342,
1040                stake_amount: 20000000000,
1041                node_id: node::Id::from_str("NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg").unwrap(),
1042                ..ApiPrimaryDelegator::default()
1043            }]),
1044        }),
1045        error: None,
1046    };
1047    assert_eq!(resp, expected);
1048}
1049
1050/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#APIUTXO>
1051#[serde_as]
1052#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
1053pub struct ApiUtxo {
1054    #[serde_as(as = "DisplayFromStr")]
1055    pub locktime: u64,
1056    #[serde_as(as = "DisplayFromStr")]
1057    pub amount: u64,
1058
1059    pub address: String,
1060    pub message: Option<String>,
1061}
1062
1063/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetsubnets>
1064#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
1065pub struct GetSubnetsResponse {
1066    pub jsonrpc: String,
1067    pub id: u32,
1068
1069    #[serde(skip_serializing_if = "Option::is_none")]
1070    pub result: Option<GetSubnetsResult>,
1071
1072    #[serde(skip_serializing_if = "Option::is_none")]
1073    pub error: Option<jsonrpc::ResponseError>,
1074}
1075
1076/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetsubnets>
1077#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
1078pub struct GetSubnetsResult {
1079    #[serde(skip_serializing_if = "Option::is_none")]
1080    pub subnets: Option<Vec<Subnet>>,
1081}
1082
1083#[serde_as]
1084#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
1085pub struct Subnet {
1086    pub id: ids::Id,
1087
1088    #[serde(rename = "controlKeys", skip_serializing_if = "Option::is_none")]
1089    pub control_keys: Option<Vec<ids::short::Id>>,
1090
1091    #[serde_as(as = "DisplayFromStr")]
1092    pub threshold: u32,
1093}
1094
1095impl Default for Subnet {
1096    fn default() -> Self {
1097        Self {
1098            id: ids::Id::empty(),
1099            control_keys: None,
1100            threshold: 0,
1101        }
1102    }
1103}
1104
1105/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_subnets --exact --show-output
1106#[test]
1107fn test_get_subnets() {
1108    use crate::ids;
1109    use std::str::FromStr;
1110
1111    // ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetsubnets>
1112    let resp: GetSubnetsResponse = serde_json::from_str(
1113        "
1114
1115{
1116    \"jsonrpc\": \"2.0\",
1117    \"result\": {
1118        \"subnets\": [
1119            {
1120                \"id\": \"hW8Ma7dLMA7o4xmJf3AXBbo17bXzE7xnThUd3ypM4VAWo1sNJ\",
1121                \"controlKeys\": [
1122                    \"KNjXsaA1sZsaKCD1cd85YXauDuxshTes2\",
1123                    \"Aiz4eEt5xv9t4NCnAWaQJFNz5ABqLtJkR\"
1124                ],
1125                \"threshold\": \"2\"
1126            }
1127        ]
1128    },
1129    \"id\": 1
1130}
1131
1132",
1133    )
1134    .unwrap();
1135
1136    let expected = GetSubnetsResponse {
1137        jsonrpc: "2.0".to_string(),
1138        id: 1,
1139        result: Some(GetSubnetsResult {
1140            subnets: Some(vec![Subnet {
1141                id: ids::Id::from_str("hW8Ma7dLMA7o4xmJf3AXBbo17bXzE7xnThUd3ypM4VAWo1sNJ").unwrap(),
1142                control_keys: Some(vec![
1143                    ids::short::Id::from_str("KNjXsaA1sZsaKCD1cd85YXauDuxshTes2").unwrap(),
1144                    ids::short::Id::from_str("Aiz4eEt5xv9t4NCnAWaQJFNz5ABqLtJkR").unwrap(),
1145                ]),
1146                threshold: 2,
1147            }]),
1148        }),
1149        error: None,
1150    };
1151    assert_eq!(resp, expected);
1152}
1153
1154/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetblockchains>
1155#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
1156pub struct GetBlockchainsResponse {
1157    pub jsonrpc: String,
1158    pub id: u32,
1159
1160    #[serde(skip_serializing_if = "Option::is_none")]
1161    pub result: Option<GetBlockchainsResult>,
1162
1163    #[serde(skip_serializing_if = "Option::is_none")]
1164    pub error: Option<jsonrpc::ResponseError>,
1165}
1166
1167/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetblockchains>
1168#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
1169pub struct GetBlockchainsResult {
1170    #[serde(skip_serializing_if = "Option::is_none")]
1171    pub blockchains: Option<Vec<Blockchain>>,
1172}
1173
1174#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, PartialOrd, Ord, Hash)]
1175pub struct Blockchain {
1176    pub id: ids::Id,
1177    pub name: String,
1178    #[serde(rename = "subnetID")]
1179    pub subnet_id: ids::Id,
1180    #[serde(rename = "vmID")]
1181    pub vm_id: ids::Id,
1182}
1183
1184impl Default for Blockchain {
1185    fn default() -> Self {
1186        Self {
1187            id: ids::Id::empty(),
1188            name: String::new(),
1189            subnet_id: ids::Id::empty(),
1190            vm_id: ids::Id::empty(),
1191        }
1192    }
1193}
1194
1195/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_blockchains --exact --show-output
1196#[test]
1197fn test_get_blockchains() {
1198    use crate::ids;
1199    use std::str::FromStr;
1200
1201    // ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformgetblockchains>
1202    let resp: GetBlockchainsResponse = serde_json::from_str(
1203        "
1204
1205{
1206    \"jsonrpc\": \"2.0\",
1207    \"result\": {
1208        \"blockchains\": [
1209            {
1210                \"id\": \"yC7LZEwfaYhEvreNm48iGMtASiukPaT4899N9df2rkhWUzDeQ\",
1211                \"name\": \"subnetevm\",
1212                \"subnetID\": \"268FZNRqAUwE4baMPkEQj48gnbGz34pgocWZvHQaUpnwb9Cp7i\",
1213                \"vmID\": \"srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy\"
1214            },
1215            {
1216                \"id\": \"27sJotKt62mibBwKFAwxvCKn8KEbdvk4Bn3nFSVJSZebrxMfdU\",
1217                \"name\": \"C-Chain\",
1218                \"subnetID\": \"11111111111111111111111111111111LpoYY\",
1219                \"vmID\": \"mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6\"
1220            },
1221            {
1222                \"id\": \"2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM\",
1223                \"name\": \"X-Chain\",
1224                \"subnetID\": \"11111111111111111111111111111111LpoYY\",
1225                \"vmID\": \"jvYyfQTxGMJLuGWa55kdP2p2zSUYsQ5Raupu4TW34ZAUBAbtq\"
1226            }
1227        ]
1228    },
1229    \"id\": 1
1230}
1231
1232",
1233    )
1234    .unwrap();
1235
1236    let expected = GetBlockchainsResponse {
1237        jsonrpc: "2.0".to_string(),
1238        id: 1,
1239        result: Some(GetBlockchainsResult {
1240            blockchains: Some(vec![
1241                Blockchain {
1242                    id: ids::Id::from_str("yC7LZEwfaYhEvreNm48iGMtASiukPaT4899N9df2rkhWUzDeQ")
1243                        .unwrap(),
1244                    name: String::from("subnetevm"),
1245                    subnet_id: ids::Id::from_str(
1246                        "268FZNRqAUwE4baMPkEQj48gnbGz34pgocWZvHQaUpnwb9Cp7i",
1247                    )
1248                    .unwrap(),
1249                    vm_id: ids::Id::from_str("srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy")
1250                        .unwrap(),
1251                },
1252                Blockchain {
1253                    id: ids::Id::from_str("27sJotKt62mibBwKFAwxvCKn8KEbdvk4Bn3nFSVJSZebrxMfdU")
1254                        .unwrap(),
1255                    name: String::from("C-Chain"),
1256                    subnet_id: ids::Id::empty(),
1257                    vm_id: ids::Id::from_str("mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6")
1258                        .unwrap(),
1259                },
1260                Blockchain {
1261                    id: ids::Id::from_str("2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM")
1262                        .unwrap(),
1263                    name: String::from("X-Chain"),
1264                    subnet_id: ids::Id::empty(),
1265                    vm_id: ids::Id::from_str("jvYyfQTxGMJLuGWa55kdP2p2zSUYsQ5Raupu4TW34ZAUBAbtq")
1266                        .unwrap(),
1267                },
1268            ]),
1269        }),
1270        error: None,
1271    };
1272    assert_eq!(resp, expected);
1273}
1274
1275/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformissuetx>
1276#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
1277pub struct GetBlockchainStatusRequest {
1278    pub jsonrpc: String,
1279    pub id: u32,
1280
1281    pub method: String,
1282
1283    #[serde(skip_serializing_if = "Option::is_none")]
1284    pub params: Option<GetBlockchainStatusParams>,
1285}
1286
1287impl Default for GetBlockchainStatusRequest {
1288    fn default() -> Self {
1289        Self {
1290            jsonrpc: String::from(super::DEFAULT_VERSION),
1291            id: super::DEFAULT_ID,
1292            method: String::new(),
1293            params: None,
1294        }
1295    }
1296}
1297
1298impl GetBlockchainStatusRequest {
1299    pub fn encode_json(&self) -> io::Result<String> {
1300        serde_json::to_string(&self)
1301            .map_err(|e| Error::new(ErrorKind::Other, format!("failed to serialize JSON {}", e)))
1302    }
1303}
1304
1305#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
1306pub struct GetBlockchainStatusParams {
1307    #[serde(rename = "blockchainID")]
1308    pub blockchain_id: ids::Id,
1309}
1310
1311#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
1312pub struct GetBlockchainStatusResponse {
1313    pub jsonrpc: String,
1314    pub id: u32,
1315
1316    #[serde(skip_serializing_if = "Option::is_none")]
1317    pub result: Option<GetBlockchainStatusResult>,
1318
1319    #[serde(skip_serializing_if = "Option::is_none")]
1320    pub error: Option<super::ResponseError>,
1321}
1322
1323impl Default for GetBlockchainStatusResponse {
1324    fn default() -> Self {
1325        Self {
1326            jsonrpc: "2.0".to_string(),
1327            id: 1,
1328            result: None,
1329            error: None,
1330        }
1331    }
1332}
1333
1334/// ref. <https://docs.avax.network/apis/avalanchego/apis/p-chain#platformissuetx>
1335#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
1336pub struct GetBlockchainStatusResult {
1337    pub status: String,
1338}
1339
1340/// RUST_LOG=debug cargo test --package avalanche-types --lib -- jsonrpc::platformvm::test_get_blockchain_status --exact --show-output
1341#[test]
1342fn test_get_blockchain_status() {
1343    let resp: GetBlockchainStatusResponse = serde_json::from_str(
1344        "
1345    
1346    {
1347        \"jsonrpc\": \"2.0\",
1348        \"result\": {
1349            \"status\": \"Created\"
1350        },
1351        \"id\": 1
1352    }
1353    
1354    ",
1355    )
1356    .unwrap();
1357
1358    let expected = GetBlockchainStatusResponse {
1359        jsonrpc: "2.0".to_string(),
1360        id: 1,
1361        result: Some(GetBlockchainStatusResult {
1362            status: String::from("Created"),
1363        }),
1364        error: None,
1365    };
1366    assert_eq!(resp, expected);
1367}