4

I was doing some experiments with Substrate Node Template. I found that for every extrinsic, user is charged with some fee. I explored

https://github.com/paritytech/substrate/blob/master/frame/transaction-payment/src/lib.rs https://github.com/paritytech/substrate/blob/master/bin/node/runtime/src/lib.rs

but couldn't understand how this is working internally.

In Runtime configuration, I found that this pallet does not depend on any other pallet neither this is a dependency for any other pallet. Then how this works internally?

Can someone please help me to understand how transaction-payment pallet is being handled internally without any dependent/dependency?

1 Answer 1

7

It handles transaction payments through what is known as Signed Extensions in Substrate. Basically, a signed extension allows you to perform logic that pertains to validating the transaction received from the network/transaction pool, and also allows you to run some logic before and after an extrinsic is executed. The pre-dispatch method is called before a transaction is executed, and the validate method is called to put it in the Transaction Queue.

In the runtime code you have linked search for the type facilitating this behaviour is : pallet_asset_tx_payment::ChargeAssetTxPayment<Runtime>

This is basically validating a transaction and computing the fees that should be charged for a transaction. Of interest are these two methods:

fn validate(
        &self,
        who: &Self::AccountId,
        call: &Self::Call,
        info: &DispatchInfoOf<Self::Call>,
        len: usize,
    ) -> TransactionValidity {
        let (final_fee, _) = self.withdraw_fee(who, call, info, len)?;
        let tip = self.0;
        Ok(ValidTransaction {
            priority: Self::get_priority(info, len, tip, final_fee),
            ..Default::default()
        })
    }

    fn pre_dispatch(
        self,
        who: &Self::AccountId,
        call: &Self::Call,
        info: &DispatchInfoOf<Self::Call>,
        len: usize,
    ) -> Result<Self::Pre, TransactionValidityError> {
        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
        Ok((self.0, who.clone(), imbalance))
    }

Then there is one more piece of the puzzle which is the query_info method defined inside the pallet. It is used by the transaction payment runtime api to query for the extrinsic weight, runtime extrinsic base weight and current fee multiplier and compute a fee without having knowledge of additional tips and other charges (which will be obtained by the transaction information itself, i.e. the input to the signed extension):

https://github.com/paritytech/substrate/blob/e6768a3bd553ddbed12fe1a0e4a2ef8d4f8fdf52/bin/node/runtime/src/lib.rs#L2031-L2041

Finally, The actual fee is deducted using a CurrencyAdapter type that is specified at the runtime configuration.

    type OnChargeTransaction = CurrencyAdapter<Balances, ()>;

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.