From 9dd21b1eed1a96e5028c9e03269c1454a2bc5075 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= <alex.theissen@me.com>
Date: Thu, 21 May 2020 12:16:04 +0200
Subject: [PATCH] Allow fee calculation to happen off-chain (#6076)

* Emit a PaymentParameters event once per block

This contains per-block paramaters need to calculate
fees off-chain.

* Add WeightToFee trait

* Add documentation to polynomial types

* Ignore pseudo code snippet for doc tests

* Use `Mul` implementation of Perbill

* Add tests for WeightToFeePolynomial

* Revert "Emit a PaymentParameters event once per block"

This reverts commit 6c4763baff3d8179676a3c1660fe7063fd56a8ca.

Co-authored-by: Gavin Wood <gavin@parity.io>
---
 substrate/Cargo.lock                          |  46 +++---
 .../bin/node-template/runtime/src/lib.rs      |   7 +-
 substrate/bin/node/executor/tests/basic.rs    |  11 +-
 substrate/bin/node/executor/tests/fees.rs     |  11 +-
 substrate/bin/node/runtime/src/impls.rs       |  21 +--
 substrate/bin/node/runtime/src/lib.rs         |  11 +-
 .../frame/balances/src/tests_composite.rs     |   6 +-
 substrate/frame/balances/src/tests_local.rs   |   6 +-
 substrate/frame/contracts/src/tests.rs        |   4 +-
 substrate/frame/executive/src/lib.rs          |  12 +-
 substrate/frame/support/Cargo.toml            |   1 +
 substrate/frame/support/src/weights.rs        | 147 ++++++++++++++++++
 .../frame/transaction-payment/Cargo.toml      |   1 +
 .../frame/transaction-payment/src/lib.rs      |  36 +++--
 14 files changed, 241 insertions(+), 79 deletions(-)

diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 42a3c10cbfd..8bd5963b81c 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -749,7 +749,7 @@ dependencies = [
  "log",
  "regalloc",
  "serde",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "target-lexicon",
  "thiserror",
 ]
@@ -787,7 +787,7 @@ checksum = "e45f82e3446dd1ebb8c2c2f6a6b0e6cd6cd52965c7e5f7b1b35e9a9ace31ccde"
 dependencies = [
  "cranelift-codegen",
  "log",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "target-lexicon",
 ]
 
@@ -1488,6 +1488,7 @@ dependencies = [
  "paste",
  "pretty_assertions",
  "serde",
+ "smallvec 1.4.0",
  "sp-arithmetic",
  "sp-core",
  "sp-inherents",
@@ -1875,7 +1876,7 @@ dependencies = [
  "byteorder 1.3.4",
  "fallible-iterator",
  "indexmap",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "stable_deref_trait",
 ]
 
@@ -2495,7 +2496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e763b2a9b500ba47948061d1e8bc3b5f03a8a1f067dbcf822a4d2c84d2b54a3a"
 dependencies = [
  "parity-util-mem",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
 ]
 
 [[package]]
@@ -2524,7 +2525,7 @@ dependencies = [
  "parking_lot 0.10.2",
  "regex",
  "rocksdb",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
 ]
 
 [[package]]
@@ -2630,7 +2631,7 @@ dependencies = [
  "parity-multiaddr 0.9.0",
  "parking_lot 0.10.2",
  "pin-project",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "wasm-timer",
 ]
 
@@ -2661,7 +2662,7 @@ dependencies = [
  "ring",
  "rw-stream-sink",
  "sha2",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "thiserror",
  "unsigned-varint",
  "void",
@@ -2714,7 +2715,7 @@ dependencies = [
  "prost",
  "prost-build",
  "rand 0.7.3",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
 ]
 
 [[package]]
@@ -2737,7 +2738,7 @@ dependencies = [
  "prost-build",
  "rand 0.7.3",
  "sha2",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "unsigned-varint",
  "wasm-timer",
 ]
@@ -2754,7 +2755,7 @@ dependencies = [
  "log",
  "prost",
  "prost-build",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "wasm-timer",
 ]
 
@@ -2778,7 +2779,7 @@ dependencies = [
  "prost-build",
  "rand 0.7.3",
  "sha2",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "uint",
  "unsigned-varint",
  "void",
@@ -2802,7 +2803,7 @@ dependencies = [
  "log",
  "net2",
  "rand 0.7.3",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "void",
  "wasm-timer",
 ]
@@ -2931,7 +2932,7 @@ dependencies = [
  "libp2p-core",
  "log",
  "rand 0.7.3",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "void",
  "wasm-timer",
 ]
@@ -3299,7 +3300,7 @@ dependencies = [
  "futures 0.3.4",
  "log",
  "pin-project",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "unsigned-varint",
 ]
 
@@ -4652,6 +4653,7 @@ dependencies = [
  "pallet-balances",
  "pallet-transaction-payment-rpc-runtime-api",
  "parity-scale-codec",
+ "smallvec 1.4.0",
  "sp-core",
  "sp-io",
  "sp-runtime",
@@ -4847,7 +4849,7 @@ dependencies = [
  "parity-util-mem-derive",
  "parking_lot 0.10.2",
  "primitive-types",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "winapi 0.3.8",
 ]
 
@@ -4923,7 +4925,7 @@ dependencies = [
  "cloudabi",
  "libc",
  "redox_syscall",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "winapi 0.3.8",
 ]
 
@@ -5614,7 +5616,7 @@ checksum = "b27b256b41986ac5141b37b8bbba85d314fbf546c182eb255af6720e07e4f804"
 dependencies = [
  "log",
  "rustc-hash",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
 ]
 
 [[package]]
@@ -7198,9 +7200,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
+checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
 
 [[package]]
 name = "snow"
@@ -7247,7 +7249,7 @@ dependencies = [
  "log",
  "rand 0.7.3",
  "sha1",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
  "static_assertions",
  "thiserror",
 ]
@@ -8973,7 +8975,7 @@ dependencies = [
  "hashbrown",
  "log",
  "rustc-hex",
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
 ]
 
 [[package]]
@@ -9077,7 +9079,7 @@ version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
 dependencies = [
- "smallvec 1.3.0",
+ "smallvec 1.4.0",
 ]
 
 [[package]]
diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs
index 2037f7bb77f..55fa4cd4aa7 100644
--- a/substrate/bin/node-template/runtime/src/lib.rs
+++ b/substrate/bin/node-template/runtime/src/lib.rs
@@ -15,8 +15,7 @@ use sp_runtime::{
 	transaction_validity::{TransactionValidity, TransactionSource},
 };
 use sp_runtime::traits::{
-	BlakeTwo256, Block as BlockT, IdentityLookup, Verify, ConvertInto, IdentifyAccount, NumberFor,
-	Saturating,
+	BlakeTwo256, Block as BlockT, IdentityLookup, Verify, IdentifyAccount, NumberFor, Saturating,
 };
 use sp_api::impl_runtime_apis;
 use sp_consensus_aura::sr25519::AuthorityId as AuraId;
@@ -36,7 +35,7 @@ pub use frame_support::{
 	construct_runtime, parameter_types, StorageValue,
 	traits::{KeyOwnerProofSystem, Randomness},
 	weights::{
-		Weight,
+		Weight, IdentityFee,
 		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
 	},
 };
@@ -244,7 +243,7 @@ impl transaction_payment::Trait for Runtime {
 	type Currency = balances::Module<Runtime>;
 	type OnTransactionPayment = ();
 	type TransactionByteFee = TransactionByteFee;
-	type WeightToFee = ConvertInto;
+	type WeightToFee = IdentityFee<Balance>;
 	type FeeMultiplierUpdate = ();
 }
 
diff --git a/substrate/bin/node/executor/tests/basic.rs b/substrate/bin/node/executor/tests/basic.rs
index 9350c3546ff..857f438e1c1 100644
--- a/substrate/bin/node/executor/tests/basic.rs
+++ b/substrate/bin/node/executor/tests/basic.rs
@@ -19,12 +19,15 @@ use codec::{Encode, Decode, Joiner};
 use frame_support::{
 	StorageValue, StorageMap,
 	traits::Currency,
-	weights::{GetDispatchInfo, DispatchInfo, DispatchClass, constants::ExtrinsicBaseWeight},
+	weights::{
+		GetDispatchInfo, DispatchInfo, DispatchClass, constants::ExtrinsicBaseWeight,
+		WeightToFeePolynomial,
+	},
 };
 use sp_core::{NeverNativeValue, traits::Externalities, storage::well_known_keys};
 use sp_runtime::{
 	ApplyExtrinsicResult, Fixed128,
-	traits::{Hash as HashT, Convert},
+	traits::Hash as HashT,
 	transaction_validity::InvalidTransaction,
 };
 use pallet_contracts::ContractAddressFor;
@@ -54,9 +57,9 @@ fn transfer_fee<E: Encode>(extrinsic: &E, fee_multiplier: Fixed128) -> Balance {
 	let length_fee = TransactionByteFee::get() * (extrinsic.encode().len() as Balance);
 
 	let base_weight = ExtrinsicBaseWeight::get();
-	let base_fee = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::convert(base_weight);
+	let base_fee = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&base_weight);
 	let weight = default_transfer_call().get_dispatch_info().weight;
-	let weight_fee = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::convert(weight);
+	let weight_fee = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&weight);
 
 	base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee)
 }
diff --git a/substrate/bin/node/executor/tests/fees.rs b/substrate/bin/node/executor/tests/fees.rs
index 0e55f781e78..c4c3ca0bfc6 100644
--- a/substrate/bin/node/executor/tests/fees.rs
+++ b/substrate/bin/node/executor/tests/fees.rs
@@ -19,16 +19,15 @@ use codec::{Encode, Joiner};
 use frame_support::{
 	StorageValue, StorageMap,
 	traits::Currency,
-	weights::{GetDispatchInfo, constants::ExtrinsicBaseWeight},
+	weights::{GetDispatchInfo, constants::ExtrinsicBaseWeight, IdentityFee, WeightToFeePolynomial},
 };
 use sp_core::NeverNativeValue;
-use sp_runtime::{Fixed128, Perbill, traits::Convert};
+use sp_runtime::{Fixed128, Perbill};
 use node_runtime::{
 	CheckedExtrinsic, Call, Runtime, Balances, TransactionPayment,
-	TransactionByteFee, WeightFeeCoefficient,
+	TransactionByteFee,
 	constants::currency::*,
 };
-use node_runtime::impls::LinearWeightToFee;
 use node_primitives::Balance;
 use node_testing::keyring::*;
 
@@ -181,13 +180,13 @@ fn transaction_fee_is_correct_ultimate() {
 		let mut balance_alice = (100 - 69) * DOLLARS;
 
 		let base_weight = ExtrinsicBaseWeight::get();
-		let base_fee = LinearWeightToFee::<WeightFeeCoefficient>::convert(base_weight);
+		let base_fee = IdentityFee::<Balance>::calc(&base_weight);
 
 		let length_fee = TransactionByteFee::get() * (xt.clone().encode().len() as Balance);
 		balance_alice -= length_fee;
 
 		let weight = default_transfer_call().get_dispatch_info().weight;
-		let weight_fee = LinearWeightToFee::<WeightFeeCoefficient>::convert(weight);
+		let weight_fee = IdentityFee::<Balance>::calc(&weight);
 
 		// we know that weight to fee multiplier is effect-less in block 1.
 		// current weight of transfer = 200_000_000
diff --git a/substrate/bin/node/runtime/src/impls.rs b/substrate/bin/node/runtime/src/impls.rs
index ef994392b52..884bde08df3 100644
--- a/substrate/bin/node/runtime/src/impls.rs
+++ b/substrate/bin/node/runtime/src/impls.rs
@@ -21,7 +21,9 @@ use core::num::NonZeroI128;
 use node_primitives::Balance;
 use sp_runtime::traits::{Convert, Saturating};
 use sp_runtime::{Fixed128, Perquintill};
-use frame_support::{traits::{OnUnbalanced, Currency, Get}, weights::Weight};
+use frame_support::{
+	traits::{OnUnbalanced, Currency, Get},
+};
 use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance};
 
 pub struct Author;
@@ -47,18 +49,6 @@ impl Convert<u128, Balance> for CurrencyToVoteHandler {
 	fn convert(x: u128) -> Balance { x * Self::factor() }
 }
 
-/// Convert from weight to balance via a simple coefficient multiplication
-/// The associated type C encapsulates a constant in units of balance per weight
-pub struct LinearWeightToFee<C>(sp_std::marker::PhantomData<C>);
-
-impl<C: Get<Balance>> Convert<Weight, Balance> for LinearWeightToFee<C> {
-	fn convert(w: Weight) -> Balance {
-		// setting this to zero will disable the weight fee.
-		let coefficient = C::get();
-		Balance::from(w).saturating_mul(coefficient)
-	}
-}
-
 /// Update the given multiplier based on the following formula
 ///
 ///   diff = (previous_block_weight - target_weight)/max_weight
@@ -120,7 +110,7 @@ mod tests {
 	use sp_runtime::assert_eq_error_rate;
 	use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime};
 	use crate::{constants::currency::*, TransactionPayment, TargetBlockFullness};
-	use frame_support::weights::Weight;
+	use frame_support::weights::{Weight, WeightToFeePolynomial};
 	use core::num::NonZeroI128;
 
 	fn max() -> Weight {
@@ -228,7 +218,8 @@ mod tests {
 				if fm == next { panic!("The fee should ever increase"); }
 				fm = next;
 				iterations += 1;
-				let fee = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::convert(tx_weight);
+				let fee =
+					<Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&tx_weight);
 				let adjusted_fee = fm.saturated_multiply_accumulate(fee);
 				println!(
 					"iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index eb8344d3e95..209e86e7470 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -26,7 +26,7 @@ use sp_std::prelude::*;
 use frame_support::{
 	construct_runtime, parameter_types, debug,
 	weights::{
-		Weight,
+		Weight, IdentityFee,
 		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
 	},
 	traits::{Currency, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness, LockIdentifier},
@@ -74,7 +74,7 @@ pub use pallet_staking::StakerStatus;
 
 /// Implementations of some helper traits passed into runtime modules as associated types.
 pub mod impls;
-use impls::{CurrencyToVoteHandler, Author, LinearWeightToFee, TargetedFeeAdjustment};
+use impls::{CurrencyToVoteHandler, Author, TargetedFeeAdjustment};
 
 /// Constant values used within the runtime.
 pub mod constants;
@@ -228,9 +228,6 @@ impl pallet_balances::Trait for Runtime {
 
 parameter_types! {
 	pub const TransactionByteFee: Balance = 10 * MILLICENTS;
-	// In the Substrate node, a weight of 10_000_000 (smallest non-zero weight)
-	// is mapped to 10_000_000 units of fees, hence:
-	pub const WeightFeeCoefficient: Balance = 1;
 	// for a sane configuration, this should always be less than `AvailableBlockRatio`.
 	pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
 }
@@ -239,7 +236,9 @@ impl pallet_transaction_payment::Trait for Runtime {
 	type Currency = Balances;
 	type OnTransactionPayment = DealWithFees;
 	type TransactionByteFee = TransactionByteFee;
-	type WeightToFee = LinearWeightToFee<WeightFeeCoefficient>;
+	// In the Substrate node, a weight of 10_000_000 (smallest non-zero weight)
+	// is mapped to 10_000_000 units of fees, hence:
+	type WeightToFee = IdentityFee<Balance>;
 	type FeeMultiplierUpdate = TargetedFeeAdjustment<TargetBlockFullness>;
 }
 
diff --git a/substrate/frame/balances/src/tests_composite.rs b/substrate/frame/balances/src/tests_composite.rs
index 78cdc3838b2..7b9ec1f91ea 100644
--- a/substrate/frame/balances/src/tests_composite.rs
+++ b/substrate/frame/balances/src/tests_composite.rs
@@ -21,14 +21,14 @@
 
 use sp_runtime::{
 	Perbill,
-	traits::{ConvertInto, IdentityLookup},
+	traits::IdentityLookup,
 	testing::Header,
 };
 use sp_core::H256;
 use sp_io;
 use frame_support::{impl_outer_origin, parameter_types};
 use frame_support::traits::Get;
-use frame_support::weights::{Weight, DispatchInfo};
+use frame_support::weights::{Weight, DispatchInfo, IdentityFee};
 use std::cell::RefCell;
 use crate::{GenesisConfig, Module, Trait, decl_tests, tests::CallWithDispatchInfo};
 
@@ -87,7 +87,7 @@ impl pallet_transaction_payment::Trait for Test {
 	type Currency = Module<Test>;
 	type OnTransactionPayment = ();
 	type TransactionByteFee = TransactionByteFee;
-	type WeightToFee = ConvertInto;
+	type WeightToFee = IdentityFee<u64>;
 	type FeeMultiplierUpdate = ();
 }
 impl Trait for Test {
diff --git a/substrate/frame/balances/src/tests_local.rs b/substrate/frame/balances/src/tests_local.rs
index 4b0700748c8..9ff76839f4c 100644
--- a/substrate/frame/balances/src/tests_local.rs
+++ b/substrate/frame/balances/src/tests_local.rs
@@ -21,14 +21,14 @@
 
 use sp_runtime::{
 	Perbill,
-	traits::{ConvertInto, IdentityLookup},
+	traits::IdentityLookup,
 	testing::Header,
 };
 use sp_core::H256;
 use sp_io;
 use frame_support::{impl_outer_origin, parameter_types};
 use frame_support::traits::{Get, StorageMapShim};
-use frame_support::weights::{Weight, DispatchInfo};
+use frame_support::weights::{Weight, DispatchInfo, IdentityFee};
 use std::cell::RefCell;
 use crate::{GenesisConfig, Module, Trait, decl_tests, tests::CallWithDispatchInfo};
 
@@ -87,7 +87,7 @@ impl pallet_transaction_payment::Trait for Test {
 	type Currency = Module<Test>;
 	type OnTransactionPayment = ();
 	type TransactionByteFee = TransactionByteFee;
-	type WeightToFee = ConvertInto;
+	type WeightToFee = IdentityFee<u64>;
 	type FeeMultiplierUpdate = ();
 }
 impl Trait for Test {
diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs
index 4dfaa8035ff..23c0417dac5 100644
--- a/substrate/frame/contracts/src/tests.rs
+++ b/substrate/frame/contracts/src/tests.rs
@@ -37,7 +37,7 @@ use frame_support::{
 	assert_ok, assert_err, assert_err_ignore_postinfo, impl_outer_dispatch, impl_outer_event,
 	impl_outer_origin, parameter_types,
 	storage::child, StorageMap, StorageValue, traits::{Currency, Get},
-	weights::{DispatchInfo, DispatchClass, Weight, PostDispatchInfo, Pays},
+	weights::{DispatchInfo, DispatchClass, Weight, PostDispatchInfo, Pays, IdentityFee},
 };
 use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}};
 use sp_core::storage::well_known_keys;
@@ -152,7 +152,7 @@ impl pallet_transaction_payment::Trait for Test {
 	type Currency = Balances;
 	type OnTransactionPayment = ();
 	type TransactionByteFee = TransactionByteFee;
-	type WeightToFee = Test;
+	type WeightToFee = IdentityFee<BalanceOf<Self>>;
 	type FeeMultiplierUpdate = ();
 }
 
diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs
index 9ac323828d0..f7ac060a6ce 100644
--- a/substrate/frame/executive/src/lib.rs
+++ b/substrate/frame/executive/src/lib.rs
@@ -454,12 +454,12 @@ mod tests {
 	use sp_core::H256;
 	use sp_runtime::{
 		generic::Era, Perbill, DispatchError, testing::{Digest, Header, Block},
-		traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, Convert, ConvertInto},
+		traits::{Header as HeaderT, BlakeTwo256, IdentityLookup},
 		transaction_validity::{InvalidTransaction, UnknownTransaction, TransactionValidityError},
 	};
 	use frame_support::{
 		impl_outer_event, impl_outer_origin, parameter_types, impl_outer_dispatch,
-		weights::{Weight, RuntimeDbWeight},
+		weights::{Weight, RuntimeDbWeight, IdentityFee, WeightToFeePolynomial},
 		traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason},
 	};
 	use frame_system::{self as system, Call as SystemCall, ChainContext, LastRuntimeUpgradeInfo};
@@ -589,7 +589,7 @@ mod tests {
 		type Currency = Balances;
 		type OnTransactionPayment = ();
 		type TransactionByteFee = TransactionByteFee;
-		type WeightToFee = ConvertInto;
+		type WeightToFee = IdentityFee<Balance>;
 		type FeeMultiplierUpdate = ();
 	}
 	impl custom::Trait for Runtime {}
@@ -675,7 +675,8 @@ mod tests {
 		}.assimilate_storage(&mut t).unwrap();
 		let xt = TestXt::new(Call::Balances(BalancesCall::transfer(2, 69)), sign_extra(1, 0, 0));
 		let weight = xt.get_dispatch_info().weight + <Runtime as frame_system::Trait>::ExtrinsicBaseWeight::get();
-		let fee: Balance = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::convert(weight);
+		let fee: Balance
+			= <Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&weight);
 		let mut t = sp_io::TestExternalities::new(t);
 		t.execute_with(|| {
 			Executive::initialize_block(&Header::new(
@@ -871,7 +872,8 @@ mod tests {
 				);
 				let weight = xt.get_dispatch_info().weight
 					+ <Runtime as frame_system::Trait>::ExtrinsicBaseWeight::get();
-				let fee: Balance = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::convert(weight);
+				let fee: Balance =
+					<Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&weight);
 				Executive::initialize_block(&Header::new(
 					1,
 					H256::default(),
diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml
index 3213f0dfe4f..61363be2df6 100644
--- a/substrate/frame/support/Cargo.toml
+++ b/substrate/frame/support/Cargo.toml
@@ -29,6 +29,7 @@ once_cell = { version = "1", default-features = false, optional = true }
 sp-state-machine = { version = "0.8.0-dev", optional = true, path = "../../primitives/state-machine" }
 bitmask = { version = "0.5.0", default-features = false }
 impl-trait-for-tuples = "0.1.3"
+smallvec = "1.4.0"
 
 [dev-dependencies]
 pretty_assertions = "0.6.1"
diff --git a/substrate/frame/support/src/weights.rs b/substrate/frame/support/src/weights.rs
index a0d150cbe93..771f908ecf7 100644
--- a/substrate/frame/support/src/weights.rs
+++ b/substrate/frame/support/src/weights.rs
@@ -135,6 +135,9 @@ use sp_runtime::{
 	generic::{CheckedExtrinsic, UncheckedExtrinsic},
 };
 use crate::dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, DispatchError};
+use sp_runtime::traits::SaturatedConversion;
+use sp_arithmetic::{Perbill, traits::{BaseArithmetic, Saturating}};
+use smallvec::{smallvec, SmallVec};
 
 /// Re-export priority as type
 pub use sp_runtime::transaction_validity::TransactionPriority;
@@ -530,6 +533,90 @@ impl RuntimeDbWeight {
 	}
 }
 
+/// One coefficient and its position in the `WeightToFeePolynomial`.
+///
+/// One term of polynomial is calculated as:
+///
+/// ```ignore
+/// coeff_integer * x^(degree) + coeff_frac * x^(degree)
+/// ```
+///
+/// The `negative` value encodes whether the term is added or substracted from the
+/// overall polynomial result.
+#[derive(Clone, Encode, Decode)]
+pub struct WeightToFeeCoefficient<Balance> {
+	/// The integral part of the coefficient.
+	pub coeff_integer: Balance,
+	/// The fractional part of the coefficient.
+	pub coeff_frac: Perbill,
+	/// True iff the coefficient should be interpreted as negative.
+	pub negative: bool,
+	/// Degree/exponent of the term.
+	pub degree: u8,
+}
+
+/// A list of coefficients that represent one polynomial.
+pub type WeightToFeeCoefficients<T> = SmallVec<[WeightToFeeCoefficient<T>; 4]>;
+
+/// A trait that describes the weight to fee calculation as polynomial.
+///
+/// An implementor should only implement the `polynomial` function.
+pub trait WeightToFeePolynomial {
+	/// The type that is returned as result from polynomial evaluation.
+	type Balance: BaseArithmetic + From<u32> + Copy;
+
+	/// Returns a polynomial that describes the weight to fee conversion.
+	///
+	/// This is the only function that should be manually implemented. Please note
+	/// that all calculation is done in the probably unsigned `Balance` type. This means
+	/// that the order of coefficients is important as putting the negative coefficients
+	/// first will most likely saturate the result to zero mid evaluation.
+	fn polynomial() -> WeightToFeeCoefficients<Self::Balance>;
+
+	/// Calculates the fee from the passed `weight` according to the `polynomial`.
+	///
+	/// This should not be overriden in most circumstances. Calculation is done in the
+	/// `Balance` type and never overflows. All evaluation is saturating.
+	fn calc(weight: &Weight) -> Self::Balance {
+		Self::polynomial().iter().fold(Self::Balance::saturated_from(0u32), |mut acc, args| {
+			let w = Self::Balance::saturated_from(*weight).saturating_pow(args.degree.into());
+
+			// The sum could get negative. Therefore we only sum with the accumulator. 
+			// The Perbill Mul implementation is non overflowing.
+			let frac = args.coeff_frac * w;
+			let integer = args.coeff_integer.saturating_mul(w);
+
+			if args.negative {
+				acc = acc.saturating_sub(frac);
+				acc = acc.saturating_sub(integer);
+			} else {
+				acc = acc.saturating_add(frac);
+				acc = acc.saturating_add(integer);
+			}
+
+			acc
+		})
+	}
+}
+
+/// Implementor of `WeightToFeePolynomial` that maps one unit of weight to one unit of fee.
+pub struct IdentityFee<T>(sp_std::marker::PhantomData<T>);
+
+impl<T> WeightToFeePolynomial for IdentityFee<T> where
+	T: BaseArithmetic + From<u32> + Copy
+{
+	type Balance = T;
+
+	fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
+		smallvec!(WeightToFeeCoefficient {
+			coeff_integer: 1u32.into(),
+			coeff_frac: Perbill::zero(),
+			negative: false,
+			degree: 1,
+		})
+	}
+}
+
 #[cfg(test)]
 #[allow(dead_code)]
 mod tests {
@@ -651,4 +738,64 @@ mod tests {
 			1000
 		);
 	}
+
+	type Balance = u64;
+
+	// 0.5x^3 + 2.333x2 + 7x - 10_000
+	struct Poly;
+	impl WeightToFeePolynomial for Poly {
+		type Balance = Balance;
+
+		fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
+			smallvec![
+				WeightToFeeCoefficient {
+					coeff_integer: 0,
+					coeff_frac: Perbill::from_fraction(0.5),
+					negative: false,
+					degree: 3
+				},
+				WeightToFeeCoefficient {
+					coeff_integer: 2,
+					coeff_frac: Perbill::from_rational_approximation(1u32, 3u32),
+					negative: false,
+					degree: 2
+				},
+				WeightToFeeCoefficient {
+					coeff_integer: 7,
+					coeff_frac: Perbill::zero(),
+					negative: false,
+					degree: 1
+				},
+				WeightToFeeCoefficient {
+					coeff_integer: 10_000,
+					coeff_frac: Perbill::zero(),
+					negative: true,
+					degree: 0
+				},
+			]
+		}
+	}
+
+	#[test]
+	fn polynomial_works() {
+		assert_eq!(Poly::calc(&100), 514033);
+		assert_eq!(Poly::calc(&10_123), 518917034928);
+	}
+
+	#[test]
+	fn polynomial_does_not_underflow() {
+		assert_eq!(Poly::calc(&0), 0);
+	}
+
+	#[test]
+	fn polynomial_does_not_overflow() {
+		assert_eq!(Poly::calc(&Weight::max_value()), Balance::max_value() - 10_000);
+	}
+
+	#[test]
+	fn identity_fee_works() {
+		assert_eq!(IdentityFee::<Balance>::calc(&0), 0);
+		assert_eq!(IdentityFee::<Balance>::calc(&50), 50);
+		assert_eq!(IdentityFee::<Balance>::calc(&Weight::max_value()), Balance::max_value());
+	}
 }
diff --git a/substrate/frame/transaction-payment/Cargo.toml b/substrate/frame/transaction-payment/Cargo.toml
index 0f8b05c3ddd..56efe907f5e 100644
--- a/substrate/frame/transaction-payment/Cargo.toml
+++ b/substrate/frame/transaction-payment/Cargo.toml
@@ -18,6 +18,7 @@ sp-runtime = { version = "2.0.0-dev", default-features = false, path = "../../pr
 frame-support = { version = "2.0.0-dev", default-features = false, path = "../support" }
 frame-system = { version = "2.0.0-dev", default-features = false, path = "../system" }
 pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0-dev", default-features = false, path = "./rpc/runtime-api" }
+smallvec = "1.4.0"
 
 [dev-dependencies]
 sp-io = { version = "2.0.0-dev", path = "../../primitives/io" }
diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs
index 17fe11db6c4..71ef8a56c2f 100644
--- a/substrate/frame/transaction-payment/src/lib.rs
+++ b/substrate/frame/transaction-payment/src/lib.rs
@@ -37,7 +37,10 @@ use codec::{Encode, Decode};
 use frame_support::{
 	decl_storage, decl_module,
 	traits::{Currency, Get, OnUnbalanced, ExistenceRequirement, WithdrawReason, Imbalance},
-	weights::{Weight, DispatchInfo, PostDispatchInfo, GetDispatchInfo, Pays},
+	weights::{
+		Weight, DispatchInfo, PostDispatchInfo, GetDispatchInfo, Pays, WeightToFeePolynomial,
+		WeightToFeeCoefficient,
+	},
 	dispatch::DispatchResult,
 };
 use sp_runtime::{
@@ -72,7 +75,7 @@ pub trait Trait: frame_system::Trait {
 	type TransactionByteFee: Get<BalanceOf<Self>>;
 
 	/// Convert a weight value into a deductible fee based on the currency type.
-	type WeightToFee: Convert<Weight, BalanceOf<Self>>;
+	type WeightToFee: WeightToFeePolynomial<Balance=BalanceOf<Self>>;
 
 	/// Update the multiplier of the next block, based on the previous block's weight.
 	type FeeMultiplierUpdate: Convert<Multiplier, Multiplier>;
@@ -89,9 +92,13 @@ decl_module! {
 		/// The fee to be paid for making a transaction; the per-byte portion.
 		const TransactionByteFee: BalanceOf<T> = T::TransactionByteFee::get();
 
+		/// The polynomial that is applied in order to derive fee from weight.
+		const WeightToFee: Vec<WeightToFeeCoefficient<BalanceOf<T>>> =
+			T::WeightToFee::polynomial().to_vec();
+
 		fn on_finalize() {
 			NextFeeMultiplier::mutate(|fm| {
-				*fm = T::FeeMultiplierUpdate::convert(*fm)
+				*fm = T::FeeMultiplierUpdate::convert(*fm);
 			});
 		}
 	}
@@ -183,7 +190,7 @@ impl<T: Trait> Module<T> {
 		// cap the weight to the maximum defined in runtime, otherwise it will be the
 		// `Bounded` maximum of its data type, which is not desired.
 		let capped_weight = weight.min(<T as frame_system::Trait>::MaximumBlockWeight::get());
-		T::WeightToFee::convert(capped_weight)
+		T::WeightToFee::calc(&capped_weight)
 	}
 }
 
@@ -318,7 +325,10 @@ mod tests {
 	use codec::Encode;
 	use frame_support::{
 		impl_outer_dispatch, impl_outer_origin, parameter_types,
-		weights::{DispatchClass, DispatchInfo, PostDispatchInfo, GetDispatchInfo, Weight},
+		weights::{
+			DispatchClass, DispatchInfo, PostDispatchInfo, GetDispatchInfo, Weight,
+			WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient,
+		},
 	};
 	use pallet_balances::Call as BalancesCall;
 	use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
@@ -329,6 +339,7 @@ mod tests {
 		Perbill,
 	};
 	use std::cell::RefCell;
+	use smallvec::smallvec;
 
 	const CALL: &<Runtime as frame_system::Trait>::Call =
 		&Call::Balances(BalancesCall::transfer(2, 69));
@@ -411,10 +422,17 @@ mod tests {
 		fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) }
 	}
 
-	pub struct WeightToFee(u64);
-	impl Convert<Weight, u64> for WeightToFee {
-		fn convert(t: Weight) -> u64 {
-			WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64))
+	pub struct WeightToFee;
+	impl WeightToFeePolynomial for WeightToFee {
+		type Balance = u64;
+
+		fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
+			smallvec![WeightToFeeCoefficient {
+				degree: 1,
+				coeff_frac: Perbill::zero(),
+				coeff_integer: WEIGHT_TO_FEE.with(|v| *v.borrow()),
+				negative: false,
+			}]
 		}
 	}
 
-- 
GitLab