docs
docs copied to clipboard
Correct explanation of `partialFee`
According to https://wiki.polkadot.network/docs/build-node-interaction#fetching-a-block, partialFee contains the weight and length fee, not just the weight fee.
We should look to the code for real behavior, not just wiki: https://github.com/paritytech/substrate/blob/master/frame/transaction-payment/src/lib.rs#L506
impl<Balance: AtLeast32BitUnsigned + Copy> FeeDetails<Balance> {
/// Returns the final fee.
///
/// ```ignore
/// final_fee = inclusion_fee + tip;
/// ```
pub fn final_fee(&self) -> Balance {
self.inclusion_fee
.as_ref()
.map(|i| i.inclusion_fee())
.unwrap_or_else(|| Zero::zero())
.saturating_add(self.tip)
}
}
So it does include tip
@shawntabrizi I believe you're looking at the wrong function. Isn't the payment info populated by query_info?
I mean... the transaction info cannot contain any information about the tip before signing, right?
I believe the tip information is included in the same extrinsic payload that is signed and submitted to chain. Pretty sure I am looking at the right function, as I am not aware of the term "partialFee" being used elsewhere.
@shawntabrizi Thank you for helping again, Shawn. I'm fairly clueless about javascript/typescript, so I'm not sure exactly how the code of polkadot.js works. But I think we may not be talking about the same thing. Apologies for the long comment, I'll try to explain.
First of all, I understand that the tip is part of the payload that is submitted to the chain. The question is which types of fee are included in the partialFee field of the objects returned by tx.paymentInfo and api.rpc.payment.queryInfo. For both cases, I've created an example to demonstrate behavior:
-
For
tx.paymentInfo, I use the following code:const { ApiPromise, WsProvider, Keyring } = require("@polkadot/api"); ALICE = "15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5"; ALICE_KEY = "//ALICE"; BOB = "14E5nqKAp3oAJcmzgZhUD2RcptBeUBScxKHgJKU4HPNcKVf3"; BASE = 1000000000000; MILLI = BASE / 1000; async function main() { const provider = new WsProvider("ws://127.0.0.1:9944"); const api = await ApiPromise.create({ provider }); const keyring = new Keyring({ type: "sr25519" }); alice = keyring.addFromUri(ALICE_KEY); const tx = api.tx.balances.transfer(BOB, 10 * MILLI); const paymentInfo = await tx.paymentInfo(alice); const paymentInfoWithTip = await tx.paymentInfo(alice, { tip: 100 * BASE }); console.log(`Partial fee (no tip): ${paymentInfo.partialFee.toNumber()}`); console.log(`Partial fee (w/ tip): ${paymentInfoWithTip.partialFee.toNumber()}`); process.exit(); } main() .catch(console.error);The idea here is that, according to the docs:
A wrapper for this is available on the tx itself, taking exactly the same parameters as you would pass to a normal
.signAndSendoperation, specifically.paymentInfo(sender, <any options>).Nevertheless, the fees are almost identical, despite the generous tip:
Partial fee (no tip): 171485576 Partial fee (w/ tip): 177485576The difference between the tips is due to the length of the payload, I guess?
-
For
api.rpc.payment.queryInfo, check the following transaction: https://westend.subscan.io/extrinsic/0x3a6f9eb77a9a75201b60b1aed65e4d63b9b4772ab5acfd469bc604833b06c49e with fee 0.01610000137 WND (+0.1 WND tips). Run the following code:const { ApiPromise, WsProvider, Keyring } = require("@polkadot/api"); async function main() { const provider = new WsProvider("ws://127.0.0.1:9944"); const api = await ApiPromise.create({ provider }); let blockHash = await api.rpc.chain.getBlockHash(8685417); let block = await api.rpc.chain.getBlock(blockHash.toString()); let extrinsic = block.block.extrinsics.filter((extrinsic) => { return extrinsic.hash.toHex() == 0x3a6f9eb77a9a75201b60b1aed65e4d63b9b4772ab5acfd469bc604833b06c49e} )[0]; const queryInfo = await api.rpc.payment.queryInfo(extrinsic.toHex(), blockHash); console.log(queryInfo.partialFee.toNumber()); process.exit(); } main().catch(console.error);This prints
16100001370, so the tip is not included.
Regarding the underlying Rust code, I'm not sure I understand what you're saying either. The term partial fee appears only in the code of query_info, which, I presume, is used to populate the return value of tx.paymentInfo and api.rpc.payment.queryInfo. You write:
Pretty sure I am looking at the right function, as I am not aware of the term "partialFee" being used elsewhere.
I don't see any relation between partial_fee/partialFee and compute_fee_raw or FeeDetails, other than that query_info calls compute_free_raw, but it's called with a tip parameter of zero, so it doesn't really matter. Am I missing something important here?
So, long story short, I'm not sure I understand where you're going with the pieces of code you posted, but I'm fairly convinced that the behavior of polkadot.js and the polkadot-wiki are correct.