referrerpolicy=no-referrer-when-downgrade

Crate pallet_meta_tx

Source
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§

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§