referrerpolicy=no-referrer-when-downgrade

pallet_meta_tx/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Meta Tx (Meta Transaction) Pallet
19//!
20//! This pallet enables the dispatch of transactions that are authorized by one party (the signer)
21//! and executed by an untrusted third party (the relayer), who covers the transaction fees.
22//!
23//! ## Pallet API
24//!
25//! See the [`pallet`] module for more information about the interfaces this pallet exposes,
26//! including its configuration trait, dispatchables, storage items, events and errors.
27//!
28//! ## Overview
29//!
30//! The pallet provides a client-level API, typically not meant for direct use by end users.
31//! A meta transaction, constructed with the help of a wallet, contains a target call, necessary
32//! extensions, and the signer's signature. This transaction is then broadcast, and any interested
33//! relayer can pick it up and execute it. The relayer submits a regular transaction via the
34//! [`dispatch`](`Pallet::dispatch`) function, passing the meta transaction as an argument to
35//! execute the target call on behalf of the signer while covering the fees.
36//!
37//! ### Example
38#![doc = docify::embed!("src/tests.rs", sign_and_execute_meta_tx)]
39//!
40//! ## Low-Level / Implementation Details
41//!
42//! The structure of a meta transaction is identical to the
43//! [`General`](sp_runtime::generic::Preamble::General) transaction.
44//! It contains the target call along with a configurable set of extensions and its associated
45//! version. Typically, these extensions include type like
46//! `pallet_verify_signature::VerifySignature`, which provides the signer address
47//! and the signature of the payload, encompassing the call and the meta-transaction’s
48//! configurations, such as its mortality.  The extensions follow the same [`TransactionExtension`]
49//! contract, and common types such as [`frame_system::CheckGenesis`],
50//! [`frame_system::CheckMortality`], [`frame_system::CheckNonce`], etc., are applicable in the
51//! context of meta transactions. Check the `mock` setup for the example.
52
53#![cfg_attr(not(feature = "std"), no_std)]
54
55mod benchmarking;
56#[cfg(test)]
57mod mock;
58#[cfg(all(test, not(feature = "runtime-benchmarks")))]
59mod tests;
60pub mod weights;
61#[cfg(feature = "runtime-benchmarks")]
62pub use benchmarking::types::WeightlessExtension;
63pub use pallet::*;
64pub use weights::WeightInfo;
65mod extension;
66pub use extension::MetaTxMarker;
67
68use core::ops::Add;
69use frame_support::{
70	dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo},
71	pallet_prelude::*,
72};
73use frame_system::{pallet_prelude::*, RawOrigin as SystemOrigin};
74use sp_runtime::{
75	generic::ExtensionVersion,
76	traits::{
77		AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, TransactionExtension,
78	},
79};
80use sp_std::prelude::*;
81
82/// Meta Transaction type.
83///
84/// The data that is provided and signed by the signer and shared with the relayer.
85#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, Clone, RuntimeDebug, DecodeWithMemTracking)]
86pub struct MetaTx<Call, Extension> {
87	/// The target call to be executed on behalf of the signer.
88	call: Call,
89	/// The extension version.
90	extension_version: ExtensionVersion,
91	/// The extension/s for the meta transaction.
92	extension: Extension,
93}
94
95impl<Call, Extension> MetaTx<Call, Extension> {
96	/// Create a new meta transaction.
97	pub fn new(call: Call, extension_version: ExtensionVersion, extension: Extension) -> Self {
98		Self { call, extension_version, extension }
99	}
100}
101
102/// The [`MetaTx`] for the given config.
103pub type MetaTxFor<T> = MetaTx<<T as frame_system::Config>::RuntimeCall, <T as Config>::Extension>;
104
105#[frame_support::pallet]
106pub mod pallet {
107	use super::*;
108
109	#[pallet::config]
110	pub trait Config:
111		frame_system::Config<
112		RuntimeCall: Dispatchable<
113			Info = DispatchInfo,
114			PostInfo = PostDispatchInfo,
115			RuntimeOrigin = <Self as frame_system::Config>::RuntimeOrigin,
116		>,
117		RuntimeOrigin: AsTransactionAuthorizedOrigin + From<SystemOrigin<Self::AccountId>>,
118	>
119	{
120		/// Weight information for calls in this pallet.
121		type WeightInfo: WeightInfo;
122		/// The overarching event type.
123		#[allow(deprecated)]
124		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
125		/// Transaction extension/s for meta transactions.
126		///
127		/// The extensions that must be present in every meta transaction. This generally includes
128		/// extensions like `pallet_verify_signature::VerifySignature`,
129		/// [frame_system::CheckSpecVersion], [frame_system::CheckTxVersion],
130		/// [frame_system::CheckGenesis], [frame_system::CheckMortality],
131		/// [frame_system::CheckNonce], etc. Check the `mock` setup for the example.
132		///
133		/// The types implementing the [`TransactionExtension`] trait can be composed into a tuple
134		/// type that will implement the same trait by piping invocations through each type.
135		///
136		/// In the `runtime-benchmarks` environment the type must implement [`Default`] trait.
137		/// The extension must provide an origin and the extension's weight must be zero. Use
138		/// `pallet_meta_tx::WeightlessExtension` type when the `runtime-benchmarks` feature
139		/// enabled.
140		type Extension: TransactionExtension<<Self as frame_system::Config>::RuntimeCall>;
141	}
142
143	#[pallet::error]
144	pub enum Error<T> {
145		/// Invalid proof (e.g. signature).
146		BadProof,
147		/// The meta transaction is not yet valid (e.g. nonce too high).
148		Future,
149		/// The meta transaction is outdated (e.g. nonce too low).
150		Stale,
151		/// The meta transactions's birth block is ancient.
152		AncientBirthBlock,
153		/// The transaction extension did not authorize any origin.
154		UnknownOrigin,
155		/// The meta transaction is invalid.
156		Invalid,
157	}
158
159	#[pallet::event]
160	#[pallet::generate_deposit(pub(crate) fn deposit_event)]
161	pub enum Event<T: Config> {
162		/// A meta transaction has been dispatched.
163		///
164		/// Contains the dispatch result of the meta transaction along with post-dispatch
165		/// information.
166		Dispatched { result: DispatchResultWithPostInfo },
167	}
168
169	#[pallet::pallet]
170	pub struct Pallet<T>(_);
171
172	#[pallet::call]
173	impl<T: Config> Pallet<T> {
174		/// Dispatch a given meta transaction.
175		///
176		/// - `_origin`: Can be any kind of origin.
177		/// - `meta_tx`: Meta Transaction with a target call to be dispatched.
178		#[pallet::call_index(0)]
179		#[pallet::weight({
180			let dispatch_info = meta_tx.call.get_dispatch_info();
181			let extension_weight = meta_tx.extension.weight(&meta_tx.call);
182			let bare_call_weight = T::WeightInfo::bare_dispatch();
183			(
184				dispatch_info.call_weight.add(extension_weight).add(bare_call_weight),
185				dispatch_info.class,
186			)
187		})]
188		pub fn dispatch(
189			_origin: OriginFor<T>,
190			meta_tx: Box<MetaTxFor<T>>,
191		) -> DispatchResultWithPostInfo {
192			let origin = SystemOrigin::None;
193			let meta_tx_size = meta_tx.encoded_size();
194			// `info` with worst-case call weight and extension weight.
195			let info = {
196				let mut info = meta_tx.call.get_dispatch_info();
197				info.extension_weight = meta_tx.extension.weight(&meta_tx.call);
198				info
199			};
200
201			// dispatch the meta transaction.
202			let meta_dispatch_res = meta_tx
203				.extension
204				.dispatch_transaction(
205					origin.into(),
206					meta_tx.call,
207					&info,
208					meta_tx_size,
209					meta_tx.extension_version,
210				)
211				.map_err(Error::<T>::from)?;
212
213			Self::deposit_event(Event::Dispatched { result: meta_dispatch_res });
214
215			// meta weight after possible refunds.
216			let meta_weight = meta_dispatch_res
217				.map_or_else(|err| err.post_info.actual_weight, |info| info.actual_weight)
218				.unwrap_or(info.total_weight());
219
220			Ok((Some(T::WeightInfo::bare_dispatch().saturating_add(meta_weight)), true.into())
221				.into())
222		}
223	}
224
225	/// Implements [`From<TransactionValidityError>`] for [`Error`] by mapping the relevant error
226	/// variants.
227	impl<T> From<TransactionValidityError> for Error<T> {
228		fn from(err: TransactionValidityError) -> Self {
229			use TransactionValidityError::*;
230			match err {
231				Unknown(_) => Error::<T>::Invalid,
232				Invalid(err) => match err {
233					InvalidTransaction::BadProof => Error::<T>::BadProof,
234					InvalidTransaction::Future => Error::<T>::Future,
235					InvalidTransaction::Stale => Error::<T>::Stale,
236					InvalidTransaction::AncientBirthBlock => Error::<T>::AncientBirthBlock,
237					InvalidTransaction::UnknownOrigin => Error::<T>::UnknownOrigin,
238					_ => Error::<T>::Invalid,
239				},
240			}
241		}
242	}
243}