11

Do we have any reference how we can implement gasless/feeless transaction?

2
  • 5
    Want 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? Apr 11, 2022 at 19:26

3 Answers 3

5

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.

12

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 Calls - 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

7

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-paymententirely

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.