#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(any(feature = "v12", feature = "v13", feature = "v14")))]
compile_error!("Enable one of the metadata versions");
#[cfg(all(feature = "v12", feature = "v13", feature = "v14"))]
compile_error!("Only one metadata version can be enabled at the moment");
#[cfg(all(feature = "v12", feature = "v13"))]
compile_error!("Only one metadata version can be enabled at the moment");
#[cfg(all(feature = "v12", feature = "v14"))]
compile_error!("Only one metadata version can be enabled at the moment");
#[cfg(all(feature = "v13", feature = "v14"))]
compile_error!("Only one metadata version can be enabled at the moment");
#[macro_use]
extern crate alloc;
pub use codec;
pub use frame_metadata::RuntimeMetadataPrefixed;
pub use meta::Metadata;
pub use meta_ext as meta;
#[cfg(feature = "json")]
pub use scales::JsonValue;
#[cfg(feature = "v14")]
pub use scales::Value;
use async_trait::async_trait;
use core::{fmt, ops::Deref};
use meta::{Entry, Meta};
#[cfg(feature = "std")]
use once_cell::sync::OnceCell;
#[cfg(not(feature = "std"))]
use once_cell::unsync::OnceCell;
use prelude::*;
#[cfg(feature = "v14")]
use scale_info::PortableRegistry;
mod prelude {
pub use alloc::boxed::Box;
pub use alloc::string::{String, ToString};
pub use alloc::vec::Vec;
}
pub type Result<T> = core::result::Result<T, Error>;
#[cfg(any(feature = "http", feature = "http-web"))]
pub mod http;
#[cfg(feature = "ws")]
pub mod ws;
mod hasher;
pub mod meta_ext;
#[cfg(any(feature = "http", feature = "http-web", feature = "ws"))]
mod rpc;
#[derive(Debug)]
pub struct Sube<B> {
backend: B,
meta: OnceCell<Metadata>,
}
impl<B: Backend> Sube<B> {
pub fn new(backend: B) -> Self {
Sube {
backend,
meta: OnceCell::new(),
}
}
pub fn new_with_meta(backend: B, meta: Metadata) -> Self {
Sube {
backend,
meta: meta.into(),
}
}
pub async fn metadata(&self) -> Result<&Metadata> {
match self.meta.get() {
Some(meta) => Ok(meta),
None => {
let meta = self.backend.metadata().await?;
self.meta.set(meta).expect("unset");
Ok(self.meta.get().unwrap())
}
}
}
#[cfg(feature = "v14")]
pub async fn query(&self, key: &str) -> Result<Value<'_>> {
let key = self.key_from_path(key).await?;
let res = self.query_storage(&key).await?;
let reg = self.registry().await?;
Ok(Value::new(res, key.1, reg))
}
pub async fn key_from_path(&self, path: &str) -> Result<StorageKey> {
StorageKey::from_uri(self.metadata().await?, path)
}
#[cfg(feature = "v14")]
pub async fn registry(&self) -> Result<&PortableRegistry> {
Ok(&self.metadata().await?.types)
}
#[cfg(feature = "decode")]
pub async fn decode<'a, T>(
&'a self,
data: T,
ty: u32,
) -> Result<impl serde::Serialize + codec::Encode + 'a>
where
T: Into<scales::Bytes> + 'static,
{
Ok(Value::new(data.into(), ty, self.registry().await?))
}
}
impl<B: Backend> From<B> for Sube<B> {
fn from(b: B) -> Self {
Sube::new(b)
}
}
impl<T: Backend> Deref for Sube<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.backend
}
}
#[async_trait]
pub trait Backend {
async fn query_storage(&self, key: &StorageKey) -> Result<Vec<u8>>;
async fn submit<T>(&self, ext: T) -> Result<()>
where
T: AsRef<[u8]> + Send;
async fn metadata(&self) -> Result<Metadata>;
}
pub struct Offline(pub Metadata);
#[async_trait]
impl Backend for Offline {
async fn query_storage(&self, _key: &StorageKey) -> Result<Vec<u8>> {
Err(Error::ChainUnavailable)
}
async fn submit<T>(&self, _ext: T) -> Result<()>
where
T: AsRef<[u8]> + Send,
{
Err(Error::ChainUnavailable)
}
async fn metadata(&self) -> Result<Metadata> {
Ok(self.0.clone_meta())
}
}
#[derive(Clone, Debug)]
pub enum Error {
ChainUnavailable,
BadInput,
BadKey,
BadMetadata,
Decode(codec::Error),
NoMetadataLoaded,
Node(String),
ParseStorageItem,
StorageKeyNotFound,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Node(e) => write!(f, "{:}", e),
_ => write!(f, "{:?}", self),
}
}
}
#[cfg(feature = "ws")]
impl From<async_tungstenite::tungstenite::Error> for Error {
fn from(_err: async_tungstenite::tungstenite::Error) -> Self {
Error::ChainUnavailable
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[derive(Clone, Debug)]
pub struct StorageKey(Vec<u8>, u32);
impl StorageKey {
pub fn from_uri(meta: &Metadata, uri: &str) -> Result<Self> {
let (pallet, item, map_keys) = Self::parse_uri(uri).ok_or(Error::ParseStorageItem)?;
log::debug!(
"StorageKey parts: [module]={} [item]={} [keys]={:?}",
pallet,
item,
map_keys,
);
Self::new(meta, &pallet, &item, &map_keys)
}
pub fn new(meta: &Metadata, pallet: &str, item: &str, map_keys: &[&str]) -> Result<Self> {
let entry = meta
.storage_entry(pallet, item)
.ok_or(Error::StorageKeyNotFound)?;
let key = entry
.key(pallet, map_keys)
.ok_or(Error::StorageKeyNotFound)?;
Ok(StorageKey(key, entry.ty_id()))
}
pub fn parse_uri(uri: &str) -> Option<(String, String, Vec<&str>)> {
let mut path = uri.trim_matches('/').split('/');
let pallet = path.next().map(to_camel)?;
let item = path.next().map(to_camel)?;
let map_keys = path.collect::<Vec<_>>();
Some((pallet, item, map_keys))
}
}
impl fmt::Display for StorageKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x{}", hex::encode(&self.0))
}
}
impl AsRef<[u8]> for StorageKey {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
fn to_camel(term: &str) -> String {
let underscore_count = term.chars().filter(|c| *c == '-').count();
let mut result = String::with_capacity(term.len() - underscore_count);
let mut at_new_word = true;
for c in term.chars().skip_while(|&c| c == '-') {
if c == '-' {
at_new_word = true;
} else if at_new_word {
result.push(c.to_ascii_uppercase());
at_new_word = false;
} else {
result.push(c);
}
}
result
}