Just to elaborate on this, since I've been digging into it and asking around...
We can use the following formula to calculate the
partial_fee for an extrinsic (ie the total/final fee
minus any tip).
partial_fee = base_fee + len_fee + ((adjusted_weight_fee/estimated_weight)*actual_weight)
base_fee is a fixed base fee to include some transaction in a block. It accounts
for the work needed to verify the signature and such and is constant for any tx.
len_fee is a fee paid based on the size (length in bytes) of the transaction. Longer
transactions require more storage.
adjusted_weight_fee is a fee that is itself
estimated_weight * targeted_fee_adjustment.
targeted_fee_adjustment is some adjustment made based on the network load and such, and is
an opaque internal value we have no access to.
estimated_weight is the "pre-dispatch" weight of the transaction. It's set based on the cost
of processing the transaction on reference hardware.
actual_weight is the weight that is found in the
ExtrinsicSuccess event for the extrinsic in
a block (it's just called
weight in the event), and is often the same as
but the node has the opportunity to change it to whatever it likes, I think.
The RPC endpoint
The RPC endpoint
weight in the response), and
partialFee value, which is our best guess at the inclusion fee for the tx without actually submitting
it and seeing whether the node changes the weight/decides not to take a fee at all.
To get the correct values for some extrinsic from those endpoints, provide the extrinsic bytes, and the
block number one before the block it made it into (eg if the extrinsic was in block 100, you'd use
block 99 as an argument). Using the correct block number is very important.
Once you've called these endpoints, access the
ExtrinsicSuccess event to find the
(it's just called
weight in the event props), but also a
paysFee value which signals whether the
extrinsic actually incurred a fee at all or not (a node has the opportunity to refund the fee entirely
if it likes by setting this to
So, the steps to calculate the fee are basically:
paysFee is No, the fee paid is 0
- else, gather the values needed above and plus them into the equation above to return the final
partial fee that was paid.
Why the above equation?!
Well, the basic way to calculate a pre-dispatch fee is:
partial_fee = base_fee + len_fee + adjusted_weight_fee
(see above for explanations of each value).
We can obtain this result just from the
payment RPC endpoints.
Once the extrinsic is in a block though, the
ExtrinsicSuccess event signals whether the weight used to
calculate the fee changed (and whether we need to pay at all via
paysFee). So we need to recalculate the
adjusted_weight_fee by swapping out the original
weight it was based on with the correct weight from the
ExtrinsicSuccess event. That's all there really is to it.
Note that doing this calculation may incur some precision loss, and so it's recommended to use high precision
maths libraries to get the most accurate values back.
Substrate based chains can use whatever custom fee logic they like, and so this logic/calculation only applies to chains that copy the Polkadot fee calculation logic.
There will soon be a
transactionPayment::TransactionFeePaid event, which will include the final fee paid for some transaction (ie the inclusion/partial fee + any tip). This will become a more reliable way to calculate fees going forwards (and is on Kusama already), but of course there is always a need to calculate fees for historic blocks too :)