mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 15:51:12 +00:00
feat: Mortal extrinsic construction
This commit is contained in:
@@ -33,6 +33,8 @@ serde = { version = "1.0.115", features = ["derive"] }
|
||||
serde_json = "1.0.57"
|
||||
url = "2.1.1"
|
||||
codec = { package = "parity-scale-codec", version = "1.3.5", default-features = false, features = ["derive", "full"] }
|
||||
quote = "1.0.7"
|
||||
proc-macro2 = "1.0.19"
|
||||
|
||||
frame-metadata = { version = "12", package = "frame-metadata" }
|
||||
frame-support = { version = "2.0.0", package = "frame-support" }
|
||||
|
||||
@@ -20,6 +20,8 @@ use substrate_subxt::{
|
||||
ClientBuilder,
|
||||
KusamaRuntime,
|
||||
PairSigner,
|
||||
SignedOptions,
|
||||
DEFAULT_ERA_PERIOD
|
||||
};
|
||||
|
||||
#[async_std::main]
|
||||
@@ -30,7 +32,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
|
||||
let client = ClientBuilder::<KusamaRuntime>::new().build().await?;
|
||||
let hash = client.transfer(&signer, &dest, 10_000).await?;
|
||||
let hash = client.transfer(
|
||||
&signer,
|
||||
SignedOptions { era_period: Some(DEFAULT_ERA_PERIOD) },
|
||||
&dest,
|
||||
10_000
|
||||
).await?;
|
||||
|
||||
println!("Balance transfer extrinsic submitted: {}", hash);
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ use substrate_subxt::{
|
||||
ClientBuilder,
|
||||
DefaultNodeRuntime,
|
||||
PairSigner,
|
||||
SignedOptions,
|
||||
DEFAULT_ERA_PERIOD,
|
||||
};
|
||||
|
||||
#[async_std::main]
|
||||
@@ -33,7 +35,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
|
||||
let client = ClientBuilder::<DefaultNodeRuntime>::new().build().await?;
|
||||
let result = client.transfer_and_watch(&signer, &dest, 10_000).await?;
|
||||
let result = client.transfer_and_watch(
|
||||
&signer,
|
||||
SignedOptions { era_period: Some(DEFAULT_ERA_PERIOD) },
|
||||
&dest,
|
||||
10_000
|
||||
).await?;
|
||||
|
||||
if let Some(event) = result.transfer()? {
|
||||
println!("Balance transfer success: value: {:?}", event.amount);
|
||||
|
||||
@@ -27,6 +27,8 @@ use substrate_subxt::{
|
||||
EventSubscription,
|
||||
EventsDecoder,
|
||||
PairSigner,
|
||||
SignedOptions,
|
||||
DEFAULT_ERA_PERIOD,
|
||||
};
|
||||
|
||||
#[async_std::main]
|
||||
@@ -42,7 +44,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
decoder.with_balances();
|
||||
let mut sub = EventSubscription::<DefaultNodeRuntime>::new(sub, decoder);
|
||||
sub.filter_event::<TransferEvent<_>>();
|
||||
client.transfer(&signer, &dest, 10_000).await?;
|
||||
client.transfer(
|
||||
&signer,
|
||||
SignedOptions { era_period: Some(DEFAULT_ERA_PERIOD) },
|
||||
&dest,
|
||||
10_000
|
||||
).await?;
|
||||
let raw = sub.next().await.unwrap().unwrap();
|
||||
let event = TransferEvent::<DefaultNodeRuntime>::decode(&mut &raw.data[..]);
|
||||
if let Ok(e) = event {
|
||||
|
||||
@@ -64,6 +64,7 @@ pub fn call(s: Structure) -> TokenStream {
|
||||
fn #call<'a>(
|
||||
&'a self,
|
||||
signer: &'a (dyn #subxt::Signer<T> + Send + Sync),
|
||||
opts: #subxt::SignedOptions,
|
||||
#args
|
||||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<T::Hash, #subxt::Error>> + Send + 'a>>;
|
||||
|
||||
@@ -71,6 +72,7 @@ pub fn call(s: Structure) -> TokenStream {
|
||||
fn #call_and_watch<'a>(
|
||||
&'a self,
|
||||
signer: &'a (dyn #subxt::Signer<T> + Send + Sync),
|
||||
opts: #subxt::SignedOptions,
|
||||
#args
|
||||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<#subxt::ExtrinsicSuccess<T>, #subxt::Error>> + Send + 'a>>;
|
||||
}
|
||||
@@ -82,19 +84,21 @@ pub fn call(s: Structure) -> TokenStream {
|
||||
fn #call<'a>(
|
||||
&'a self,
|
||||
signer: &'a (dyn #subxt::Signer<T> + Send + Sync),
|
||||
opts: #subxt::SignedOptions,
|
||||
#args
|
||||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<T::Hash, #subxt::Error>> + Send + 'a>> {
|
||||
let #marker = core::marker::PhantomData::<T>;
|
||||
Box::pin(self.submit(#build_struct, signer))
|
||||
Box::pin(self.submit(#build_struct, signer, opts))
|
||||
}
|
||||
|
||||
fn #call_and_watch<'a>(
|
||||
&'a self,
|
||||
signer: &'a (dyn #subxt::Signer<T> + Send + Sync),
|
||||
opts: #subxt::SignedOptions,
|
||||
#args
|
||||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<#subxt::ExtrinsicSuccess<T>, #subxt::Error>> + Send + 'a>> {
|
||||
let #marker = core::marker::PhantomData::<T>;
|
||||
Box::pin(self.watch(#build_struct, signer))
|
||||
Box::pin(self.watch(#build_struct, signer, opts))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,14 +138,13 @@ where
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is modified from the substrate version to allow passing in of the genesis hash, which is
|
||||
/// returned via `additional_signed()`. It assumes therefore `Era::Immortal` (The transaction is
|
||||
/// valid forever)
|
||||
/// This is modified from the substrate version to allow passing in a hash (either the genesis hash
|
||||
/// if immortal or current hash if mortal. The hash is returned via `additional_signed()`.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
|
||||
pub struct CheckEra<T: System>(
|
||||
/// The default structure for the Extra encoding
|
||||
pub (Era, PhantomData<T>),
|
||||
/// Local genesis hash to be used for `AdditionalSigned`
|
||||
/// Local hash to be used for `AdditionalSigned`
|
||||
#[codec(skip)]
|
||||
pub T::Hash,
|
||||
);
|
||||
@@ -238,6 +237,7 @@ pub trait SignedExtra<T: System>: SignedExtension {
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
era_info: (Era, T::Hash)
|
||||
) -> Self;
|
||||
|
||||
/// Returns the transaction extra.
|
||||
@@ -251,6 +251,8 @@ pub struct DefaultExtra<T: System> {
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
// Era and either the genesis_hash if immortal or the current hash if mortal
|
||||
era_info: (Era, T::Hash)
|
||||
}
|
||||
|
||||
impl<T: System + Balances + Clone + Debug + Eq + Send + Sync> SignedExtra<T>
|
||||
@@ -271,12 +273,14 @@ impl<T: System + Balances + Clone + Debug + Eq + Send + Sync> SignedExtra<T>
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
era_info: (Era, T::Hash)
|
||||
) -> Self {
|
||||
DefaultExtra {
|
||||
spec_version,
|
||||
tx_version,
|
||||
nonce,
|
||||
genesis_hash,
|
||||
era_info,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +289,7 @@ impl<T: System + Balances + Clone + Debug + Eq + Send + Sync> SignedExtra<T>
|
||||
CheckSpecVersion(PhantomData, self.spec_version),
|
||||
CheckTxVersion(PhantomData, self.tx_version),
|
||||
CheckGenesis(PhantomData, self.genesis_hash),
|
||||
CheckEra((Era::Immortal, PhantomData), self.genesis_hash),
|
||||
CheckEra((self.era_info.0, PhantomData), self.era_info.1),
|
||||
CheckNonce(self.nonce),
|
||||
CheckWeight(PhantomData),
|
||||
ChargeTransactionPayment(<T as Balances>::Balance::default()),
|
||||
|
||||
+16
-2
@@ -31,7 +31,10 @@ pub use self::{
|
||||
},
|
||||
};
|
||||
|
||||
use sp_runtime::traits::SignedExtension;
|
||||
use sp_runtime::{
|
||||
traits::SignedExtension,
|
||||
generic::Era
|
||||
};
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
use crate::{
|
||||
@@ -41,6 +44,10 @@ use crate::{
|
||||
Error,
|
||||
};
|
||||
|
||||
/// A reasonable default for `era_period`
|
||||
pub const DEFAULT_ERA_PERIOD: u64 = 64;
|
||||
|
||||
|
||||
/// UncheckedExtrinsic type.
|
||||
pub type UncheckedExtrinsic<T> = sp_runtime::generic::UncheckedExtrinsic<
|
||||
<T as System>::Address,
|
||||
@@ -59,6 +66,7 @@ pub async fn create_signed<T>(
|
||||
nonce: T::Index,
|
||||
call: Encoded,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
era_opts: Option<(u64, u64, T::Hash)>,
|
||||
) -> Result<UncheckedExtrinsic<T>, Error>
|
||||
where
|
||||
T: Runtime,
|
||||
@@ -67,7 +75,13 @@ where
|
||||
{
|
||||
let spec_version = runtime_version.spec_version;
|
||||
let tx_version = runtime_version.transaction_version;
|
||||
let extra: T::Extra = T::Extra::new(spec_version, tx_version, nonce, genesis_hash);
|
||||
let era_info = match era_opts {
|
||||
Some((period, cur_num, cur_hash)) => {
|
||||
(Era::mortal(period, cur_num), cur_hash)
|
||||
},
|
||||
None => (Era::Immortal, genesis_hash)
|
||||
};
|
||||
let extra: T::Extra = T::Extra::new(spec_version, tx_version, nonce, genesis_hash, era_info);
|
||||
let payload = SignedPayload::<T>::new(call, extra.extra())?;
|
||||
let signed = signer.sign(payload).await?;
|
||||
Ok(signed)
|
||||
|
||||
+55
-2
@@ -63,6 +63,12 @@ use sp_core::{
|
||||
pub use sp_runtime::traits::SignedExtension;
|
||||
pub use sp_version::RuntimeVersion;
|
||||
use std::marker::PhantomData;
|
||||
use sp_runtime::{
|
||||
traits::{Block, Header},
|
||||
SaturatedConversion
|
||||
};
|
||||
use quote::{TokenStreamExt, quote};
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
mod error;
|
||||
mod events;
|
||||
@@ -84,6 +90,7 @@ pub use crate::{
|
||||
SignedExtra,
|
||||
Signer,
|
||||
UncheckedExtrinsic,
|
||||
DEFAULT_ERA_PERIOD
|
||||
},
|
||||
frame::*,
|
||||
metadata::{
|
||||
@@ -193,6 +200,37 @@ pub struct Client<T: Runtime> {
|
||||
page_size: u32,
|
||||
}
|
||||
|
||||
/// Construction options for a signed extrinsic
|
||||
// TODO: tip can go in here https://github.com/paritytech/substrate-subxt/issues/187
|
||||
#[derive(Clone)]
|
||||
pub struct SignedOptions {
|
||||
/// The period, measured in blocks, that transaction will live for, starting from a checkpoint
|
||||
/// block. A good default is 64 (64 * 6secs = 6min 40sec).
|
||||
///
|
||||
/// `era_period == None`: immortal transaction.
|
||||
/// `0 <= era_period <= 65536`: rounded up to the closest power of 2, starting at 4.
|
||||
/// `65536 < era_period`: 65536.
|
||||
// pub era_period: Option<u64>,
|
||||
pub era_period: Option<u64>,
|
||||
}
|
||||
// https://github.com/dtolnay/quote/issues/129#issue-481909264
|
||||
fn options_to_tokens<T : quote::ToTokens>(input: &core::option::Option<T>) -> TokenStream {
|
||||
match input {
|
||||
Some(value) => quote!(core::option::Option::Some(#value)),
|
||||
None => quote!(core::option::Option::None)
|
||||
}
|
||||
}
|
||||
impl quote::ToTokens for self::SignedOptions {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let era_period = options_to_tokens(&self.era_period);
|
||||
tokens.append_all(quote!(
|
||||
SignedOptions {
|
||||
era_period: #era_period
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Runtime> Clone for Client<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@@ -444,6 +482,7 @@ impl<T: Runtime> Client<T> {
|
||||
&self,
|
||||
call: C,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
opts: SignedOptions,
|
||||
) -> Result<UncheckedExtrinsic<T>, Error>
|
||||
where
|
||||
<<T::Extra as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
@@ -455,12 +494,24 @@ impl<T: Runtime> Client<T> {
|
||||
self.account(signer.account_id(), None).await?.nonce
|
||||
};
|
||||
let call = self.encode(call)?;
|
||||
let era_opts = if opts.era_period.is_some() {
|
||||
let era_period = opts.era_period.unwrap();
|
||||
let current_block = self.block(None::<T::Hash>).await?.unwrap().block;
|
||||
let current_number = (*current_block.header().number()).saturated_into::<u64>();
|
||||
let current_hash = current_block.hash();
|
||||
|
||||
Some((era_period, current_number, current_hash))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let signed = extrinsic::create_signed(
|
||||
&self.runtime_version,
|
||||
self.genesis_hash,
|
||||
account_nonce,
|
||||
call,
|
||||
signer,
|
||||
era_opts,
|
||||
)
|
||||
.await?;
|
||||
Ok(signed)
|
||||
@@ -498,12 +549,13 @@ impl<T: Runtime> Client<T> {
|
||||
&self,
|
||||
call: C,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
opts: SignedOptions,
|
||||
) -> Result<T::Hash, Error>
|
||||
where
|
||||
<<T::Extra as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
Send + Sync,
|
||||
{
|
||||
let extrinsic = self.create_signed(call, signer).await?;
|
||||
let extrinsic = self.create_signed(call, signer, opts).await?;
|
||||
self.submit_extrinsic(extrinsic).await
|
||||
}
|
||||
|
||||
@@ -512,12 +564,13 @@ impl<T: Runtime> Client<T> {
|
||||
&self,
|
||||
call: C,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
opts: SignedOptions,
|
||||
) -> Result<ExtrinsicSuccess<T>, Error>
|
||||
where
|
||||
<<T::Extra as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
Send + Sync,
|
||||
{
|
||||
let extrinsic = self.create_signed(call, signer).await?;
|
||||
let extrinsic = self.create_signed(call, signer, opts).await?;
|
||||
let decoder = self.events_decoder::<C>();
|
||||
self.submit_and_watch_extrinsic(extrinsic, decoder).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user