diff --git a/tests/tests/test-fees/test-fee-multiplier.ts b/tests/tests/test-fees/test-fee-multiplier.ts index 1bc3cb8a17..09bb3fad35 100644 --- a/tests/tests/test-fees/test-fee-multiplier.ts +++ b/tests/tests/test-fees/test-fee-multiplier.ts @@ -13,7 +13,8 @@ import { } from "../../util/xcm"; import { expectOk } from "../../util/expect"; import { KeyringPair } from "@substrate/txwrapper-core"; -import { TARGET_FILL_AMOUNT } from "../../util/constants"; +import { GLMR, TARGET_FILL_AMOUNT, WEIGHT_FEE } from "../../util/constants"; +import { verifyLatestBlockFees } from "../../util/block"; // Note on the values from 'transactionPayment.nextFeeMultiplier': this storage item is actually a // FixedU128, which is basically a u128 with an implicit denominator of 10^18. However, this @@ -425,3 +426,42 @@ describeDevMoonbeam("Fee Multiplier - XCM Executions", (context) => { expect(initialValue.eq(postValue), "Fee Multiplier has changed between blocks").to.be.true; }); }); + +describeDevMoonbeam("TransactionPayment Runtime Queries", (context) => { + it("should be able to query length fee", async function () { + // this test is really meant to show that `queryLengthToFee()` works, but for the inquisitive, + // this is how our length fee is calculated: + // fee = N**3 + N * 1_000_000_000 (where N: size_in_bytes): + const numBytes = 1n; + const coefficient = 1_000_000_000n; + const exponent = 3n; + const expected = numBytes ** exponent + numBytes * coefficient; + + const adjusted_length_fee = + await context.polkadotApi.call.transactionPaymentApi.queryLengthToFee(numBytes); + expect(adjusted_length_fee.toBigInt()).to.eq(expected); + }); + + it("should be able to query weight fee", async function () { + const adjusted_weight_fee = + await context.polkadotApi.call.transactionPaymentApi.queryWeightToFee({ + refTime: 1, + proofSize: 1, + }); + expect(adjusted_weight_fee.toBigInt()).to.eq(WEIGHT_FEE); + }); + + it("should be able to calculate entire fee", async function () { + const tx = await context.polkadotApi.tx.balances.transfer(alith.address, GLMR).signAsync(alith); + const result = await context.createBlock(tx); + await verifyLatestBlockFees(context); + }); + + it("should be able to calculate entire fee including tip", async function () { + const tx = await context.polkadotApi.tx.balances + .transfer(alith.address, GLMR) + .signAsync(alith, { tip: 123 }); + const result = await context.createBlock(tx); + await verifyLatestBlockFees(context); + }); +}); diff --git a/tests/util/block.ts b/tests/util/block.ts index ccd085af88..9da54fc2b8 100644 --- a/tests/util/block.ts +++ b/tests/util/block.ts @@ -11,7 +11,8 @@ import { import { FrameSystemEventRecord, SpWeightsWeightV2Weight } from "@polkadot/types/lookup"; import { u32, u64, u128, Option } from "@polkadot/types"; import { expect } from "chai"; -import { WEIGHT_PER_GAS } from "./constants"; + +import { EXTRINSIC_BASE_WEIGHT, WEIGHT_PER_GAS } from "./constants"; import { DevTestContext } from "./setup-dev-tests"; import { rateLimiter } from "./common"; import type { Block, AccountId20 } from "@polkadot/types/interfaces/runtime/types"; @@ -138,7 +139,9 @@ export const verifyBlockFees = async ( let blockBurnt = 0n; // iterate over every extrinsic - for (const { events, extrinsic, fee } of blockDetails.txWithEvents) { + for (const txWithEvents of blockDetails.txWithEvents) { + let { events, extrinsic, fee } = txWithEvents; + // This hash will only exist if the transaction was executed through ethereum. let ethereumAddress = ""; @@ -217,9 +220,40 @@ export const verifyBlockFees = async ( txBurnt += tipFeePortions.burnt; } else { // For a regular substrate tx, we use the partialFee - let feePortions = calculateFeePortions(fee.partialFee.toBigInt()); - txFees = fee.partialFee.toBigInt(); - txBurnt += feePortions.burnt; + const feePortions = calculateFeePortions(fee.partialFee.toBigInt()); + const tipPortions = calculateFeePortions(extrinsic.tip.toBigInt()); + txFees += fee.partialFee.toBigInt() + extrinsic.tip.toBigInt(); + txBurnt += feePortions.burnt + tipPortions.burnt; + + // verify entire substrate txn fee + const apiAt = await context.polkadotApi.at(previousBlockHash); + const lengthFee = ( + (await apiAt.call.transactionPaymentApi.queryLengthToFee( + extrinsic.encodedLength + )) as any + ).toBigInt(); + + const unadjustedWeightFee = ( + (await apiAt.call.transactionPaymentApi.queryWeightToFee({ + refTime: fee.weight, + proofSize: 0n, + })) as any + ).toBigInt(); + const multiplier = await apiAt.query.transactionPayment.nextFeeMultiplier(); + const denominator = 1_000_000_000_000_000_000n; + const weightFee = (unadjustedWeightFee * multiplier.toBigInt()) / denominator; + + const baseFee = ( + (await apiAt.call.transactionPaymentApi.queryWeightToFee({ + refTime: EXTRINSIC_BASE_WEIGHT, + proofSize: 0n, + })) as any + ).toBigInt(); + + const tip = extrinsic.tip.toBigInt(); + const expectedPartialFee = lengthFee + weightFee + baseFee; + + expect(expectedPartialFee).to.eq(fee.partialFee.toBigInt()); } blockFees += txFees;