Do we have any reference how we can implement gasless/feeless transaction?
-
5Want to note here, that transaction can NEVER be "gasless", or in Substrate terms "weightless". All extrinsics should record their weight. However, they can be "feeless", as in the user can be programmed to not take a fee for the weight they use.– Shawn Tabrizi ♦Apr 7, 2022 at 18:27
-
But this does it have security issues?– Mrisho LukambaApr 11, 2022 at 19:26
3 Answers
It sounds like you want transactions to be 'free' - possibly for a some subset of network participants.
Depending on your runtime there are different ways to achieve this. The easiest being: you can remove transaction payments entirely from your runtime by simply deleting pallet_transaction_payments
from the construct_runtime!
macro invocation.
For something more subtle you can write a custom implementation of OnChargeTransaction
and hook this up to the transaction payments pallet.
I would recommend having a read through the docs for pallet_transaction_payment and the OnChargeTransaction trait.
There are broadly two ways of doing this: Inherents and Unsigned Transactions
Inherents are designed to be placed directly into the block by the author; they aren't stored in the transaction pool and broadcast between nodes. They are generated as part of the authoring process.
Unsigned Transactions are, from FRAME's perspective, extrinsics like any other. Except for the main difference that they have no associated sending account. They still manifest as Call
s - all the differences in handling unsigned transactions are related to deduplicating and spam resilience to avoid the spam against the chain.
One example is Polkadot's 'claims' module. Here's the code for the actual call, which uses ensure_none
to check that there is no regular account origin: https://github.com/paritytech/polkadot/blob/29a3f90bcbe6f237494d495f26c5f31c1451b216/runtime/common/src/claims.rs#L313-L328 and here's the code for the ValidateUnsigned
implementation which specifies additional validity and spam protection conditions that are necessary for this transaction type to be safe. Nodes will also use the validate_unsigned
implementation to keep their transaction pool pruned and avoid broadcasting or keeping around outdated unsigned transactions. https://github.com/paritytech/polkadot/blob/29a3f90bcbe6f237494d495f26c5f31c1451b216/runtime/common/src/claims.rs#L466-L516
While I also was doing something that requires fee-less transaction, it might be worthwhile for future reader,
Do not charge fee in specific extrinsic call
While defining extrinsic call in pallet, we usually do:
#[pallet::weight(10_000 + Weight::db_write())]
fn some_call(origin: _) -> DispatchResult
this can be changed to:
#[pallet::weight(Pays::No)]
fn some_call(origin: _) -> DispatchResult
so the call to some_call
will not deduce any fee from caller
Do not charge fee for selective accounts only
It might also be case that one have list of accounts that it should not charge fee for everyone else, it should
#[pallet::weight(10_000 + Weight::db_read(2))]
fn some_extrinsic(origin: OriginFro<T>) -> DispatchResultWithPostInfo {
let caller = ensure_signed(origin)?;
let special_accounts = <SepecialList<T>>::get();
/*
Other operation
*/
if special_accounts.contains(caller) {
Ok(Pays::No.into())
} else {
Ok(Pays::Yes.into())
}
}
Do not charge fee where caller doesn't matter at all
We can simply make unsigned extrinsic at all
The whole flow don't charge any fee
As already answered, you can remove pallet_transaction-payment
entirely