// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of subxt. // // subxt is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // subxt is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with subxt. If not, see . mod calls; mod events; mod extrinsic; mod storage; use super::GeneratedTypeDerives; use crate::{ ir, struct_def::StructDef, types::TypeGenerator, }; use codec::Decode; use frame_metadata::{ v14::RuntimeMetadataV14, RuntimeMetadata, RuntimeMetadataPrefixed, }; use heck::SnakeCase as _; use proc_macro2::TokenStream as TokenStream2; use proc_macro_error::abort_call_site; use quote::{ format_ident, quote, }; use std::{ collections::HashMap, fs, io::Read, path, string::ToString, }; use syn::{ parse_quote, punctuated::Punctuated, }; pub fn generate_runtime_api

( item_mod: syn::ItemMod, path: P, generated_type_derives: Option>, ) -> TokenStream2 where P: AsRef, { let mut file = fs::File::open(&path).unwrap_or_else(|e| { abort_call_site!("Failed to open {}: {}", path.as_ref().to_string_lossy(), e) }); let mut bytes = Vec::new(); file.read_to_end(&mut bytes) .unwrap_or_else(|e| abort_call_site!("Failed to read metadata file: {}", e)); let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..]) .unwrap_or_else(|e| abort_call_site!("Failed to decode metadata: {}", e)); let mut derives = GeneratedTypeDerives::default(); if let Some(user_derives) = generated_type_derives { derives.append(user_derives.iter().cloned()) } let generator = RuntimeGenerator::new(metadata); generator.generate_runtime(item_mod, derives) } pub struct RuntimeGenerator { metadata: RuntimeMetadataV14, } impl RuntimeGenerator { pub fn new(metadata: RuntimeMetadataPrefixed) -> Self { match metadata.1 { RuntimeMetadata::V14(v14) => Self { metadata: v14 }, _ => panic!("Unsupported metadata version {:?}", metadata.1), } } pub fn generate_runtime( &self, item_mod: syn::ItemMod, derives: GeneratedTypeDerives, ) -> TokenStream2 { let item_mod_ir = ir::ItemMod::from(item_mod); // some hardcoded default type substitutes, can be overridden by user let mut type_substitutes = [ ( "bitvec::order::Lsb0", parse_quote!(::subxt::bitvec::order::Lsb0), ), ( "bitvec::order::Msb0", parse_quote!(::subxt::bitvec::order::Msb0), ), ( "sp_core::crypto::AccountId32", parse_quote!(::subxt::sp_core::crypto::AccountId32), ), ( "primitive_types::H256", parse_quote!(::subxt::sp_core::H256), ), ( "sp_runtime::multiaddress::MultiAddress", parse_quote!(::subxt::sp_runtime::MultiAddress), ), ( "frame_support::traits::misc::WrapperKeepOpaque", parse_quote!(::subxt::WrapperKeepOpaque), ), ] .iter() .map(|(path, substitute): &(&str, syn::TypePath)| { (path.to_string(), substitute.clone()) }) .collect::>(); for (path, substitute) in item_mod_ir.type_substitutes().iter() { type_substitutes.insert(path.to_string(), substitute.clone()); } let type_gen = TypeGenerator::new( &self.metadata.types, "runtime_types", type_substitutes, derives.clone(), ); let types_mod = type_gen.generate_types_mod(); let types_mod_ident = types_mod.ident(); let pallets_with_mod_names = self .metadata .pallets .iter() .map(|pallet| { ( pallet, format_ident!("{}", pallet.name.to_string().to_snake_case()), ) }) .collect::>(); let modules = pallets_with_mod_names.iter().map(|(pallet, mod_name)| { let calls = if let Some(ref calls) = pallet.calls { calls::generate_calls(&type_gen, pallet, calls, types_mod_ident) } else { quote!() }; let event = if let Some(ref event) = pallet.event { events::generate_events(&type_gen, pallet, event, types_mod_ident) } else { quote!() }; let storage_mod = if let Some(ref storage) = pallet.storage { storage::generate_storage(&type_gen, pallet, storage, types_mod_ident) } else { quote!() }; quote! { pub mod #mod_name { use super::#types_mod_ident; #calls #event #storage_mod } } }); let outer_event_variants = self.metadata.pallets.iter().filter_map(|p| { let variant_name = format_ident!("{}", p.name); let mod_name = format_ident!("{}", p.name.to_string().to_snake_case()); let index = proc_macro2::Literal::u8_unsuffixed(p.index); p.event.as_ref().map(|_| { quote! { #[codec(index = #index)] #variant_name(#mod_name::Event), } }) }); let outer_event = quote! { #derives pub enum Event { #( #outer_event_variants )* } }; let extensions = extrinsic::generate_extensions(&type_gen, &self.metadata.extrinsic, types_mod_ident); let mod_ident = item_mod_ir.ident; let pallets_with_storage = pallets_with_mod_names .iter() .filter_map(|(pallet, pallet_mod_name)| { pallet.storage.as_ref().map(|_| pallet_mod_name) }); let pallets_with_calls = pallets_with_mod_names .iter() .filter_map(|(pallet, pallet_mod_name)| { pallet.calls.as_ref().map(|_| pallet_mod_name) }); quote! { #[allow(dead_code, unused_imports, non_camel_case_types)] pub mod #mod_ident { #outer_event #( #modules )* #types_mod /// Default configuration of common types for a target Substrate runtime. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct DefaultConfig; impl ::subxt::Config for DefaultConfig { type Index = u32; type BlockNumber = u32; type Hash = ::subxt::sp_core::H256; type Hashing = ::subxt::sp_runtime::traits::BlakeTwo256; type AccountId = ::subxt::sp_runtime::AccountId32; type Address = ::subxt::sp_runtime::MultiAddress; type Header = ::subxt::sp_runtime::generic::Header< Self::BlockNumber, ::subxt::sp_runtime::traits::BlakeTwo256 >; type Signature = ::subxt::sp_runtime::MultiSignature; type Extrinsic = ::subxt::sp_runtime::OpaqueExtrinsic; } impl ::subxt::ExtrinsicExtraData for DefaultConfig { type AccountData = AccountData; type Extra = ::subxt::DefaultExtra; } pub type AccountData = self::system::storage::Account; impl ::subxt::AccountData for AccountData { fn nonce(result: &::Value) -> ::Index { result.nonce } fn storage_entry(account_id: ::AccountId) -> Self { Self(account_id) } } pub struct RuntimeApi> { pub client: ::subxt::Client, } impl ::core::convert::From<::subxt::Client> for RuntimeApi where T: ::subxt::Config + ::subxt::ExtrinsicExtraData, { fn from(client: ::subxt::Client) -> Self { Self { client } } } impl<'a, T> RuntimeApi where T: ::subxt::Config + ::subxt::ExtrinsicExtraData, { pub fn storage(&'a self) -> StorageApi<'a, T> { StorageApi { client: &self.client } } pub fn tx(&'a self) -> TransactionApi<'a, T> { TransactionApi { client: &self.client } } } pub struct StorageApi<'a, T> where T: ::subxt::Config + ::subxt::ExtrinsicExtraData, { client: &'a ::subxt::Client, } impl<'a, T> StorageApi<'a, T> where T: ::subxt::Config + ::subxt::ExtrinsicExtraData, { #( pub fn #pallets_with_storage(&self) -> #pallets_with_storage::storage::StorageApi<'a, T> { #pallets_with_storage::storage::StorageApi::new(self.client) } )* } pub struct TransactionApi<'a, T: ::subxt::Config + ::subxt::ExtrinsicExtraData> { client: &'a ::subxt::Client, } impl<'a, T> TransactionApi<'a, T> where T: ::subxt::Config + ::subxt::ExtrinsicExtraData, { #( pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T> { #pallets_with_calls::calls::TransactionApi::new(self.client) } )* } } } } } pub fn generate_structs_from_variants( type_gen: &TypeGenerator, type_id: u32, error_message_type_name: &str, ) -> Vec { let ty = type_gen.resolve_type(type_id); if let scale_info::TypeDef::Variant(variant) = ty.type_def() { variant .variants() .iter() .map(|var| { StructDef::new( var.name(), var.fields(), Some(syn::parse_quote!(pub)), type_gen, ) }) .collect() } else { abort_call_site!( "{} type should be an variant/enum type", error_message_type_name ) } }