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}