use crate::{
substrate_test_pallet::pallet::Call as PalletCall, AccountId, Balance, BalancesCall,
CheckSubstrateCall, Extrinsic, Nonce, Pair, RuntimeCall, SignedPayload, TransferData,
};
use codec::Encode;
use frame_metadata_hash_extension::CheckMetadataHash;
use frame_system::{CheckNonce, CheckWeight};
use sp_core::crypto::Pair as TraitPair;
use sp_keyring::AccountKeyring;
use sp_runtime::{
generic::Preamble, traits::TransactionExtension, transaction_validity::TransactionPriority,
Perbill,
};
#[derive(Clone)]
pub struct Transfer {
pub from: Pair,
pub to: AccountId,
pub amount: Balance,
pub nonce: u64,
}
impl Transfer {
pub fn into_unchecked_extrinsic(self) -> Extrinsic {
ExtrinsicBuilder::new_transfer(self).build()
}
}
impl Default for TransferData {
fn default() -> Self {
Self {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
amount: 0,
nonce: 0,
}
}
}
impl TryFrom<&Extrinsic> for TransferData {
type Error = ();
fn try_from(uxt: &Extrinsic) -> Result<Self, Self::Error> {
match uxt {
Extrinsic {
function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }),
preamble: Preamble::Signed(from, _, ((CheckNonce(nonce), ..), ..)),
} => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }),
Extrinsic {
function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }),
preamble: Preamble::Bare(_),
} => Ok(transfer.clone()),
_ => Err(()),
}
}
}
pub struct ExtrinsicBuilder {
function: RuntimeCall,
signer: Option<Pair>,
nonce: Option<Nonce>,
metadata_hash: Option<[u8; 32]>,
}
impl ExtrinsicBuilder {
pub fn new(function: impl Into<RuntimeCall>) -> Self {
Self {
function: function.into(),
signer: Some(AccountKeyring::Alice.pair()),
nonce: None,
metadata_hash: None,
}
}
pub fn new_unsigned(function: impl Into<RuntimeCall>) -> Self {
Self { function: function.into(), signer: None, nonce: None, metadata_hash: None }
}
pub fn new_bench_call(transfer: TransferData) -> Self {
Self::new_unsigned(PalletCall::bench_call { transfer })
}
pub fn new_transfer(transfer: Transfer) -> Self {
Self {
nonce: Some(transfer.nonce),
signer: Some(transfer.from.clone()),
metadata_hash: None,
..Self::new(BalancesCall::transfer_allow_death {
dest: transfer.to,
value: transfer.amount,
})
}
}
pub fn new_include_data(data: Vec<u8>) -> Self {
Self::new(PalletCall::include_data { data })
}
pub fn new_storage_change(key: Vec<u8>, value: Option<Vec<u8>>) -> Self {
Self::new_unsigned(PalletCall::storage_change { key, value })
}
pub fn new_offchain_index_set(key: Vec<u8>, value: Vec<u8>) -> Self {
Self::new(PalletCall::offchain_index_set { key, value })
}
pub fn new_offchain_index_clear(key: Vec<u8>) -> Self {
Self::new(PalletCall::offchain_index_clear { key })
}
pub fn new_indexed_call(data: Vec<u8>) -> Self {
Self::new(PalletCall::indexed_call { data })
}
pub fn new_deposit_log_digest_item(log: sp_runtime::generic::DigestItem) -> Self {
Self::new_unsigned(PalletCall::deposit_log_digest_item { log })
}
pub fn new_fill_block(ratio: Perbill) -> Self {
Self::new(PalletCall::fill_block { ratio })
}
pub fn new_call_do_not_propagate() -> Self {
Self::new(PalletCall::call_do_not_propagate {})
}
pub fn new_call_with_priority(priority: TransactionPriority) -> Self {
Self::new(PalletCall::call_with_priority { priority })
}
pub fn new_read(count: u32) -> Self {
Self::new_unsigned(PalletCall::read { count })
}
pub fn new_read_and_panic(count: u32) -> Self {
Self::new_unsigned(PalletCall::read_and_panic { count })
}
pub fn unsigned(mut self) -> Self {
self.signer = None;
self
}
pub fn nonce(mut self, nonce: Nonce) -> Self {
self.nonce = Some(nonce);
self
}
pub fn signer(mut self, signer: Pair) -> Self {
self.signer = Some(signer);
self
}
pub fn metadata_hash(mut self, metadata_hash: [u8; 32]) -> Self {
self.metadata_hash = Some(metadata_hash);
self
}
pub fn build(self) -> Extrinsic {
if let Some(signer) = self.signer {
let tx_ext = (
(CheckNonce::from(self.nonce.unwrap_or(0)), CheckWeight::new()),
CheckSubstrateCall {},
self.metadata_hash
.map(CheckMetadataHash::new_with_custom_hash)
.unwrap_or_else(|| CheckMetadataHash::new(false)),
);
let raw_payload = SignedPayload::from_raw(
self.function.clone(),
tx_ext.clone(),
tx_ext.implicit().unwrap(),
);
let signature = raw_payload.using_encoded(|e| signer.sign(e));
Extrinsic::new_signed(self.function, signer.public(), signature, tx_ext)
} else {
Extrinsic::new_bare(self.function)
}
}
}