mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 17:31:05 +00:00
Double map and plain storage support, introduce macros (#93)
* Support custom clients. * Simplify trait bounds. * Plain and double map storage support. * Simplify more trait bounds. * Add proc macro. * Add Call, Event and Store traits. * Update proc-macros. * Add with_system for proc-macro. * proc-macro: test: support signature and extra fields. * proc-macro: test: support sharing state accross steps. * proc-macro: test: fetch state sequentially. * Elide lifetimes. * Add test for plain storage. * Run rustfmt.
This commit is contained in:
+57
-16
@@ -16,15 +16,24 @@
|
||||
|
||||
//! Implements support for the pallet_balances module.
|
||||
|
||||
use crate::frame::{
|
||||
system::System,
|
||||
Call,
|
||||
use crate::{
|
||||
frame::{
|
||||
system::System,
|
||||
Call,
|
||||
Event,
|
||||
Store,
|
||||
},
|
||||
metadata::{
|
||||
Metadata,
|
||||
MetadataError,
|
||||
},
|
||||
};
|
||||
use codec::{
|
||||
Decode,
|
||||
Encode,
|
||||
};
|
||||
use frame_support::Parameter;
|
||||
use sp_core::storage::StorageKey;
|
||||
use sp_runtime::traits::{
|
||||
AtLeast32Bit,
|
||||
MaybeSerialize,
|
||||
@@ -32,6 +41,8 @@ use sp_runtime::traits::{
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
const MODULE: &str = "Balances";
|
||||
|
||||
/// The subset of the `pallet_balances::Trait` that a client must implement.
|
||||
pub trait Balances: System {
|
||||
/// The balance of an account.
|
||||
@@ -70,15 +81,22 @@ pub struct AccountData<Balance> {
|
||||
pub fee_frozen: Balance,
|
||||
}
|
||||
|
||||
const MODULE: &str = "Balances";
|
||||
const TRANSFER: &str = "transfer";
|
||||
/// The total issuance of the balances module.
|
||||
#[derive(Encode)]
|
||||
pub struct TotalIssuance<T>(pub core::marker::PhantomData<T>);
|
||||
|
||||
/// Arguments for transferring a balance
|
||||
#[derive(codec::Encode)]
|
||||
pub struct TransferArgs<T: Balances> {
|
||||
to: <T as System>::Address,
|
||||
#[codec(compact)]
|
||||
amount: <T as Balances>::Balance,
|
||||
impl<T: Balances> Store<T> for TotalIssuance<T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FIELD: &'static str = "TotalIssuance";
|
||||
type Returns = T::Balance;
|
||||
|
||||
fn key(&self, metadata: &Metadata) -> Result<StorageKey, MetadataError> {
|
||||
Ok(metadata
|
||||
.module(Self::MODULE)?
|
||||
.storage(Self::FIELD)?
|
||||
.plain()?
|
||||
.key())
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer some liquid free balance to another account.
|
||||
@@ -87,9 +105,32 @@ pub struct TransferArgs<T: Balances> {
|
||||
/// It will decrease the total issuance of the system by the `TransferFee`.
|
||||
/// If the sender's account is below the existential deposit as a result
|
||||
/// of the transfer, the account will be reaped.
|
||||
pub fn transfer<T: Balances>(
|
||||
to: <T as System>::Address,
|
||||
amount: <T as Balances>::Balance,
|
||||
) -> Call<TransferArgs<T>> {
|
||||
Call::new(MODULE, TRANSFER, TransferArgs { to, amount })
|
||||
#[derive(Encode)]
|
||||
pub struct TransferCall<'a, T: Balances> {
|
||||
/// Destination of the transfer.
|
||||
pub to: &'a <T as System>::Address,
|
||||
/// Amount to transfer.
|
||||
#[codec(compact)]
|
||||
pub amount: T::Balance,
|
||||
}
|
||||
|
||||
impl<'a, T: Balances> Call<T> for TransferCall<'a, T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FUNCTION: &'static str = "transfer";
|
||||
}
|
||||
|
||||
/// Transfer event.
|
||||
#[derive(Debug, Decode, Eq, PartialEq)]
|
||||
pub struct TransferEvent<T: Balances> {
|
||||
/// Account balance was transfered from.
|
||||
pub from: <T as System>::AccountId,
|
||||
/// Account balance was transfered to.
|
||||
pub to: <T as System>::AccountId,
|
||||
/// Amount of balance that was transfered.
|
||||
pub amount: T::Balance,
|
||||
}
|
||||
|
||||
impl<T: Balances> Event<T> for TransferEvent<T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const EVENT: &'static str = "transfer";
|
||||
}
|
||||
|
||||
+94
-95
@@ -20,23 +20,15 @@ use crate::frame::{
|
||||
balances::Balances,
|
||||
system::System,
|
||||
Call,
|
||||
Event,
|
||||
};
|
||||
use codec::{
|
||||
Decode,
|
||||
Encode,
|
||||
};
|
||||
use codec::Encode;
|
||||
|
||||
const MODULE: &str = "Contracts";
|
||||
|
||||
mod calls {
|
||||
pub const PUT_CODE: &str = "put_code";
|
||||
pub const INSTANTIATE: &str = "instantiate";
|
||||
pub const CALL: &str = "call";
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
mod events {
|
||||
pub const CODE_STORED: &str = "CodeStored";
|
||||
pub const INSTANTIATED: &str = "Instantiated";
|
||||
}
|
||||
|
||||
/// Gas units are chosen to be represented by u64 so that gas metering
|
||||
/// instructions can operate on them efficiently.
|
||||
pub type Gas = u64;
|
||||
@@ -44,40 +36,21 @@ pub type Gas = u64;
|
||||
/// The subset of the `pallet_contracts::Trait` that a client must implement.
|
||||
pub trait Contracts: System + Balances {}
|
||||
|
||||
/// Arguments for uploading contract code to the chain
|
||||
#[derive(Encode)]
|
||||
pub struct PutCodeArgs {
|
||||
#[codec(compact)]
|
||||
gas_limit: Gas,
|
||||
code: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Arguments for creating an instance of a contract
|
||||
#[derive(Encode)]
|
||||
pub struct InstantiateArgs<T: Contracts> {
|
||||
#[codec(compact)]
|
||||
endowment: <T as Balances>::Balance,
|
||||
#[codec(compact)]
|
||||
gas_limit: Gas,
|
||||
code_hash: <T as System>::Hash,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Arguments for calling a contract
|
||||
#[derive(Encode)]
|
||||
pub struct CallArgs<T: Contracts> {
|
||||
dest: <T as System>::Address,
|
||||
value: <T as Balances>::Balance,
|
||||
#[codec(compact)]
|
||||
gas_limit: Gas,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Stores the given binary Wasm code into the chain's storage and returns
|
||||
/// its `codehash`.
|
||||
/// You can instantiate contracts only with stored code.
|
||||
pub fn put_code(gas_limit: Gas, code: Vec<u8>) -> Call<PutCodeArgs> {
|
||||
Call::new(MODULE, calls::PUT_CODE, PutCodeArgs { gas_limit, code })
|
||||
#[derive(Debug, Encode)]
|
||||
pub struct PutCodeCall<'a> {
|
||||
/// Gas limit.
|
||||
#[codec(compact)]
|
||||
pub gas_limit: Gas,
|
||||
/// Wasm blob.
|
||||
pub code: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a, T: Contracts> Call<T> for PutCodeCall<'a> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FUNCTION: &'static str = "put_code";
|
||||
}
|
||||
|
||||
/// Creates a new contract from the `codehash` generated by `put_code`,
|
||||
@@ -93,22 +66,23 @@ pub fn put_code(gas_limit: Gas, code: Vec<u8>) -> Call<PutCodeArgs> {
|
||||
/// of the account. That code will be invoked upon any call received by
|
||||
/// this account.
|
||||
/// - The contract is initialized.
|
||||
pub fn instantiate<T: Contracts>(
|
||||
endowment: <T as Balances>::Balance,
|
||||
gas_limit: Gas,
|
||||
code_hash: <T as System>::Hash,
|
||||
data: Vec<u8>,
|
||||
) -> Call<InstantiateArgs<T>> {
|
||||
Call::new(
|
||||
MODULE,
|
||||
calls::INSTANTIATE,
|
||||
InstantiateArgs {
|
||||
endowment,
|
||||
gas_limit,
|
||||
code_hash,
|
||||
data,
|
||||
},
|
||||
)
|
||||
#[derive(Debug, Encode)]
|
||||
pub struct InstantiateCall<'a, T: Contracts> {
|
||||
/// Initial balance transfered to the contract.
|
||||
#[codec(compact)]
|
||||
pub endowment: <T as Balances>::Balance,
|
||||
/// Gas limit.
|
||||
#[codec(compact)]
|
||||
pub gas_limit: Gas,
|
||||
/// Code hash returned by the put_code call.
|
||||
pub code_hash: &'a <T as System>::Hash,
|
||||
/// Data to initialize the contract with.
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a, T: Contracts> Call<T> for InstantiateCall<'a, T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FUNCTION: &'static str = "instantiate";
|
||||
}
|
||||
|
||||
/// Makes a call to an account, optionally transferring some balance.
|
||||
@@ -119,22 +93,46 @@ pub fn instantiate<T: Contracts>(
|
||||
/// * If no account exists and the call value is not less than
|
||||
/// `existential_deposit`, a regular account will be created and any value
|
||||
/// will be transferred.
|
||||
pub fn call<T: Contracts>(
|
||||
dest: <T as System>::Address,
|
||||
value: <T as Balances>::Balance,
|
||||
gas_limit: Gas,
|
||||
data: Vec<u8>,
|
||||
) -> Call<CallArgs<T>> {
|
||||
Call::new(
|
||||
MODULE,
|
||||
calls::CALL,
|
||||
CallArgs {
|
||||
dest,
|
||||
value,
|
||||
gas_limit,
|
||||
data,
|
||||
},
|
||||
)
|
||||
#[derive(Debug, Encode)]
|
||||
pub struct CallCall<'a, T: Contracts> {
|
||||
/// Address of the contract.
|
||||
pub dest: &'a <T as System>::Address,
|
||||
/// Value to transfer to the contract.
|
||||
pub value: <T as Balances>::Balance,
|
||||
/// Gas limit.
|
||||
#[codec(compact)]
|
||||
pub gas_limit: Gas,
|
||||
/// Data to send to the contract.
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a, T: Contracts> Call<T> for CallCall<'a, T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FUNCTION: &'static str = "call";
|
||||
}
|
||||
|
||||
/// Code stored event.
|
||||
#[derive(Debug, Decode)]
|
||||
pub struct CodeStoredEvent<T: Contracts> {
|
||||
/// Code hash of the contract.
|
||||
pub code_hash: T::Hash,
|
||||
}
|
||||
|
||||
impl<T: Contracts> Event<T> for CodeStoredEvent<T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const EVENT: &'static str = "CodeStored";
|
||||
}
|
||||
|
||||
/// Instantiated event.
|
||||
#[derive(Debug, Decode)]
|
||||
pub struct InstantiatedEvent<T: Contracts>(
|
||||
pub <T as System>::AccountId,
|
||||
pub <T as System>::AccountId,
|
||||
);
|
||||
|
||||
impl<T: Contracts> Event<T> for InstantiatedEvent<T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const EVENT: &'static str = "Instantiated";
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -147,22 +145,16 @@ mod tests {
|
||||
Verify,
|
||||
};
|
||||
|
||||
use super::events;
|
||||
use super::*;
|
||||
use crate::{
|
||||
frame::contracts::MODULE,
|
||||
tests::test_client,
|
||||
Balances,
|
||||
Client,
|
||||
DefaultNodeRuntime as Runtime,
|
||||
Error,
|
||||
System,
|
||||
};
|
||||
|
||||
type AccountId = <Runtime as System>::AccountId;
|
||||
|
||||
async fn put_code<T, P, S>(client: &Client<T, S>, signer: P) -> Result<T::Hash, Error>
|
||||
where
|
||||
T: System + Balances + Send + Sync,
|
||||
T: Contracts + Send + Sync,
|
||||
T::Address: From<T::AccountId>,
|
||||
P: Pair,
|
||||
P::Signature: Codec,
|
||||
@@ -179,10 +171,17 @@ mod tests {
|
||||
|
||||
let xt = client.xt(signer, None).await?;
|
||||
|
||||
let result = xt.watch().submit(super::put_code(500_000, wasm)).await?;
|
||||
let result = xt
|
||||
.watch()
|
||||
.submit(PutCodeCall {
|
||||
gas_limit: 500_000,
|
||||
code: &wasm,
|
||||
})
|
||||
.await?;
|
||||
let code_hash = result
|
||||
.find_event::<T::Hash>(MODULE, events::CODE_STORED)
|
||||
.ok_or(Error::Other("Failed to find CodeStored event".into()))??;
|
||||
.find_event::<CodeStoredEvent<T>>()?
|
||||
.ok_or(Error::Other("Failed to find CodeStored event".into()))?
|
||||
.code_hash;
|
||||
|
||||
Ok(code_hash)
|
||||
}
|
||||
@@ -219,16 +218,16 @@ mod tests {
|
||||
let xt = client.xt(signer, None).await?;
|
||||
let result = xt
|
||||
.watch()
|
||||
.submit(super::instantiate::<Runtime>(
|
||||
100_000_000_000_000,
|
||||
500_000,
|
||||
code_hash,
|
||||
Vec::new(),
|
||||
))
|
||||
.submit(InstantiateCall {
|
||||
endowment: 100_000_000_000_000,
|
||||
gas_limit: 500_000,
|
||||
code_hash: &code_hash,
|
||||
data: &[],
|
||||
})
|
||||
.await?;
|
||||
let event = result
|
||||
.find_event::<(AccountId, AccountId)>(MODULE, events::INSTANTIATED)
|
||||
.ok_or(Error::Other("Failed to find Instantiated event".into()))??;
|
||||
.find_event::<InstantiatedEvent<_>>()?
|
||||
.ok_or(Error::Other("Failed to find Instantiated event".into()))?;
|
||||
Ok(event)
|
||||
});
|
||||
|
||||
|
||||
+54
-19
@@ -16,29 +16,64 @@
|
||||
|
||||
//! Implements support for built-in runtime modules.
|
||||
|
||||
use codec::Encode;
|
||||
use crate::{
|
||||
events::{
|
||||
EventsDecoder,
|
||||
EventsError,
|
||||
},
|
||||
metadata::{
|
||||
Metadata,
|
||||
MetadataError,
|
||||
},
|
||||
};
|
||||
use codec::{
|
||||
Decode,
|
||||
Encode,
|
||||
};
|
||||
use sp_core::storage::StorageKey;
|
||||
|
||||
pub mod balances;
|
||||
pub mod contracts;
|
||||
pub mod system;
|
||||
|
||||
/// Creates module calls
|
||||
pub struct Call<T: Encode> {
|
||||
/// Module name
|
||||
pub module: &'static str,
|
||||
/// Function name
|
||||
pub function: &'static str,
|
||||
/// Call arguments
|
||||
pub args: T,
|
||||
}
|
||||
|
||||
impl<T: Encode> Call<T> {
|
||||
/// Create a module call
|
||||
pub fn new(module: &'static str, function: &'static str, args: T) -> Self {
|
||||
Call {
|
||||
module,
|
||||
function,
|
||||
args,
|
||||
}
|
||||
/// Store trait.
|
||||
pub trait Store<T>: Encode {
|
||||
/// Module name.
|
||||
const MODULE: &'static str;
|
||||
/// Field name.
|
||||
const FIELD: &'static str;
|
||||
/// Return type.
|
||||
type Returns: Decode;
|
||||
/// Returns the `StorageKey`.
|
||||
fn key(&self, metadata: &Metadata) -> Result<StorageKey, MetadataError>;
|
||||
/// Returns the default value.
|
||||
fn default(
|
||||
&self,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Option<Self::Returns>, MetadataError> {
|
||||
Ok(metadata
|
||||
.module(Self::MODULE)?
|
||||
.storage(Self::FIELD)?
|
||||
.default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Call trait.
|
||||
pub trait Call<T>: Encode {
|
||||
/// Module name.
|
||||
const MODULE: &'static str;
|
||||
/// Function name.
|
||||
const FUNCTION: &'static str;
|
||||
/// Load event decoder.
|
||||
fn events_decoder(_decoder: &mut EventsDecoder<T>) -> Result<(), EventsError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Event trait.
|
||||
pub trait Event<T>: Decode {
|
||||
/// Module name.
|
||||
const MODULE: &'static str;
|
||||
/// Event name.
|
||||
const EVENT: &'static str;
|
||||
}
|
||||
|
||||
+27
-59
@@ -22,11 +22,8 @@ use codec::{
|
||||
Encode,
|
||||
};
|
||||
use frame_support::Parameter;
|
||||
use futures::future::{
|
||||
self,
|
||||
Future,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use sp_core::storage::StorageKey;
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
AtLeast32Bit,
|
||||
@@ -40,24 +37,21 @@ use sp_runtime::{
|
||||
MaybeSerialize,
|
||||
MaybeSerializeDeserialize,
|
||||
Member,
|
||||
SignedExtension,
|
||||
SimpleBitOps,
|
||||
},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
pin::Pin,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
extrinsic::SignedExtra,
|
||||
frame::{
|
||||
balances::Balances,
|
||||
Call,
|
||||
Store,
|
||||
},
|
||||
metadata::{
|
||||
Metadata,
|
||||
MetadataError,
|
||||
},
|
||||
Client,
|
||||
};
|
||||
|
||||
/// The subset of the `frame::Trait` that a client must implement.
|
||||
@@ -116,7 +110,7 @@ pub trait System: 'static + Eq + Clone + Debug {
|
||||
+ Default;
|
||||
|
||||
/// The address type. This instead of `<frame_system::Trait::Lookup as StaticLookup>::Source`.
|
||||
type Address: Codec + Clone + PartialEq + Debug;
|
||||
type Address: Codec + Clone + PartialEq + Debug + Send + Sync;
|
||||
|
||||
/// The block header.
|
||||
type Header: Parameter
|
||||
@@ -147,59 +141,33 @@ pub struct AccountInfo<T: System> {
|
||||
pub data: T::AccountData,
|
||||
}
|
||||
|
||||
/// The System extension trait for the Client.
|
||||
pub trait SystemStore {
|
||||
/// System type.
|
||||
type System: System;
|
||||
const MODULE: &str = "System";
|
||||
|
||||
/// Returns the nonce and account data for an account_id.
|
||||
fn account(
|
||||
&self,
|
||||
account_id: <Self::System as System>::AccountId,
|
||||
) -> Pin<Box<dyn Future<Output = Result<AccountInfo<Self::System>, Error>> + Send>>;
|
||||
}
|
||||
/// Account field of the `System` module.
|
||||
#[derive(Encode)]
|
||||
pub struct AccountStore<'a, T: System>(pub &'a T::AccountId);
|
||||
|
||||
impl<T: System + Balances + Sync + Send + 'static, S: 'static, E> SystemStore
|
||||
for Client<T, S, E>
|
||||
where
|
||||
E: SignedExtra<T> + SignedExtension + 'static,
|
||||
{
|
||||
type System = T;
|
||||
impl<'a, T: System> Store<T> for AccountStore<'a, T> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FIELD: &'static str = "Account";
|
||||
type Returns = AccountInfo<T>;
|
||||
|
||||
fn account(
|
||||
&self,
|
||||
account_id: <Self::System as System>::AccountId,
|
||||
) -> Pin<Box<dyn Future<Output = Result<AccountInfo<Self::System>, Error>> + Send>>
|
||||
{
|
||||
let account_map = || {
|
||||
Ok(self
|
||||
.metadata
|
||||
.module("System")?
|
||||
.storage("Account")?
|
||||
.get_map()?)
|
||||
};
|
||||
let map = match account_map() {
|
||||
Ok(map) => map,
|
||||
Err(err) => return Box::pin(future::err(err)),
|
||||
};
|
||||
let client = self.clone();
|
||||
Box::pin(async move {
|
||||
client
|
||||
.fetch_or(map.key(account_id), None, map.default())
|
||||
.await
|
||||
})
|
||||
fn key(&self, metadata: &Metadata) -> Result<StorageKey, MetadataError> {
|
||||
Ok(metadata
|
||||
.module(Self::MODULE)?
|
||||
.storage(Self::FIELD)?
|
||||
.map()?
|
||||
.key(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
const MODULE: &str = "System";
|
||||
const SET_CODE: &str = "set_code";
|
||||
|
||||
/// Arguments for updating the runtime code
|
||||
pub type SetCode = Vec<u8>;
|
||||
#[derive(Encode)]
|
||||
pub struct SetCodeCall<'a>(pub &'a Vec<u8>);
|
||||
|
||||
/// Sets the new code.
|
||||
pub fn set_code(code: Vec<u8>) -> Call<SetCode> {
|
||||
Call::new(MODULE, SET_CODE, code)
|
||||
impl<'a, T: System> Call<T> for SetCodeCall<'a> {
|
||||
const MODULE: &'static str = MODULE;
|
||||
const FUNCTION: &'static str = "set_code";
|
||||
}
|
||||
|
||||
use frame_support::weights::DispatchInfo;
|
||||
|
||||
Reference in New Issue
Block a user