gekko_metadata/lib.rs
1//! Utilities to parse and process substrate metadata. Can also be enabled in
2//! `gekko` with the `"metadata"` feature.
3//!
4//! # Example
5//!
6//! ```no_run
7//! use gekko_metadata::*;
8//!
9//! // Parse runtime metadata
10//! let content = std::fs::read_to_string("metadata_kusama_9080.hex").unwrap();
11//! let data = parse_hex_metadata(content).unwrap().into_inner();
12//!
13//! // Get information about the extrinsic.
14//! let extr = data
15//! .find_module_extrinsic("Balances", "transfer_keep_alive")
16//! .unwrap();
17//!
18//! assert_eq!(extr.module_id, 4);
19//! assert_eq!(extr.dispatch_id, 3);
20//! assert_eq!(
21//! extr.args,
22//! vec![
23//! ("dest", "<T::Lookup as StaticLookup>::Source"),
24//! ("value", "Compact<T::Balance>"),
25//! ]
26//! );
27//! ```
28
29// INFO: The earliest metadata versions are available in the substrate repo at
30// commit: a31c01b398d958ccf0a24d8c1c11fb073df66212
31
32#[macro_use]
33extern crate serde;
34#[macro_use]
35extern crate parity_scale_codec;
36
37use self::version::*;
38use parity_scale_codec::{Decode, Error as ScaleError};
39use serde_json::Error as SerdeJsonError;
40
41type Result<T> = std::result::Result<T, Error>;
42
43pub mod version;
44
45/// Parameters and other information about an individual extrinsic.
46#[derive(Debug, Clone, Eq, PartialEq)]
47pub struct ExtrinsicInfo<'a> {
48 /// The module Id. This is required when encoding the final extrinsic.
49 pub module_id: usize,
50 /// The dispatch Id. This is required when encoding the final extrinsic.
51 pub dispatch_id: usize,
52 /// The name of the module.
53 pub module_name: &'a str,
54 /// The name of the extrinsic.
55 pub extrinsic_name: &'a str,
56 /// Arguments that must be passed as the extrinsics body. A sequence of
57 /// key-value pairs, indicating the name and the type, respectively.
58 pub args: Vec<(&'a str, &'a str)>,
59 /// Documentation of the extrinsic, as provided by the Substrate metadata.
60 pub documentation: Vec<&'a str>,
61}
62
63/// An interface to retrieve information about extrinsics on any Substrate
64/// metadata version.
65pub trait ModuleMetadataExt {
66 fn modules_extrinsics<'a>(&'a self) -> Vec<ExtrinsicInfo<'a>>;
67 fn find_module_extrinsic<'a>(
68 &'a self,
69 method: &str,
70 extrinsic: &str,
71 ) -> Option<ExtrinsicInfo<'a>>;
72}
73
74/// Errors that can occur when parsing Substrate metadata.
75#[derive(Debug)]
76pub enum Error {
77 ParseJsonRpcMetadata(SerdeJsonError),
78 ParseHexMetadata(hex::FromHexError),
79 ParseRawMetadata(ScaleError),
80 InvalidMetadataVersion,
81}
82
83/// Helper type when dealing with the Json RPC response returned by
84/// Substrates `state_getMetadata`.
85#[derive(Debug, Clone, Deserialize)]
86pub struct JsonRpcResponse {
87 pub jsonrpc: String,
88 pub result: String,
89}
90
91/// Convenience function for parsing the Json RPC response returned by Substrates
92/// `state_getMetadata`.
93///
94/// Must fit the [`JsonRpcResponse`] structure.
95pub fn parse_jsonrpc_metadata<T: AsRef<[u8]>>(json: T) -> Result<MetadataVersion> {
96 let resp = serde_json::from_slice::<JsonRpcResponse>(json.as_ref())
97 .map_err(|err| Error::ParseJsonRpcMetadata(err))?;
98
99 parse_hex_metadata(resp.result.as_bytes())
100}
101
102/// Convenience function for parsing the metadata from a HEX representation, as
103/// returned by `state_getMetadata`.
104pub fn parse_hex_metadata<T: AsRef<[u8]>>(hex: T) -> Result<MetadataVersion> {
105 let hex = hex.as_ref();
106
107 // The `hex` crate does not handle `0x`...
108 let slice = if hex.starts_with(b"0x") {
109 hex[2..].as_ref()
110 } else {
111 hex
112 };
113
114 parse_raw_metadata(hex::decode(slice).map_err(|err| Error::ParseHexMetadata(err))?)
115}
116
117/// Parse the raw Substrate metadata.
118pub fn parse_raw_metadata<T: AsRef<[u8]>>(raw: T) -> Result<MetadataVersion> {
119 let raw = raw.as_ref();
120
121 // Remove the magic number before decoding, if it exists. From the substrate
122 // docs:
123 // > "The hex blob that is returned by the JSON-RPCs state_getMetadata
124 // > method starts with a hard-coded magic number, 0x6d657461, which
125 // > represents "meta" in plain text."
126 let mut slice = if raw.starts_with(b"meta") {
127 raw[4..].as_ref()
128 } else {
129 raw
130 };
131
132 MetadataVersion::decode(&mut slice).map_err(|err| Error::ParseRawMetadata(err))
133}
134
135/// Identifier of all the available Substrate metadata versions.
136#[derive(Debug, Clone, PartialEq, Encode, Decode)]
137pub enum MetadataVersion {
138 V0,
139 V1,
140 V2,
141 V3,
142 V4,
143 V5,
144 V6,
145 V7,
146 V8,
147 V9,
148 V10,
149 V11,
150 V12,
151 V13(MetadataV13),
152}
153
154impl MetadataVersion {
155 /// Consumes the object and returns the inner metadata structure, expecting
156 /// the latest version. Results in an error if the version is not the latest.
157 pub fn into_latest(self) -> Result<MetadataV13> {
158 match self {
159 MetadataVersion::V13(data) => Ok(data),
160 _ => Err(Error::InvalidMetadataVersion),
161 }
162 }
163 /// Returns the version number as an integer.
164 pub fn version_number(&self) -> usize {
165 use MetadataVersion::*;
166
167 match self {
168 V0 => 0,
169 V1 => 1,
170 V2 => 2,
171 V3 => 3,
172 V4 => 4,
173 V5 => 5,
174 V6 => 6,
175 V7 => 7,
176 V8 => 8,
177 V9 => 9,
178 V10 => 10,
179 V11 => 11,
180 V12 => 12,
181 V13(_) => 13,
182 }
183 }
184 pub fn into_inner(self) -> impl ModuleMetadataExt {
185 match self {
186 MetadataVersion::V13(m) => m,
187 _ => panic!(),
188 }
189 }
190}