#blockchain #substrate #pallet #feeless

no-std pallet-feeless

A pallet to implement a feeless Substrate blockchain node

2 releases

new 0.0.2 Apr 14, 2025
0.0.1 Apr 11, 2025

#616 in Magic Beans

Download history 176/week @ 2025-04-09

176 downloads per month

MIT license

42KB
639 lines

๐Ÿšซ pallet-feeless: Feeless Transactions with Rate Limiting for Substrate ๐Ÿšซ

Welcome to pallet-feeless โ€” a plug-and-play solution to enable fully feeless transactions in any Substrate-based blockchain! โœจ

Instead of charging transaction fees, this pallet uses a rate-limiting system to ensure fair usage and network security. It's perfect for applications where user experience, accessibility, and cost-efficiency are top priorities.


๐Ÿงฉ Introduction

Most blockchains rely on transaction fees to prevent spam and incentivize validators. While effective, this system can create friction, especially for:

  • Microtransactions ๐Ÿ’ธ
  • New users ๐Ÿง‘โ€๐Ÿ’ป
  • Developers building free-to-use apps ๐ŸŒ

This pallet removes transaction fees altogetherโ€”even during congestionโ€”by using a rate-limiting extrinsic extension. This makes your blockchain:

  • ๐Ÿšซ Free from fees
  • ๐Ÿ”„ Predictable for users
  • ๐Ÿ›ก๏ธ Secure against spam

๐Ÿ’ก How It Works

Instead of fees, each account is subject to a rate limit:

  • โฑ๏ธ Max Transactions per Period: The number of transactions allowed per account in a set block window.
  • ๐Ÿ“ฆ Max Size per Period: The total size of allowed transactions during that window.

This is enforced via a custom extrinsic extension (CheckRate) that plugs into Substrateโ€™s transaction validation pipeline alongside checks like:

  • CheckNonce
  • CheckWeight
  • CheckEra
  • ...

All validation and accounting are performed before and after dispatch, with minimal storage access to preserve performance and security.


โš™๏ธ Runtime Integration

1. Define AccountData in frame_system

type AccountData = pallet_feeless::AccountData<Balance, BlockNumber>;

This custom account type stores balance, last block, and rate data per account.


2. Set Up AccountStore in pallet_balances

type AccountStore = Account;

Required to track the accountโ€™s storage location and enable rate tracking.


3. Configure pallet_feeless

Add your custom settings in the runtime:

impl pallet_feeless::Config for Runtime {
    type MaxTxByPeriod = ConstU32<128>;     // Max transactions per period
    type MaxSizeByPeriod = ConstU32<1>;     // Max size in bytes per period
    type Period = ConstU32<5>;              // Length of the rate-limiting window (in blocks)
    type RuntimeEvent = RuntimeEvent;
    type WeightInfo = ();                   // Do not forget to generate and reference the weight after benchmarking
}

4. Update Your Transaction Extensions

Extend the runtime transaction validation to include CheckRate:

pub type TxExtension = (
    frame_system::CheckNonZeroSender<Runtime>,
    frame_system::CheckSpecVersion<Runtime>,
    frame_system::CheckTxVersion<Runtime>,
    frame_system::CheckGenesis<Runtime>,
    frame_system::CheckEra<Runtime>,
    frame_system::CheckNonce<Runtime>,
    pallet_feeless::CheckRate<Runtime>,                    // ๐Ÿ‘ˆ Rate limit extension
    frame_system::CheckWeight<Runtime>,
    pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
    frame_metadata_hash_extension::CheckMetadataHash<Runtime>,
);

5. Benchmarking and Payload Setup

In benchmarking.rs or similar:

let tx_ext: runtime::TxExtension = (
    frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
    frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
    frame_system::CheckTxVersion::<runtime::Runtime>::new(),
    frame_system::CheckGenesis::<runtime::Runtime>::new(),
    frame_system::CheckEra::<runtime::Runtime>::from(sp_runtime::generic::Era::mortal(
        period,
        best_block.saturated_into(),
    )),
    frame_system::CheckNonce::<runtime::Runtime>::from(nonce),
    pallet_feeless::CheckRate::<runtime::Runtime>::new(), // ๐Ÿ‘ˆ Add this
    frame_system::CheckWeight::<runtime::Runtime>::new(),
    pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
    frame_metadata_hash_extension::CheckMetadataHash::<runtime::Runtime>::new(false),
);

let raw_payload = runtime::SignedPayload::from_raw(
    call.clone(),
    tx_ext.clone(),
    (
        (),
        runtime::VERSION.spec_version,
        runtime::VERSION.transaction_version,
        genesis_hash,
        best_hash,
        (),
        (), // ๐Ÿ‘ˆ Add this
        (),
        (),
        None,
    ),
);

โœ… Benefits

  • ๐Ÿšซ No Transaction Fees
    Users never worry about price fluctuations.

  • โš–๏ธ Built-in Fairness
    Every account gets a rate limit, ensuring equal access.

  • ๐Ÿ› ๏ธ Developer Friendly
    Build dApps without forcing users to hold or buy tokens.

  • ๐ŸŒ Accessible UX
    Lower onboarding friction and support for micro-use cases.


โš ๏ธ Considerations

While this system improves user experience, keep in mind:

  • โš™๏ธ Fine-tuning required: Limits must strike a balance between usability and protection.
  • ๐ŸŽฏ No validator fees: Block rewards or other models must be used to incentivize validators.
  • ๐Ÿ›ก๏ธ Spam resistance: Rate limits must be sufficient to deter Sybil attacks or multi-account spamming.

๐Ÿ“ฆ Want to Try It?

Just include this pallet in your Substrate runtime and configure the limits to your needs. No additional infrastructure or fee logic is required!


๐Ÿ“„ License

MIT โ€” open-source and ready to use in your blockchain projects.

Dependencies

~23โ€“38MB
~635K SLoC