Skip to content

Commit

Permalink
Split subxt (#102)
Browse files Browse the repository at this point in the history
* Proc macro improvements.

* Use proc-macros.

* Update examples.

* Fix build.

* Run rustfmt.

* Fix total issuance test.

* Remove gas limit from put code call.

* Handle runtime errors.

* Fix tests.

* Make test more reliable.

* Revert "Handle runtime errors."

This reverts commit 26f30a9.

* Use expect instead of unwrap.

* Parse marker type.

* Fetch doesn't fail.
  • Loading branch information
dvc94ch authored May 12, 2020
1 parent 825f3ab commit f861f3f
Show file tree
Hide file tree
Showing 21 changed files with 697 additions and 564 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ pallet-indices = { version = "2.0.0-alpha.7", package = "pallet-indices" }
hex = "0.4.0"
sp-rpc = { version = "2.0.0-alpha.7", package = "sp-rpc" }
sp-core = { version = "2.0.0-alpha.7", package = "sp-core" }
sp-transaction-pool = { version = "2.0.0-alpha.7", package = "sp-transaction-pool" }
sc-rpc-api = { version = "0.8.0-alpha.7", package = "sc-rpc-api" }
sp-transaction-pool = { version = "2.0.0-alpha.7", package = "sp-transaction-pool" }
substrate-subxt-proc-macro = { path = "proc-macro" }

[dev-dependencies]
async-std = { version = "1.5.0", features = ["attributes"] }
Expand Down
39 changes: 18 additions & 21 deletions examples/fetch_remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,28 @@
// along with substrate-subxt. If not, see <http://www.gnu.org/licenses/>.

use substrate_subxt::{
system::System,
Error,
ClientBuilder,
KusamaRuntime,
};

fn main() {
async_std::task::block_on(async move {
env_logger::init();
#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

let block_hash = fetch_block_hash(1).await;
match block_hash {
Ok(Some(hash)) => println!("Block hash for block number 1: {}", hash),
Ok(None) => println!("Block number 1 not found."),
Err(_) => eprintln!("Failed to fetch block hash"),
}
});
}

async fn fetch_block_hash(
block_number: u32,
) -> Result<Option<<KusamaRuntime as System>::Hash>, Error> {
substrate_subxt::ClientBuilder::<KusamaRuntime>::new()
let client = ClientBuilder::<KusamaRuntime>::new()
.set_url("wss://kusama-rpc.polkadot.io")
.build()
.await?
.block_hash(Some(block_number.into()))
.await
.await?;

let block_number = 1;

let block_hash = client.block_hash(Some(block_number.into())).await?;

if let Some(hash) = block_hash {
println!("Block hash for block number {}: {}", block_number, hash);
} else {
println!("Block number {} not found.", block_number);
}

Ok(())
}
37 changes: 14 additions & 23 deletions examples/kusama_balance_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,27 @@

use sp_keyring::AccountKeyring;
use substrate_subxt::{
balances,
Error,
balances::*,
ClientBuilder,
KusamaRuntime,
};

fn main() {
async_std::task::block_on(async move {
env_logger::init();
#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

let xt_result = transfer_balance().await;
match xt_result {
Ok(hash) => println!("Balance transfer extrinsic submitted: {}", hash),
Err(_) => eprintln!("Balance transfer extrinisic failed"),
}
});
}

async fn transfer_balance() -> Result<sp_core::H256, Error> {
let signer = AccountKeyring::Alice.pair();
let dest = AccountKeyring::Bob.to_account_id().into();

// note use of `KusamaRuntime`
substrate_subxt::ClientBuilder::<KusamaRuntime>::new()
.build()
.await?
let client = ClientBuilder::<KusamaRuntime>::new().build().await?;

let hash = client
.xt(signer, None)
.await?
.submit(balances::TransferCall {
to: &dest,
amount: 10_000,
})
.await
.transfer(&dest, 10_000)
.await?;

println!("Balance transfer extrinsic submitted: {}", hash);

Ok(())
}
22 changes: 10 additions & 12 deletions examples/submit_and_watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

use sp_keyring::AccountKeyring;
use substrate_subxt::{
balances,
DefaultNodeRuntime as Runtime,
balances::*,
ClientBuilder,
DefaultNodeRuntime,
};

#[async_std::main]
Expand All @@ -27,18 +28,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let signer = AccountKeyring::Alice.pair();
let dest = AccountKeyring::Bob.to_account_id().into();

let cli = substrate_subxt::ClientBuilder::<Runtime>::new()
.build()
.await?;
let xt = cli.xt(signer, None).await?;
let xt_result = xt
let client = ClientBuilder::<DefaultNodeRuntime>::new().build().await?;
let result = client
.xt(signer, None)
.await?
.watch()
.submit(balances::TransferCall {
to: &dest,
amount: 10_000,
})
.transfer(&dest, 10_000)
.await?;
if let Some(event) = xt_result.find_event::<balances::TransferEvent<_>>()? {

if let Some(event) = result.transfer()? {
println!("Balance transfer success: value: {:?}", event.amount);
} else {
println!("Failed to find Balances::Transfer Event");
Expand Down
5 changes: 1 addition & 4 deletions proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,10 @@ synstructure = "0.12.3"

[dev-dependencies]
async-std = { version = "1.5.0", features = ["attributes"] }
codec = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive", "full"] }
codec = { package = "parity-scale-codec", version = "1.2.0", features = ["derive"] }
env_logger = "0.7.1"
frame-support = "2.0.0-alpha.7"
pretty_assertions = "0.6.1"
sp-core = "2.0.0-alpha.7"
sp-keyring = "2.0.0-alpha.7"
sp-runtime = "2.0.0-alpha.7"
substrate-subxt = { path = ".." }
trybuild = "1.0.25"

Expand Down
139 changes: 102 additions & 37 deletions proc-macro/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ use synstructure::Structure;

pub fn call(s: Structure) -> TokenStream {
let subxt = utils::use_crate("substrate-subxt");
let codec = utils::use_crate("parity-scale-codec");
let sp_core = utils::use_crate("sp-core");
let sp_runtime = utils::use_crate("sp-runtime");
let ident = &s.ast().ident;
let generics = &s.ast().generics;
let params = utils::type_params(generics);
Expand All @@ -39,23 +36,35 @@ pub fn call(s: Structure) -> TokenStream {
"with_{}",
utils::path_to_ident(module).to_string().to_snake_case()
);
let call_name = ident.to_string().trim_end_matches("Call").to_snake_case();
let call = format_ident!("{}", call_name);
let call_trait = format_ident!("{}CallExt", call_name.to_camel_case());
let call_name = utils::ident_to_name(ident, "Call").to_snake_case();
let bindings = utils::bindings(&s);
let fields = bindings.iter().map(|bi| {
let ident = bi.ast().ident.as_ref().unwrap();
quote!(#ident,)
});
let args = bindings.iter().map(|bi| {
let ident = bi.ast().ident.as_ref().unwrap();
let ty = &bi.ast().ty;
quote!(#ident: #ty,)
});
let args = quote!(#(#args)*);
let ret = quote!(#subxt::ExtrinsicSuccess<T>);
let fields = utils::fields(&bindings);
let marker = utils::marker_field(&fields).unwrap_or_else(|| format_ident!("_"));
let filtered_fields = utils::filter_fields(&fields, &marker);
let args = utils::fields_to_args(&filtered_fields);
let build_struct = utils::build_struct(ident, &fields);
let xt_builder = generate_trait(
&module,
&call_name,
"XtBuilder",
quote!(&'a self),
quote!(T::Hash),
&args,
&build_struct,
&marker,
);
let events_subscriber = generate_trait(
&module,
&call_name,
"EventsSubscriber",
quote!(self),
quote!(#subxt::ExtrinsicSuccess<T>),
&args,
&build_struct,
&marker,
);

let expanded = quote! {
quote! {
impl#generics #subxt::Call<T> for #ident<#(#params),*> {
const MODULE: &'static str = MODULE;
const FUNCTION: &'static str = #call_name;
Expand All @@ -67,32 +76,55 @@ pub fn call(s: Structure) -> TokenStream {
}
}

#xt_builder

#events_subscriber
}
}

pub fn generate_trait(
module: &syn::Path,
call: &str,
ty: &str,
me: TokenStream,
ret: TokenStream,
args: &TokenStream,
build_struct: &TokenStream,
marker: &syn::Ident,
) -> TokenStream {
let subxt = utils::use_crate("substrate-subxt");
let codec = utils::use_crate("parity-scale-codec");
let call_trait = format_ident!("{}Call{}", call.to_camel_case(), ty);
let call = format_ident!("{}", call);
let ty = format_ident!("{}", ty);
quote! {
/// Call extension trait.
pub trait #call_trait<T: #module> {
/// Create and submit the extrinsic.
fn #call<'a>(
self,
#me,
#args
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<#ret, #subxt::Error>> + Send + 'a>>;
}

impl<T, P, S, E> #call_trait<T> for #subxt::EventsSubscriber<T, P, S, E>
impl<T, P, S, E> #call_trait<T> for #subxt::#ty<T, P, S, E>
where
T: #module + #subxt::system::System + Send + Sync,
P: #sp_core::Pair,
S: #sp_runtime::traits::Verify + #codec::Codec + From<P::Signature> + Send + 'static,
S::Signer: From<P::Public> + #sp_runtime::traits::IdentifyAccount<AccountId = T::AccountId>,
T: #module + #subxt::system::System + Send + Sync + 'static,
P: #subxt::sp_core::Pair,
S: #subxt::sp_runtime::traits::Verify + #codec::Codec + From<P::Signature> + Send + 'static,
S::Signer: From<P::Public> + #subxt::sp_runtime::traits::IdentifyAccount<AccountId = T::AccountId>,
T::Address: From<T::AccountId>,
E: #subxt::SignedExtra<T> + #sp_runtime::traits::SignedExtension + 'static,
E: #subxt::SignedExtra<T> + #subxt::sp_runtime::traits::SignedExtension + 'static,
{
fn #call<'a>(
self,
#me,
#args
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<#ret, #subxt::Error>> + Send + 'a>> {
Box::pin(self.submit(#ident { #(#fields)* }))
let #marker = core::marker::PhantomData::<T>;
Box::pin(self.submit(#build_struct))
}
}
};

TokenStream::from(expanded)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -121,29 +153,62 @@ mod tests {
}
}

pub trait TransferCallExt<T: Balances> {
/// Call extension trait.
pub trait TransferCallXtBuilder<T: Balances> {
/// Create and submit the extrinsic.
fn transfer<'a>(
&'a self,
to: &'a <T as System>::Address,
amount: T::Balance,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<T::Hash, substrate_subxt::Error>> + Send + 'a>>;
}

impl<T, P, S, E> TransferCallXtBuilder<T> for substrate_subxt::XtBuilder<T, P, S, E>
where
T: Balances + substrate_subxt::system::System + Send + Sync + 'static,
P: substrate_subxt::sp_core::Pair,
S: substrate_subxt::sp_runtime::traits::Verify + codec::Codec + From<P::Signature> + Send + 'static,
S::Signer: From<P::Public> + substrate_subxt::sp_runtime::traits::IdentifyAccount<
AccountId = T::AccountId>,
T::Address: From<T::AccountId>,
E: substrate_subxt::SignedExtra<T> + substrate_subxt::sp_runtime::traits::SignedExtension + 'static,
{
fn transfer<'a>(
&'a self,
to: &'a <T as System>::Address,
amount: T::Balance,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<T::Hash, substrate_subxt::Error>> + Send + 'a>> {
let _ = core::marker::PhantomData::<T>;
Box::pin(self.submit(TransferCall { to, amount, }))
}
}

/// Call extension trait.
pub trait TransferCallEventsSubscriber<T: Balances> {
/// Create and submit the extrinsic.
fn transfer<'a>(
self,
to: &'a <T as System>::Address,
amount: T::Balance,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<substrate_subxt::ExtrinsicSuccess<T>, substrate_subxt::Error>> + Send + 'a>>;
}

impl<T, P, S, E> TransferCallExt<T> for substrate_subxt::EventsSubscriber<T, P, S, E>
impl<T, P, S, E> TransferCallEventsSubscriber<T> for substrate_subxt::EventsSubscriber<T, P, S, E>
where
T: Balances + substrate_subxt::system::System + Send + Sync,
P: sp_core::Pair,
S: sp_runtime::traits::Verify + codec::Codec + From<P::Signature> + Send + 'static,
S::Signer: From<P::Public> + sp_runtime::traits::IdentifyAccount<
T: Balances + substrate_subxt::system::System + Send + Sync + 'static,
P: substrate_subxt::sp_core::Pair,
S: substrate_subxt::sp_runtime::traits::Verify + codec::Codec + From<P::Signature> + Send + 'static,
S::Signer: From<P::Public> + substrate_subxt::sp_runtime::traits::IdentifyAccount<
AccountId = T::AccountId>,
T::Address: From<T::AccountId>,
E: substrate_subxt::SignedExtra<T> + sp_runtime::traits::SignedExtension + 'static,
E: substrate_subxt::SignedExtra<T> + substrate_subxt::sp_runtime::traits::SignedExtension + 'static,
{
fn transfer<'a>(
self,
to: &'a <T as System>::Address,
amount: T::Balance,
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<substrate_subxt::ExtrinsicSuccess<T>, substrate_subxt::Error>> + Send + 'a>> {
let _ = core::marker::PhantomData::<T>;
Box::pin(self.submit(TransferCall { to, amount, }))
}
}
Expand Down
6 changes: 5 additions & 1 deletion proc-macro/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn event(s: Structure) -> TokenStream {
let ident = &s.ast().ident;
let generics = &s.ast().generics;
let module = utils::module_name(generics);
let event_name = ident.to_string().trim_end_matches("Event").to_camel_case();
let event_name = utils::ident_to_name(ident, "Event").to_camel_case();
let event = format_ident!("{}", event_name.to_snake_case());
let event_trait = format_ident!("{}EventExt", event_name);

Expand All @@ -42,7 +42,9 @@ pub fn event(s: Structure) -> TokenStream {
const EVENT: &'static str = #event_name;
}

/// Event extension trait.
pub trait #event_trait<T: #module> {
/// Retrieves the event.
fn #event(&self) -> Result<Option<#ident<T>>, #codec::Error>;
}

Expand Down Expand Up @@ -76,7 +78,9 @@ mod tests {
const EVENT: &'static str = "Transfer";
}

/// Event extension trait.
pub trait TransferEventExt<T: Balances> {
/// Retrieves the event.
fn transfer(&self) -> Result<Option<TransferEvent<T>>, codec::Error>;
}

Expand Down
Loading

0 comments on commit f861f3f

Please sign in to comment.