mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 07:11:06 +00:00
Simplify creating and signing extrinsics (#490)
* WIP extrinsic api updates * First pass done; now to get things compiling again * document and tweak new structs/traits * cargo check --all-targets now compiles without issue * Polkadot and Substrate take different extra params; support both * Fix transaction format (missing byte from AccountId -> Address) and fmt * Tweak Signer trait * Tweak comments and such in extrinsic params * check all examples against newer polkadot, add new one with params, import path tweaks * clippy fix, and save an allocation when signing * Remove unnecessary Default clauses * Tidy up and fix comments. Panic if payload size >4GB * fix typo
This commit is contained in:
@@ -38,13 +38,13 @@ resides ([`CARGO_MANIFEST_DIR`](https://doc.rust-lang.org/cargo/reference/enviro
|
||||
### Initializing the API client
|
||||
|
||||
```rust
|
||||
use subxt::{ClientBuilder, DefaultConfig, DefaultExtra};
|
||||
use subxt::{ClientBuilder, DefaultConfig, SubstrateExtrinsicParams};
|
||||
|
||||
let api = ClientBuilder::new()
|
||||
.set_url("wss://rpc.polkadot.io:443")
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<node_runtime::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<node_runtime::RuntimeApi<DefaultConfig, SubstrateExtrinsicParams<DefaultConfig>>>();
|
||||
```
|
||||
|
||||
The `RuntimeApi` type is generated by the `subxt` macro from the supplied metadata. This can be parameterized with user
|
||||
@@ -53,7 +53,7 @@ chain.
|
||||
|
||||
### Querying Storage
|
||||
|
||||
Call the generated `RuntimeApi::storage()` method, followed by the `pallet_name()` and then the `storage_item_name()`.
|
||||
Call the generated `RuntimeApi::storage()` method, followed by the `pallet_name()` and then the `storage_item_name()`.
|
||||
|
||||
So in order to query `Balances::TotalIssuance`:
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ pub fn generate_calls(
|
||||
impl<'a, T, X> TransactionApi<'a, T, X>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::extrinsic::ExtrinsicParams<T>,
|
||||
{
|
||||
pub fn new(client: &'a ::subxt::Client<T>) -> Self {
|
||||
Self { client, marker: ::core::marker::PhantomData }
|
||||
|
||||
@@ -288,7 +288,7 @@ impl RuntimeGenerator {
|
||||
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>
|
||||
X: ::subxt::extrinsic::ExtrinsicParams<T>
|
||||
{
|
||||
fn from(client: ::subxt::Client<T>) -> Self {
|
||||
Self { client, marker: ::core::marker::PhantomData }
|
||||
@@ -298,7 +298,7 @@ impl RuntimeGenerator {
|
||||
impl<'a, T, X> RuntimeApi<T, X>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::extrinsic::ExtrinsicParams<T>,
|
||||
{
|
||||
pub fn constants(&'a self) -> ConstantsApi {
|
||||
ConstantsApi
|
||||
@@ -368,7 +368,7 @@ impl RuntimeGenerator {
|
||||
impl<'a, T, X> TransactionApi<'a, T, X>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::extrinsic::ExtrinsicParams<T>,
|
||||
{
|
||||
#(
|
||||
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X> {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -26,8 +26,8 @@ use sp_keyring::AccountKeyring;
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -43,12 +43,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
let hash = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(dest, 10_000)
|
||||
.sign_and_submit(&signer)
|
||||
.transfer(dest, 123_456_789_012_345)
|
||||
.sign_and_submit_default(&signer)
|
||||
.await?;
|
||||
|
||||
println!("Balance transfer extrinsic submitted: {}", hash);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2019-2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
|
||||
//! polkadot --dev --tmp
|
||||
//! ```
|
||||
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::{
|
||||
extrinsic::{
|
||||
Era,
|
||||
PlainTip,
|
||||
},
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
PolkadotExtrinsicParamsBuilder as Params,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
pub mod polkadot {}
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
// Configure the transaction tip and era:
|
||||
let tx_params = Params::new()
|
||||
.tip(PlainTip::new(20_000_000_000))
|
||||
.era(Era::Immortal, *api.client.genesis());
|
||||
|
||||
// Send the transaction:
|
||||
let hash = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(dest, 123_456_789_012_345)
|
||||
.sign_and_submit(&signer, tx_params)
|
||||
.await?;
|
||||
|
||||
println!("Balance transfer extrinsic submitted: {}", hash);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -27,8 +27,8 @@ use subxt::{
|
||||
ClientBuilder,
|
||||
Config,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -60,7 +60,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<MyConfig, DefaultExtra<MyConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<MyConfig, PolkadotExtrinsicParams<MyConfig>>>();
|
||||
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
@@ -69,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(dest, 10_000)
|
||||
.sign_and_submit(&signer)
|
||||
.sign_and_submit_default(&signer)
|
||||
.await?;
|
||||
|
||||
println!("Balance transfer extrinsic submitted: {}", hash);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
|
||||
#![allow(clippy::redundant_clone)]
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -25,7 +25,7 @@
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -38,7 +38,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
let mut iter = api.storage().system().account_iter(None).await?;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ use subxt::{
|
||||
sp_runtime::AccountId32,
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -44,7 +44,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
let era = api.storage().staking().active_era(None).await?.unwrap();
|
||||
println!(
|
||||
|
||||
Binary file not shown.
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -25,7 +25,7 @@
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -39,7 +39,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.set_url("wss://rpc.polkadot.io:443")
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
let block_number = 1u32;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -27,8 +27,8 @@ use sp_keyring::AccountKeyring;
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -55,13 +55,13 @@ async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<_>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<_>>>();
|
||||
|
||||
let balance_transfer = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(dest, 10_000)
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.sign_and_submit_then_watch_default(&signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -87,13 +87,13 @@ async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Err
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<_>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<_>>>();
|
||||
|
||||
let balance_transfer = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(dest, 10_000)
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.sign_and_submit_then_watch_default(&signer)
|
||||
.await?
|
||||
.wait_for_finalized()
|
||||
.await?;
|
||||
@@ -138,13 +138,13 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<_>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<_>>>();
|
||||
|
||||
let mut balance_transfer_progress = api
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(dest, 10_000)
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.sign_and_submit_then_watch_default(&signer)
|
||||
.await?;
|
||||
|
||||
while let Some(ev) = balance_transfer_progress.next().await {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -28,8 +28,8 @@ use std::time::Duration;
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -44,7 +44,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
// Subscribe to any events that occur:
|
||||
let mut event_sub = api.events().subscribe().await?;
|
||||
@@ -52,11 +52,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// While this subscription is active, balance transfers are made somewhere:
|
||||
async_std::task::spawn(async {
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
let api =
|
||||
ClientBuilder::new()
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<polkadot::RuntimeApi<
|
||||
DefaultConfig,
|
||||
PolkadotExtrinsicParams<DefaultConfig>,
|
||||
>>();
|
||||
|
||||
let mut transfer_amount = 1_000_000_000;
|
||||
|
||||
@@ -65,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
api.tx()
|
||||
.balances()
|
||||
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount)
|
||||
.sign_and_submit(&signer)
|
||||
.sign_and_submit_default(&signer)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -28,8 +28,8 @@ use std::time::Duration;
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -45,7 +45,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
// Subscribe to just balance transfer events, making use of `filter_events`
|
||||
// to select a single event type (note the 1-tuple) to filter out and return.
|
||||
@@ -58,18 +58,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// While this subscription is active, we imagine some balance transfers are made somewhere else:
|
||||
async_std::task::spawn(async {
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
let api =
|
||||
ClientBuilder::new()
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<polkadot::RuntimeApi<
|
||||
DefaultConfig,
|
||||
PolkadotExtrinsicParams<DefaultConfig>,
|
||||
>>();
|
||||
|
||||
// Make small balance transfers from Alice to Bob in a loop:
|
||||
loop {
|
||||
api.tx()
|
||||
.balances()
|
||||
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
|
||||
.sign_and_submit(&signer)
|
||||
.sign_and_submit_default(&signer)
|
||||
.await
|
||||
.unwrap();
|
||||
async_std::task::sleep(Duration::from_secs(10)).await;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
|
||||
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-f6d6ab005d-aarch64-macos.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```bash
|
||||
@@ -28,8 +28,8 @@ use std::time::Duration;
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
DefaultExtra,
|
||||
PairSigner,
|
||||
PolkadotExtrinsicParams,
|
||||
};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
|
||||
@@ -45,7 +45,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await?
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
|
||||
|
||||
// Subscribe to several balance related events. If we ask for more than one event,
|
||||
// we'll be given a correpsonding tuple of `Option`'s, with exactly one
|
||||
@@ -59,18 +59,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// While this subscription is active, we imagine some balance transfers are made somewhere else:
|
||||
async_std::task::spawn(async {
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let api = ClientBuilder::new()
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
|
||||
let api =
|
||||
ClientBuilder::new()
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<polkadot::RuntimeApi<
|
||||
DefaultConfig,
|
||||
PolkadotExtrinsicParams<DefaultConfig>,
|
||||
>>();
|
||||
|
||||
// Make small balance transfers from Alice to Bob in a loop:
|
||||
loop {
|
||||
api.tx()
|
||||
.balances()
|
||||
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
|
||||
.sign_and_submit(&signer)
|
||||
.sign_and_submit_default(&signer)
|
||||
.await
|
||||
.unwrap();
|
||||
async_std::task::sleep(Duration::from_secs(10)).await;
|
||||
|
||||
+119
-41
@@ -24,10 +24,8 @@ use crate::{
|
||||
HasModuleError,
|
||||
},
|
||||
extrinsic::{
|
||||
self,
|
||||
SignedExtra,
|
||||
ExtrinsicParams,
|
||||
Signer,
|
||||
UncheckedExtrinsic,
|
||||
},
|
||||
rpc::{
|
||||
Rpc,
|
||||
@@ -39,9 +37,14 @@ use crate::{
|
||||
transaction::TransactionProgress,
|
||||
Call,
|
||||
Config,
|
||||
Encoded,
|
||||
Metadata,
|
||||
};
|
||||
use codec::Decode;
|
||||
use codec::{
|
||||
Compact,
|
||||
Decode,
|
||||
Encode,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -188,7 +191,7 @@ pub struct SubmittableExtrinsic<'client, T: Config, X, C, E: Decode, Evs: Decode
|
||||
impl<'client, T, X, C, E, Evs> SubmittableExtrinsic<'client, T, X, C, E, Evs>
|
||||
where
|
||||
T: Config,
|
||||
X: SignedExtra<T>,
|
||||
X: ExtrinsicParams<T>,
|
||||
C: Call + Send + Sync,
|
||||
E: Decode + HasModuleError,
|
||||
Evs: Decode,
|
||||
@@ -202,20 +205,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and signs an extrinsic and submits it to the chain. Passes default parameters
|
||||
/// to construct the "signed extra" and "additional" payloads needed by the extrinsic.
|
||||
///
|
||||
/// Returns a [`TransactionProgress`], which can be used to track the status of the transaction
|
||||
/// and obtain details about it, once it has made it into a block.
|
||||
pub async fn sign_and_submit_then_watch_default(
|
||||
self,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
) -> Result<TransactionProgress<'client, T, E, Evs>, BasicError>
|
||||
where
|
||||
X::OtherParams: Default,
|
||||
{
|
||||
self.sign_and_submit_then_watch(signer, Default::default())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Creates and signs an extrinsic and submits it to the chain.
|
||||
///
|
||||
/// Returns a [`TransactionProgress`], which can be used to track the status of the transaction
|
||||
/// and obtain details about it, once it has made it into a block.
|
||||
pub async fn sign_and_submit_then_watch(
|
||||
self,
|
||||
signer: &(dyn Signer<T, X> + Send + Sync),
|
||||
) -> Result<TransactionProgress<'client, T, E, Evs>, BasicError>
|
||||
where
|
||||
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
Send + Sync + 'static,
|
||||
{
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
other_params: X::OtherParams,
|
||||
) -> Result<TransactionProgress<'client, T, E, Evs>, BasicError> {
|
||||
// Sign the call data to create our extrinsic.
|
||||
let extrinsic = self.create_signed(signer, Default::default()).await?;
|
||||
let extrinsic = self.create_signed(signer, other_params).await?;
|
||||
|
||||
// Get a hash of the extrinsic (we'll need this later).
|
||||
let ext_hash = T::Hashing::hash_of(&extrinsic);
|
||||
@@ -226,6 +242,26 @@ where
|
||||
Ok(TransactionProgress::new(sub, self.client, ext_hash))
|
||||
}
|
||||
|
||||
/// Creates and signs an extrinsic and submits to the chain for block inclusion. Passes
|
||||
/// default parameters to construct the "signed extra" and "additional" payloads needed
|
||||
/// by the extrinsic.
|
||||
///
|
||||
/// Returns `Ok` with the extrinsic hash if it is valid extrinsic.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Success does not mean the extrinsic has been included in the block, just that it is valid
|
||||
/// and has been included in the transaction pool.
|
||||
pub async fn sign_and_submit_default(
|
||||
self,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
) -> Result<T::Hash, BasicError>
|
||||
where
|
||||
X::OtherParams: Default,
|
||||
{
|
||||
self.sign_and_submit(signer, Default::default()).await
|
||||
}
|
||||
|
||||
/// Creates and signs an extrinsic and submits to the chain for block inclusion.
|
||||
///
|
||||
/// Returns `Ok` with the extrinsic hash if it is valid extrinsic.
|
||||
@@ -236,26 +272,20 @@ where
|
||||
/// and has been included in the transaction pool.
|
||||
pub async fn sign_and_submit(
|
||||
self,
|
||||
signer: &(dyn Signer<T, X> + Send + Sync),
|
||||
) -> Result<T::Hash, BasicError>
|
||||
where
|
||||
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
Send + Sync + 'static,
|
||||
{
|
||||
let extrinsic = self.create_signed(signer, Default::default()).await?;
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
other_params: X::OtherParams,
|
||||
) -> Result<T::Hash, BasicError> {
|
||||
let extrinsic = self.create_signed(signer, other_params).await?;
|
||||
self.client.rpc().submit_extrinsic(extrinsic).await
|
||||
}
|
||||
|
||||
/// Creates a signed extrinsic.
|
||||
/// Creates a returns a raw signed extrinsic, without submitting it.
|
||||
pub async fn create_signed(
|
||||
&self,
|
||||
signer: &(dyn Signer<T, X> + Send + Sync),
|
||||
additional_params: X::Parameters,
|
||||
) -> Result<UncheckedExtrinsic<T, X>, BasicError>
|
||||
where
|
||||
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
Send + Sync + 'static,
|
||||
{
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
other_params: X::OtherParams,
|
||||
) -> Result<Encoded, BasicError> {
|
||||
// 1. Get nonce
|
||||
let account_nonce = if let Some(nonce) = signer.nonce() {
|
||||
nonce
|
||||
} else {
|
||||
@@ -264,21 +294,69 @@ where
|
||||
.system_account_next_index(signer.account_id())
|
||||
.await?
|
||||
};
|
||||
let call = self
|
||||
.client
|
||||
.metadata()
|
||||
.pallet(C::PALLET)
|
||||
.and_then(|pallet| pallet.encode_call(&self.call))?;
|
||||
|
||||
let signed = extrinsic::create_signed(
|
||||
&self.client.runtime_version,
|
||||
self.client.genesis_hash,
|
||||
// 2. SCALE encode call data to bytes (pallet u8, call u8, call params).
|
||||
let call_data = {
|
||||
let mut bytes = Vec::new();
|
||||
let pallet = self.client.metadata().pallet(C::PALLET)?;
|
||||
bytes.push(pallet.index());
|
||||
bytes.push(pallet.call_index::<C>()?);
|
||||
self.call.encode_to(&mut bytes);
|
||||
Encoded(bytes)
|
||||
};
|
||||
|
||||
// 3. Construct our custom additional/extra params.
|
||||
let additional_and_extra_params = X::new(
|
||||
self.client.runtime_version.spec_version,
|
||||
self.client.runtime_version.transaction_version,
|
||||
account_nonce,
|
||||
call,
|
||||
signer,
|
||||
additional_params,
|
||||
)
|
||||
.await?;
|
||||
Ok(signed)
|
||||
self.client.genesis_hash,
|
||||
other_params,
|
||||
);
|
||||
|
||||
// 4. Construct signature. This is compatible with the Encode impl
|
||||
// for SignedPayload (which is this payload of bytes that we'd like)
|
||||
// to sign. See:
|
||||
// https://github.com/paritytech/substrate/blob/9a6d706d8db00abb6ba183839ec98ecd9924b1f8/primitives/runtime/src/generic/unchecked_extrinsic.rs#L215)
|
||||
let signature = {
|
||||
let mut bytes = Vec::new();
|
||||
call_data.encode_to(&mut bytes);
|
||||
additional_and_extra_params.encode_extra_to(&mut bytes);
|
||||
additional_and_extra_params.encode_additional_to(&mut bytes);
|
||||
if bytes.len() > 256 {
|
||||
signer.sign(&sp_core::blake2_256(&bytes))
|
||||
} else {
|
||||
signer.sign(&bytes)
|
||||
}
|
||||
};
|
||||
|
||||
// 5. Encode extrinsic, now that we have the parts we need. This is compatible
|
||||
// with the Encode impl for UncheckedExtrinsic (protocol version 4).
|
||||
let extrinsic = {
|
||||
let mut encoded_inner = Vec::new();
|
||||
// "is signed" + transaction protocol version (4)
|
||||
(0b10000000 + 4u8).encode_to(&mut encoded_inner);
|
||||
// from address for signature
|
||||
signer.address().encode_to(&mut encoded_inner);
|
||||
// the signature bytes
|
||||
signature.encode_to(&mut encoded_inner);
|
||||
// attach custom extra params
|
||||
additional_and_extra_params.encode_extra_to(&mut encoded_inner);
|
||||
// and now, call data
|
||||
call_data.encode_to(&mut encoded_inner);
|
||||
// now, prefix byte length:
|
||||
let len = Compact(
|
||||
u32::try_from(encoded_inner.len())
|
||||
.expect("extrinsic size expected to be <4GB"),
|
||||
);
|
||||
let mut encoded = Vec::new();
|
||||
len.encode_to(&mut encoded);
|
||||
encoded.extend(encoded_inner);
|
||||
encoded
|
||||
};
|
||||
|
||||
// Wrap in Encoded to ensure that any more "encode" calls leave it in the right state.
|
||||
// maybe we can just return the raw bytes..
|
||||
Ok(Encoded(extrinsic))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@ pub trait Config: 'static {
|
||||
+ Default
|
||||
+ AtLeast32Bit
|
||||
+ Copy
|
||||
+ scale_info::TypeInfo;
|
||||
+ scale_info::TypeInfo
|
||||
+ Into<u64>;
|
||||
|
||||
/// The block number type used by the runtime.
|
||||
type BlockNumber: Parameter
|
||||
|
||||
@@ -1,481 +0,0 @@
|
||||
// Copyright 2019-2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::PhantomDataSendSync;
|
||||
use codec::{
|
||||
Decode,
|
||||
Encode,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{
|
||||
generic::Era,
|
||||
traits::{
|
||||
DispatchInfoOf,
|
||||
SignedExtension,
|
||||
},
|
||||
transaction_validity::TransactionValidityError,
|
||||
};
|
||||
|
||||
use crate::Config;
|
||||
|
||||
/// Extra type.
|
||||
// pub type Extra<T> = <<T as Config>::Extra as SignedExtra<T>>::Extra;
|
||||
|
||||
/// SignedExtra checks copied from substrate, in order to remove requirement to implement
|
||||
/// substrate's `frame_system::Trait`
|
||||
|
||||
/// Ensure the runtime version registered in the transaction is the same as at present.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is modified from the substrate version to allow passing in of the version, which is
|
||||
/// returned via `additional_signed()`.
|
||||
|
||||
/// Ensure the runtime version registered in the transaction is the same as at present.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckSpecVersion<T: Config>(
|
||||
pub PhantomDataSendSync<T>,
|
||||
/// Local version to be used for `AdditionalSigned`
|
||||
#[codec(skip)]
|
||||
pub u32,
|
||||
);
|
||||
|
||||
impl<T: Config> SignedExtension for CheckSpecVersion<T> {
|
||||
const IDENTIFIER: &'static str = "CheckSpecVersion";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = u32;
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(self.1)
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure the transaction version registered in the transaction is the same as at present.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is modified from the substrate version to allow passing in of the version, which is
|
||||
/// returned via `additional_signed()`.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckTxVersion<T: Config>(
|
||||
pub PhantomDataSendSync<T>,
|
||||
/// Local version to be used for `AdditionalSigned`
|
||||
#[codec(skip)]
|
||||
pub u32,
|
||||
);
|
||||
|
||||
impl<T: Config> SignedExtension for CheckTxVersion<T> {
|
||||
const IDENTIFIER: &'static str = "CheckTxVersion";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = u32;
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(self.1)
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Check genesis hash
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is modified from the substrate version to allow passing in of the genesis hash, which is
|
||||
/// returned via `additional_signed()`.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckGenesis<T: Config>(
|
||||
pub PhantomDataSendSync<T>,
|
||||
/// Local genesis hash to be used for `AdditionalSigned`
|
||||
#[codec(skip)]
|
||||
pub T::Hash,
|
||||
);
|
||||
|
||||
impl<T: Config> SignedExtension for CheckGenesis<T> {
|
||||
const IDENTIFIER: &'static str = "CheckGenesis";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = T::Hash;
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(self.1)
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for transaction mortality.
|
||||
///
|
||||
/// # 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)
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckMortality<T: Config>(
|
||||
/// The default structure for the Extra encoding
|
||||
pub (Era, PhantomDataSendSync<T>),
|
||||
/// Local genesis hash to be used for `AdditionalSigned`
|
||||
#[codec(skip)]
|
||||
pub T::Hash,
|
||||
);
|
||||
|
||||
impl<T: Config> SignedExtension for CheckMortality<T> {
|
||||
const IDENTIFIER: &'static str = "CheckMortality";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = T::Hash;
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(self.1)
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Nonce check and increment to give replay protection for transactions.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckNonce<T: Config>(#[codec(compact)] pub T::Index);
|
||||
|
||||
impl<T: Config> SignedExtension for CheckNonce<T> {
|
||||
const IDENTIFIER: &'static str = "CheckNonce";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource limit check.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckWeight<T: Config>(pub PhantomDataSendSync<T>);
|
||||
|
||||
impl<T: Config> SignedExtension for CheckWeight<T> {
|
||||
const IDENTIFIER: &'static str = "CheckWeight";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
|
||||
/// in the queue.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = ""),
|
||||
Default(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct ChargeTransactionPayment<T: Config>(
|
||||
#[codec(compact)] u128,
|
||||
pub PhantomDataSendSync<T>,
|
||||
);
|
||||
|
||||
impl<T: Config> SignedExtension for ChargeTransactionPayment<T> {
|
||||
const IDENTIFIER: &'static str = "ChargeTransactionPayment";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
|
||||
/// in the queue.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = ""),
|
||||
Default(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct ChargeAssetTxPayment<T: Config> {
|
||||
/// The tip for the block author.
|
||||
#[codec(compact)]
|
||||
pub tip: u128,
|
||||
/// The asset with which to pay the tip.
|
||||
pub asset_id: Option<u32>,
|
||||
/// Marker for unused type parameter.
|
||||
pub marker: PhantomDataSendSync<T>,
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension for ChargeAssetTxPayment<T> {
|
||||
const IDENTIFIER: &'static str = "ChargeAssetTxPayment";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for implementing transaction extras for a runtime.
|
||||
pub trait SignedExtra<T: Config>: SignedExtension {
|
||||
/// The type the extras.
|
||||
type Extra: SignedExtension + Send + Sync;
|
||||
/// The additional config parameters.
|
||||
type Parameters: Default + Send + Sync;
|
||||
|
||||
/// Creates a new `SignedExtra`.
|
||||
fn new(
|
||||
spec_version: u32,
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
additional_params: Self::Parameters,
|
||||
) -> Self;
|
||||
|
||||
/// Returns the transaction extra.
|
||||
fn extra(&self) -> Self::Extra;
|
||||
}
|
||||
|
||||
/// Default `SignedExtra` for substrate runtimes.
|
||||
#[derive(Derivative, Encode, Decode, TypeInfo)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Debug(bound = ""),
|
||||
Eq(bound = "")
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct DefaultExtraWithTxPayment<T: Config, X> {
|
||||
spec_version: u32,
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
marker: PhantomDataSendSync<X>,
|
||||
}
|
||||
|
||||
impl<T, X> SignedExtra<T> for DefaultExtraWithTxPayment<T, X>
|
||||
where
|
||||
T: Config,
|
||||
X: SignedExtension<AccountId = T::AccountId, Call = ()> + Default,
|
||||
{
|
||||
type Extra = (
|
||||
CheckSpecVersion<T>,
|
||||
CheckTxVersion<T>,
|
||||
CheckGenesis<T>,
|
||||
CheckMortality<T>,
|
||||
CheckNonce<T>,
|
||||
CheckWeight<T>,
|
||||
X,
|
||||
);
|
||||
type Parameters = ();
|
||||
|
||||
fn new(
|
||||
spec_version: u32,
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
_params: Self::Parameters,
|
||||
) -> Self {
|
||||
DefaultExtraWithTxPayment {
|
||||
spec_version,
|
||||
tx_version,
|
||||
nonce,
|
||||
genesis_hash,
|
||||
marker: PhantomDataSendSync::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extra(&self) -> Self::Extra {
|
||||
(
|
||||
CheckSpecVersion(PhantomDataSendSync::new(), self.spec_version),
|
||||
CheckTxVersion(PhantomDataSendSync::new(), self.tx_version),
|
||||
CheckGenesis(PhantomDataSendSync::new(), self.genesis_hash),
|
||||
CheckMortality(
|
||||
(Era::Immortal, PhantomDataSendSync::new()),
|
||||
self.genesis_hash,
|
||||
),
|
||||
CheckNonce(self.nonce),
|
||||
CheckWeight(PhantomDataSendSync::new()),
|
||||
X::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, X: SignedExtension<AccountId = T::AccountId, Call = ()> + Default> SignedExtension
|
||||
for DefaultExtraWithTxPayment<T, X>
|
||||
where
|
||||
T: Config,
|
||||
X: SignedExtension,
|
||||
{
|
||||
const IDENTIFIER: &'static str = "DefaultExtra";
|
||||
type AccountId = T::AccountId;
|
||||
type Call = ();
|
||||
type AdditionalSigned =
|
||||
<<Self as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned;
|
||||
type Pre = ();
|
||||
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
|
||||
self.extra().additional_signed()
|
||||
}
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A default `SignedExtra` configuration, with [`ChargeTransactionPayment`] for tipping.
|
||||
///
|
||||
/// Note that this must match the `SignedExtra` type in the target runtime's extrinsic definition.
|
||||
pub type DefaultExtra<T> = DefaultExtraWithTxPayment<T, ChargeTransactionPayment<T>>;
|
||||
+10
-62
@@ -16,74 +16,22 @@
|
||||
|
||||
//! Create signed or unsigned extrinsics.
|
||||
|
||||
mod extra;
|
||||
mod params;
|
||||
mod signer;
|
||||
|
||||
pub use self::{
|
||||
extra::{
|
||||
ChargeAssetTxPayment,
|
||||
ChargeTransactionPayment,
|
||||
CheckGenesis,
|
||||
CheckMortality,
|
||||
CheckNonce,
|
||||
CheckSpecVersion,
|
||||
CheckTxVersion,
|
||||
CheckWeight,
|
||||
DefaultExtra,
|
||||
DefaultExtraWithTxPayment,
|
||||
SignedExtra,
|
||||
params::{
|
||||
AssetTip,
|
||||
Era,
|
||||
ExtrinsicParams,
|
||||
PlainTip,
|
||||
PolkadotExtrinsicParams,
|
||||
PolkadotExtrinsicParamsBuilder,
|
||||
SubstrateExtrinsicParams,
|
||||
SubstrateExtrinsicParamsBuilder,
|
||||
},
|
||||
signer::{
|
||||
PairSigner,
|
||||
Signer,
|
||||
},
|
||||
};
|
||||
|
||||
use sp_runtime::traits::SignedExtension;
|
||||
|
||||
use crate::{
|
||||
error::BasicError,
|
||||
rpc::RuntimeVersion,
|
||||
Config,
|
||||
Encoded,
|
||||
};
|
||||
|
||||
/// UncheckedExtrinsic type.
|
||||
pub type UncheckedExtrinsic<T, X> = sp_runtime::generic::UncheckedExtrinsic<
|
||||
<T as Config>::Address,
|
||||
Encoded,
|
||||
<T as Config>::Signature,
|
||||
<X as SignedExtra<T>>::Extra,
|
||||
>;
|
||||
|
||||
/// SignedPayload type.
|
||||
pub type SignedPayload<T, X> =
|
||||
sp_runtime::generic::SignedPayload<Encoded, <X as SignedExtra<T>>::Extra>;
|
||||
|
||||
/// Creates a signed extrinsic
|
||||
pub async fn create_signed<T, X>(
|
||||
runtime_version: &RuntimeVersion,
|
||||
genesis_hash: T::Hash,
|
||||
nonce: T::Index,
|
||||
call: Encoded,
|
||||
signer: &(dyn Signer<T, X> + Send + Sync),
|
||||
additional_params: X::Parameters,
|
||||
) -> Result<UncheckedExtrinsic<T, X>, BasicError>
|
||||
where
|
||||
T: Config,
|
||||
X: SignedExtra<T>,
|
||||
<X::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
|
||||
{
|
||||
let spec_version = runtime_version.spec_version;
|
||||
let tx_version = runtime_version.transaction_version;
|
||||
let extra = X::new(
|
||||
spec_version,
|
||||
tx_version,
|
||||
nonce,
|
||||
genesis_hash,
|
||||
additional_params,
|
||||
);
|
||||
let payload = SignedPayload::<T, X>::new(call, extra.extra())?;
|
||||
let signed = signer.sign(payload).await?;
|
||||
Ok(signed)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
// Copyright 2019-2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{
|
||||
Compact,
|
||||
Encode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Config,
|
||||
Encoded,
|
||||
};
|
||||
|
||||
// We require Era as a param below, so make it available from here.
|
||||
pub use sp_runtime::generic::Era;
|
||||
|
||||
/// This trait allows you to configure the "signed extra" and
|
||||
/// "additional" parameters that are signed and used in transactions.
|
||||
/// see [`BaseExtrinsicParams`] for an implementation that is compatible with
|
||||
/// a Polkadot node.
|
||||
pub trait ExtrinsicParams<T: Config> {
|
||||
/// These parameters can be provided to the constructor along with
|
||||
/// some default parameters that `subxt` understands, in order to
|
||||
/// help construct your [`ExtrinsicParams`] object.
|
||||
type OtherParams;
|
||||
|
||||
/// Construct a new instance of our [`ExtrinsicParams`]
|
||||
fn new(
|
||||
spec_version: u32,
|
||||
tx_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Self;
|
||||
|
||||
/// This is expected to SCALE encode the "signed extra" parameters
|
||||
/// to some buffer that has been provided. These are the parameters
|
||||
/// which are sent along with the transaction, as well as taken into
|
||||
/// account when signing the transaction.
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>);
|
||||
|
||||
/// This is expected to SCALE encode the "additional" parameters
|
||||
/// to some buffer that has been provided. These parameters are _not_
|
||||
/// sent along with the transaction, but are taken into account when
|
||||
/// signing it, meaning the client and node must agree on their values.
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>);
|
||||
}
|
||||
|
||||
/// A struct representing the signed extra and additional parameters required
|
||||
/// to construct a transaction for the default substrate node.
|
||||
pub type SubstrateExtrinsicParams<T> = BaseExtrinsicParams<T, AssetTip>;
|
||||
|
||||
/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
|
||||
/// This is what you provide to methods like `sign_and_submit()`.
|
||||
pub type SubstrateExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, AssetTip>;
|
||||
|
||||
/// A struct representing the signed extra and additional parameters required
|
||||
/// to construct a transaction for a polkadot node.
|
||||
pub type PolkadotExtrinsicParams<T> = BaseExtrinsicParams<T, PlainTip>;
|
||||
|
||||
/// A builder which leads to [`PolkadotExtrinsicParams`] being constructed.
|
||||
/// This is what you provide to methods like `sign_and_submit()`.
|
||||
pub type PolkadotExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, PlainTip>;
|
||||
|
||||
/// An implementation of [`ExtrinsicParams`] that is suitable for constructing
|
||||
/// extrinsics that can be sent to a node with the same signed extra and additional
|
||||
/// parameters as a Polkadot/Substrate node. The way that tip payments are specified
|
||||
/// differs between Substrate and Polkadot nodes, and so we are generic over that in
|
||||
/// order to support both here with relative ease.
|
||||
///
|
||||
/// If your node differs in the "signed extra" and "additional" parameters expected
|
||||
/// to be sent/signed with a transaction, then you can define your own type which
|
||||
/// implements the [`ExtrinsicParams`] trait.
|
||||
pub struct BaseExtrinsicParams<T: Config, Tip> {
|
||||
era: Era,
|
||||
nonce: T::Index,
|
||||
tip: Tip,
|
||||
spec_version: u32,
|
||||
transaction_version: u32,
|
||||
genesis_hash: T::Hash,
|
||||
mortality_checkpoint: T::Hash,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
/// This builder allows you to provide the parameters that can be configured in order to
|
||||
/// construct a [`BaseExtrinsicParams`] value. This implements [`Default`], which allows
|
||||
/// [`BaseExtrinsicParams`] to be used with convenience methods like `sign_and_submit_default()`.
|
||||
///
|
||||
/// Prefer to use [`SubstrateExtrinsicParamsBuilder`] for a version of this tailored towards
|
||||
/// Substrate, or [`PolkadotExtrinsicParamsBuilder`] for a version tailored to Polkadot.
|
||||
pub struct BaseExtrinsicParamsBuilder<T: Config, Tip> {
|
||||
era: Era,
|
||||
mortality_checkpoint: Option<T::Hash>,
|
||||
tip: Tip,
|
||||
}
|
||||
|
||||
impl<T: Config, Tip: Default> BaseExtrinsicParamsBuilder<T, Tip> {
|
||||
/// Instantiate the default set of [`BaseExtrinsicParamsBuilder`]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Set the [`Era`], which defines how long the transaction will be valid for
|
||||
/// (it can be either immortal, or it can be mortal and expire after a certain amount
|
||||
/// of time). The second argument is the block hash after which the transaction
|
||||
/// becomes valid, and must align with the era phase (see the [`Era::Mortal`] docs
|
||||
/// for more detail on that).
|
||||
pub fn era(mut self, era: Era, checkpoint: T::Hash) -> Self {
|
||||
self.era = era;
|
||||
self.mortality_checkpoint = Some(checkpoint);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the tip you'd like to give to the block author
|
||||
/// for this transaction.
|
||||
pub fn tip(mut self, tip: impl Into<Tip>) -> Self {
|
||||
self.tip = tip.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config, Tip: Default> Default for BaseExtrinsicParamsBuilder<T, Tip> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
era: Era::Immortal,
|
||||
mortality_checkpoint: None,
|
||||
tip: Tip::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config, Tip: Encode> ExtrinsicParams<T> for BaseExtrinsicParams<T, Tip> {
|
||||
type OtherParams = BaseExtrinsicParamsBuilder<T, Tip>;
|
||||
|
||||
fn new(
|
||||
// Provided from subxt client:
|
||||
spec_version: u32,
|
||||
transaction_version: u32,
|
||||
nonce: T::Index,
|
||||
genesis_hash: T::Hash,
|
||||
// Provided externally:
|
||||
other_params: Self::OtherParams,
|
||||
) -> Self {
|
||||
BaseExtrinsicParams {
|
||||
era: other_params.era,
|
||||
mortality_checkpoint: other_params
|
||||
.mortality_checkpoint
|
||||
.unwrap_or(genesis_hash),
|
||||
tip: other_params.tip,
|
||||
nonce,
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash,
|
||||
marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
let nonce: u64 = self.nonce.into();
|
||||
let tip = Encoded(self.tip.encode());
|
||||
(self.era, Compact(nonce), tip).encode_to(v);
|
||||
}
|
||||
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
(
|
||||
self.spec_version,
|
||||
self.transaction_version,
|
||||
self.genesis_hash,
|
||||
self.mortality_checkpoint,
|
||||
)
|
||||
.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
/// A tip payment.
|
||||
#[derive(Copy, Clone, Default, Encode)]
|
||||
pub struct PlainTip {
|
||||
#[codec(compact)]
|
||||
tip: u128,
|
||||
}
|
||||
|
||||
impl PlainTip {
|
||||
/// Create a new tip of the amount provided.
|
||||
pub fn new(amount: u128) -> Self {
|
||||
PlainTip { tip: amount }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for PlainTip {
|
||||
fn from(n: u128) -> Self {
|
||||
PlainTip::new(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// A tip payment made in the form of a specific asset.
|
||||
#[derive(Copy, Clone, Default, Encode)]
|
||||
pub struct AssetTip {
|
||||
#[codec(compact)]
|
||||
tip: u128,
|
||||
asset: Option<u32>,
|
||||
}
|
||||
|
||||
impl AssetTip {
|
||||
/// Create a new tip of the amount provided.
|
||||
pub fn new(amount: u128) -> Self {
|
||||
AssetTip {
|
||||
tip: amount,
|
||||
asset: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Designate the tip as being of a particular asset class.
|
||||
/// If this is not set, then the native currency is used.
|
||||
pub fn of_asset(mut self, asset: u32) -> Self {
|
||||
self.asset = Some(asset);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for AssetTip {
|
||||
fn from(n: u128) -> Self {
|
||||
AssetTip::new(n)
|
||||
}
|
||||
}
|
||||
@@ -17,58 +17,51 @@
|
||||
//! A library to **sub**mit e**xt**rinsics to a
|
||||
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
|
||||
|
||||
use super::{
|
||||
SignedExtra,
|
||||
SignedPayload,
|
||||
UncheckedExtrinsic,
|
||||
};
|
||||
use crate::Config;
|
||||
use codec::Encode;
|
||||
use sp_core::Pair;
|
||||
use sp_runtime::traits::{
|
||||
IdentifyAccount,
|
||||
SignedExtension,
|
||||
Verify,
|
||||
};
|
||||
|
||||
/// Extrinsic signer.
|
||||
#[async_trait::async_trait]
|
||||
pub trait Signer<T: Config, E: SignedExtra<T>> {
|
||||
/// Returns the account id.
|
||||
fn account_id(&self) -> &T::AccountId;
|
||||
|
||||
/// Signing transactions requires a [`Signer`]. This is responsible for
|
||||
/// providing the "from" account that the transaction is being signed by,
|
||||
/// as well as actually signing a SCALE encoded payload. Optionally, a
|
||||
/// signer can also provide the nonce for the transaction to use.
|
||||
pub trait Signer<T: Config> {
|
||||
/// Optionally returns a nonce.
|
||||
fn nonce(&self) -> Option<T::Index>;
|
||||
|
||||
/// Takes an unsigned extrinsic and returns a signed extrinsic.
|
||||
/// Return the "from" account ID.
|
||||
fn account_id(&self) -> &T::AccountId;
|
||||
|
||||
/// Return the "from" address.
|
||||
fn address(&self) -> T::Address;
|
||||
|
||||
/// Takes a signer payload for an extrinsic, and returns a signature based on it.
|
||||
///
|
||||
/// Some signers may fail, for instance because the hardware on which the keys are located has
|
||||
/// refused the operation.
|
||||
async fn sign(
|
||||
&self,
|
||||
extrinsic: SignedPayload<T, E>,
|
||||
) -> Result<UncheckedExtrinsic<T, E>, String>;
|
||||
fn sign(&self, signer_payload: &[u8]) -> T::Signature;
|
||||
}
|
||||
|
||||
/// Extrinsic signer using a private key.
|
||||
/// A [`Signer`] implementation that can be constructed from an [`Pair`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PairSigner<T: Config, E, P: Pair> {
|
||||
pub struct PairSigner<T: Config, P: Pair> {
|
||||
account_id: T::AccountId,
|
||||
nonce: Option<T::Index>,
|
||||
signer: P,
|
||||
marker: std::marker::PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<T, E, P> PairSigner<T, E, P>
|
||||
impl<T, P> PairSigner<T, P>
|
||||
where
|
||||
T: Config,
|
||||
E: SignedExtra<T>,
|
||||
T::Signature: From<P::Signature>,
|
||||
<T::Signature as Verify>::Signer:
|
||||
From<P::Public> + IdentifyAccount<AccountId = T::AccountId>,
|
||||
P: Pair,
|
||||
{
|
||||
/// Creates a new `Signer` from a `Pair`.
|
||||
/// Creates a new [`Signer`] from a [`Pair`].
|
||||
pub fn new(signer: P) -> Self {
|
||||
let account_id =
|
||||
<T::Signature as Verify>::Signer::from(signer.public()).into_account();
|
||||
@@ -76,11 +69,11 @@ where
|
||||
account_id,
|
||||
nonce: None,
|
||||
signer,
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the nonce to a new value.
|
||||
/// Sets the nonce to a new value. By default, the nonce will
|
||||
/// be retrieved from the node. Setting one here will override that.
|
||||
pub fn set_nonce(&mut self, nonce: T::Index) {
|
||||
self.nonce = Some(nonce);
|
||||
}
|
||||
@@ -90,43 +83,37 @@ where
|
||||
self.nonce = self.nonce.map(|nonce| nonce + 1u32.into());
|
||||
}
|
||||
|
||||
/// Returns the signer.
|
||||
/// Returns the [`Pair`] implementation used to construct this.
|
||||
pub fn signer(&self) -> &P {
|
||||
&self.signer
|
||||
}
|
||||
|
||||
/// Return the account ID.
|
||||
pub fn account_id(&self) -> &T::AccountId {
|
||||
&self.account_id
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<T, E, P> Signer<T, E> for PairSigner<T, E, P>
|
||||
impl<T, P> Signer<T> for PairSigner<T, P>
|
||||
where
|
||||
T: Config,
|
||||
E: SignedExtra<T>,
|
||||
T::AccountId: Into<T::Address> + 'static,
|
||||
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
|
||||
Send + Sync + 'static,
|
||||
T::AccountId: Into<T::Address> + Clone + 'static,
|
||||
P: Pair + 'static,
|
||||
P::Signature: Into<T::Signature> + 'static,
|
||||
{
|
||||
fn account_id(&self) -> &T::AccountId {
|
||||
&self.account_id
|
||||
}
|
||||
|
||||
fn nonce(&self) -> Option<T::Index> {
|
||||
self.nonce
|
||||
}
|
||||
|
||||
async fn sign(
|
||||
&self,
|
||||
extrinsic: SignedPayload<T, E>,
|
||||
) -> Result<UncheckedExtrinsic<T, E>, String> {
|
||||
let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload));
|
||||
let (call, extra, _) = extrinsic.deconstruct();
|
||||
let extrinsic = UncheckedExtrinsic::<T, E>::new_signed(
|
||||
call,
|
||||
self.account_id.clone().into(),
|
||||
signature.into(),
|
||||
extra,
|
||||
);
|
||||
Ok(extrinsic)
|
||||
fn account_id(&self) -> &T::AccountId {
|
||||
&self.account_id
|
||||
}
|
||||
|
||||
fn address(&self) -> T::Address {
|
||||
self.account_id.clone().into()
|
||||
}
|
||||
|
||||
fn sign(&self, signer_payload: &[u8]) -> T::Signature {
|
||||
self.signer.sign(signer_payload).into()
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -90,12 +90,11 @@ pub use crate::{
|
||||
RawEventDetails,
|
||||
},
|
||||
extrinsic::{
|
||||
DefaultExtra,
|
||||
DefaultExtraWithTxPayment,
|
||||
PairSigner,
|
||||
SignedExtra,
|
||||
Signer,
|
||||
UncheckedExtrinsic,
|
||||
PolkadotExtrinsicParams,
|
||||
PolkadotExtrinsicParamsBuilder,
|
||||
SubstrateExtrinsicParams,
|
||||
SubstrateExtrinsicParamsBuilder,
|
||||
},
|
||||
metadata::{
|
||||
ErrorMetadata,
|
||||
|
||||
+11
-10
@@ -30,10 +30,7 @@ use frame_metadata::{
|
||||
META_RESERVED,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Call,
|
||||
Encoded,
|
||||
};
|
||||
use crate::Call;
|
||||
use scale_info::{
|
||||
form::PortableForm,
|
||||
Type,
|
||||
@@ -148,18 +145,22 @@ impl PalletMetadata {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Encode a call based on this pallet metadata.
|
||||
pub fn encode_call<C>(&self, call: &C) -> Result<Encoded, MetadataError>
|
||||
/// Get the index of this pallet.
|
||||
pub fn index(&self) -> u8 {
|
||||
self.index
|
||||
}
|
||||
|
||||
/// Attempt to resolve a call into an index in this pallet, failing
|
||||
/// if the call is not found in this pallet.
|
||||
pub fn call_index<C>(&self) -> Result<u8, MetadataError>
|
||||
where
|
||||
C: Call,
|
||||
{
|
||||
let fn_index = self
|
||||
let fn_index = *self
|
||||
.calls
|
||||
.get(C::FUNCTION)
|
||||
.ok_or(MetadataError::CallNotFound(C::FUNCTION))?;
|
||||
let mut bytes = vec![self.index, *fn_index];
|
||||
bytes.extend(call.encode());
|
||||
Ok(Encoded(bytes))
|
||||
Ok(fn_index)
|
||||
}
|
||||
|
||||
/// Return [`StorageEntryMetadata`] given some storage key.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,6 @@ use crate::{
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::Signer;
|
||||
|
||||
// Check that we can subscribe to non-finalized block events.
|
||||
#[async_std::test]
|
||||
@@ -118,7 +117,7 @@ async fn balance_transfer_subscription() -> Result<(), subxt::BasicError> {
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob.clone().into(), 10_000)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?;
|
||||
|
||||
// Wait for the next balance transfer event in our subscription stream
|
||||
|
||||
@@ -34,10 +34,7 @@ use sp_runtime::{
|
||||
AccountId32,
|
||||
MultiAddress,
|
||||
};
|
||||
use subxt::{
|
||||
Error,
|
||||
Signer,
|
||||
};
|
||||
use subxt::Error;
|
||||
|
||||
#[async_std::test]
|
||||
async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
|
||||
@@ -62,7 +59,7 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob_address, 10_000)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -118,7 +115,7 @@ async fn multiple_transfers_work_nonce_incremented(
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob_address.clone(), 10_000)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_in_block() // Don't need to wait for finalization; this is quicker.
|
||||
.await?
|
||||
@@ -163,7 +160,7 @@ async fn storage_balance_lock() -> Result<(), subxt::Error<DispatchError>> {
|
||||
100_000_000_000_000,
|
||||
runtime_types::pallet_staking::RewardDestination::Stash,
|
||||
)
|
||||
.sign_and_submit_then_watch(&bob)
|
||||
.sign_and_submit_then_watch_default(&bob)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
@@ -203,7 +200,7 @@ async fn transfer_error() {
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(hans_address, 100_000_000_000_000_000)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -215,7 +212,7 @@ async fn transfer_error() {
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(alice_addr, 100_000_000_000_000_000)
|
||||
.sign_and_submit_then_watch(&hans)
|
||||
.sign_and_submit_then_watch_default(&hans)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -242,7 +239,7 @@ async fn transfer_implicit_subscription() {
|
||||
.tx()
|
||||
.balances()
|
||||
.transfer(bob_addr, 10_000)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
|
||||
@@ -28,7 +28,7 @@ use crate::{
|
||||
DispatchError,
|
||||
},
|
||||
test_context,
|
||||
NodeRuntimeSignedExtra,
|
||||
NodeRuntimeParams,
|
||||
TestContext,
|
||||
};
|
||||
use sp_core::sr25519::Pair;
|
||||
@@ -44,7 +44,7 @@ use subxt::{
|
||||
|
||||
struct ContractsTestContext {
|
||||
cxt: TestContext,
|
||||
signer: PairSigner<DefaultConfig, NodeRuntimeSignedExtra, Pair>,
|
||||
signer: PairSigner<DefaultConfig, Pair>,
|
||||
}
|
||||
|
||||
type Hash = <DefaultConfig as Config>::Hash;
|
||||
@@ -62,7 +62,7 @@ impl ContractsTestContext {
|
||||
self.cxt.client()
|
||||
}
|
||||
|
||||
fn contracts_tx(&self) -> TransactionApi<DefaultConfig, NodeRuntimeSignedExtra> {
|
||||
fn contracts_tx(&self) -> TransactionApi<DefaultConfig, NodeRuntimeParams> {
|
||||
self.cxt.api.tx().contracts()
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ impl ContractsTestContext {
|
||||
vec![], // data
|
||||
vec![], // salt
|
||||
)
|
||||
.sign_and_submit_then_watch(&self.signer)
|
||||
.sign_and_submit_then_watch_default(&self.signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -131,7 +131,7 @@ impl ContractsTestContext {
|
||||
data,
|
||||
salt,
|
||||
)
|
||||
.sign_and_submit_then_watch(&self.signer)
|
||||
.sign_and_submit_then_watch_default(&self.signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -162,7 +162,7 @@ impl ContractsTestContext {
|
||||
None, // storage_deposit_limit
|
||||
input_data,
|
||||
)
|
||||
.sign_and_submit_then_watch(&self.signer)
|
||||
.sign_and_submit_then_watch_default(&self.signer)
|
||||
.await?;
|
||||
|
||||
log::info!("Call result: {:?}", result);
|
||||
|
||||
@@ -32,10 +32,7 @@ use sp_core::{
|
||||
Pair,
|
||||
};
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::{
|
||||
Error,
|
||||
Signer,
|
||||
};
|
||||
use subxt::Error;
|
||||
|
||||
/// Helper function to generate a crypto pair from seed
|
||||
fn get_from_seed(seed: &str) -> sr25519::Pair {
|
||||
@@ -58,7 +55,7 @@ async fn validate_with_controller_account() {
|
||||
.tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs())
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -75,7 +72,7 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error<DispatchE
|
||||
.tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs())
|
||||
.sign_and_submit_then_watch(&alice_stash)
|
||||
.sign_and_submit_then_watch_default(&alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
@@ -96,7 +93,7 @@ async fn nominate_with_controller_account() {
|
||||
.tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.account_id().clone().into()])
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -115,7 +112,7 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error<DispatchE
|
||||
.tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.account_id().clone().into()])
|
||||
.sign_and_submit_then_watch(&alice_stash)
|
||||
.sign_and_submit_then_watch_default(&alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
@@ -139,7 +136,7 @@ async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
|
||||
.tx()
|
||||
.staking()
|
||||
.nominate(vec![bob_stash.account_id().clone().into()])
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -158,7 +155,7 @@ async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
|
||||
.tx()
|
||||
.staking()
|
||||
.chill()
|
||||
.sign_and_submit_then_watch(&alice_stash)
|
||||
.sign_and_submit_then_watch_default(&alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
@@ -173,7 +170,7 @@ async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
|
||||
.tx()
|
||||
.staking()
|
||||
.chill()
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
@@ -197,7 +194,7 @@ async fn tx_bond() -> Result<(), Error<DispatchError>> {
|
||||
100_000_000_000_000,
|
||||
RewardDestination::Stash,
|
||||
)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
@@ -213,7 +210,7 @@ async fn tx_bond() -> Result<(), Error<DispatchError>> {
|
||||
100_000_000_000_000,
|
||||
RewardDestination::Stash,
|
||||
)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
|
||||
@@ -44,7 +44,7 @@ async fn test_sudo() -> Result<(), subxt::Error<DispatchError>> {
|
||||
.tx()
|
||||
.sudo()
|
||||
.sudo(call)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
@@ -70,7 +70,7 @@ async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error<DispatchError>>
|
||||
.tx()
|
||||
.sudo()
|
||||
.sudo_unchecked_weight(call, 0)
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
|
||||
@@ -24,7 +24,6 @@ use crate::{
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::Signer;
|
||||
|
||||
#[async_std::test]
|
||||
async fn storage_account() -> Result<(), subxt::Error<DispatchError>> {
|
||||
@@ -52,7 +51,7 @@ async fn tx_remark_with_event() -> Result<(), subxt::Error<DispatchError>> {
|
||||
.tx()
|
||||
.system()
|
||||
.remark_with_event(b"remarkable".to_vec())
|
||||
.sign_and_submit_then_watch(&alice)
|
||||
.sign_and_submit_then_watch_default(&alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
|
||||
@@ -49,7 +49,7 @@ async fn storage_map_lookup() -> Result<(), subxt::Error<DispatchError>> {
|
||||
.tx()
|
||||
.system()
|
||||
.remark(vec![1, 2, 3, 4, 5])
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.sign_and_submit_then_watch_default(&signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -114,7 +114,7 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error<DispatchError
|
||||
.tx()
|
||||
.assets()
|
||||
.create(99, alice.clone().into(), 1)
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.sign_and_submit_then_watch_default(&signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
@@ -122,7 +122,7 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error<DispatchError
|
||||
.tx()
|
||||
.assets()
|
||||
.approve_transfer(99, bob.clone().into(), 123)
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.sign_and_submit_then_watch_default(&signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
|
||||
@@ -22,18 +22,16 @@ pub(crate) use crate::{
|
||||
use sp_core::sr25519::Pair;
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::{
|
||||
extrinsic::ChargeAssetTxPayment,
|
||||
Client,
|
||||
DefaultConfig,
|
||||
DefaultExtraWithTxPayment,
|
||||
PairSigner,
|
||||
SubstrateExtrinsicParams,
|
||||
};
|
||||
|
||||
/// substrate node should be installed on the $PATH
|
||||
const SUBSTRATE_NODE_PATH: &str = "substrate";
|
||||
|
||||
pub type NodeRuntimeSignedExtra =
|
||||
DefaultExtraWithTxPayment<DefaultConfig, ChargeAssetTxPayment<DefaultConfig>>;
|
||||
pub type NodeRuntimeParams = SubstrateExtrinsicParams<DefaultConfig>;
|
||||
|
||||
pub async fn test_node_process_with(
|
||||
key: AccountKeyring,
|
||||
@@ -60,7 +58,7 @@ pub async fn test_node_process() -> TestNodeProcess<DefaultConfig> {
|
||||
|
||||
pub struct TestContext {
|
||||
pub node_proc: TestNodeProcess<DefaultConfig>,
|
||||
pub api: node_runtime::RuntimeApi<DefaultConfig, NodeRuntimeSignedExtra>,
|
||||
pub api: node_runtime::RuntimeApi<DefaultConfig, NodeRuntimeParams>,
|
||||
}
|
||||
|
||||
impl TestContext {
|
||||
@@ -76,8 +74,6 @@ pub async fn test_context() -> TestContext {
|
||||
TestContext { node_proc, api }
|
||||
}
|
||||
|
||||
pub fn pair_signer(
|
||||
pair: Pair,
|
||||
) -> PairSigner<DefaultConfig, NodeRuntimeSignedExtra, Pair> {
|
||||
pub fn pair_signer(pair: Pair) -> PairSigner<DefaultConfig, Pair> {
|
||||
PairSigner::new(pair)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user