Expand description
§Meta Tx (Meta Transaction) Pallet
This pallet enables the dispatch of transactions that are authorized by one party (the signer) and executed by an untrusted third party (the relayer), who covers the transaction fees.
§Pallet API
See the pallet
module for more information about the interfaces this pallet exposes,
including its configuration trait, dispatchables, storage items, events and errors.
§Overview
The pallet provides a client-level API, typically not meant for direct use by end users.
A meta transaction, constructed with the help of a wallet, contains a target call, necessary
extensions, and the signer’s signature. This transaction is then broadcast, and any interested
relayer can pick it up and execute it. The relayer submits a regular transaction via the
dispatch
function, passing the meta transaction as an argument to
execute the target call on behalf of the signer while covering the fees.
§Example
#[test]
fn sign_and_execute_meta_tx() {
new_test_ext().execute_with(|| {
// meta tx signer
let alice_keyring = Sr25519Keyring::Alice;
// meta tx relayer
let bob_keyring = Sr25519Keyring::Bob;
let alice_account: AccountId = alice_keyring.public().into();
let bob_account: AccountId = bob_keyring.public().into();
let tx_fee: Balance = (2 * TX_FEE).into(); // base tx fee + weight fee
let alice_balance = force_set_balance(alice_account.clone());
let bob_balance = force_set_balance(bob_account.clone());
// Alice builds a meta transaction.
let remark_call =
RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] });
let meta_tx_bare_ext = create_meta_tx_bare_ext(alice_account.clone());
let meta_tx_sig =
create_signature(remark_call.clone(), meta_tx_bare_ext.clone(), alice_keyring);
let meta_tx_ext = (
VerifySignatureExt::new_with_signature(meta_tx_sig, alice_account.clone()),
// append signed part.
meta_tx_bare_ext,
);
let meta_tx = MetaTxFor::<Runtime>::new(
remark_call.clone(),
META_EXTENSION_VERSION,
meta_tx_ext.clone(),
);
// Encode and share with the world.
let meta_tx_encoded = meta_tx.encode();
// Bob acts as meta transaction relayer.
let meta_tx = MetaTxFor::<Runtime>::decode(&mut &meta_tx_encoded[..]).unwrap();
let call = RuntimeCall::MetaTx(Call::dispatch { meta_tx: Box::new(meta_tx.clone()) });
let tx_bare_ext = create_tx_bare_ext(bob_account.clone());
let tx_sig = create_signature(call.clone(), tx_bare_ext.clone(), bob_keyring);
let tx_ext = (
VerifySignatureExt::new_with_signature(tx_sig, bob_account.clone()),
// append signed part
tx_bare_ext,
);
let uxt = UncheckedExtrinsic::new_transaction(call.clone(), tx_ext.clone());
// Check Extrinsic validity and apply it.
let result = apply_extrinsic(uxt);
// Asserting the results and make sure the weight is correct.
let tx_weight = tx_ext.weight(&call) + <Runtime as Config>::WeightInfo::bare_dispatch();
let meta_tx_weight = remark_call
.get_dispatch_info()
.call_weight
.add(meta_tx_ext.weight(&remark_call));
assert_eq!(
result,
Ok(PostDispatchInfo {
actual_weight: Some(meta_tx_weight + tx_weight),
pays_fee: Pays::Yes,
})
);
System::assert_has_event(RuntimeEvent::MetaTx(crate::Event::Dispatched {
result: Ok(PostDispatchInfo {
actual_weight: Some(meta_tx_weight),
pays_fee: Pays::Yes,
}),
}));
System::assert_has_event(RuntimeEvent::System(frame_system::Event::Remarked {
sender: alice_account.clone(),
hash: <Runtime as frame_system::Config>::Hashing::hash(&[1]),
}));
// Alice balance is unchanged, Bob paid the transaction fee.
assert_eq!(alice_balance, Balances::free_balance(alice_account));
assert_eq!(bob_balance - tx_fee, Balances::free_balance(bob_account));
});
}
§Low-Level / Implementation Details
The structure of a meta transaction is identical to the
General
transaction.
It contains the target call along with a configurable set of extensions and its associated
version. Typically, these extensions include type like
pallet_verify_signature::VerifySignature
, which provides the signer address
and the signature of the payload, encompassing the call and the meta-transaction’s
configurations, such as its mortality. The extensions follow the same [TransactionExtension
]
contract, and common types such as [frame_system::CheckGenesis
],
[frame_system::CheckMortality
], [frame_system::CheckNonce
], etc., are applicable in the
context of meta transactions. Check the mock
setup for the example.
Re-exports§
pub use weights::WeightInfo;
pub use pallet::*;
Modules§
- The
pallet
module in each FRAME pallet hosts the most important items needed to construct this pallet. - Autogenerated weights for
pallet_meta_tx
Structs§
- Meta Transaction type.
- This type serves as a marker extension to differentiate meta-transactions from regular transactions. It implements the
TransactionExtension
trait and carries constant implicit data (“_meta_tx”). - A weightless extension to facilitate the bare dispatch benchmark.
Type Aliases§
- The
MetaTx
for the given config.