git.delta.rocks / unique-network / refs/commits / 17e188aac633

difftreelog

Merge pull request #577 from UniqueNetwork/fix/fee-multiplier-update

Yaroslav Bolyukin2022-12-19parents: #49182bd #bea7716.patch.diff
in: master

16 files changed

modifiedpallets/configuration/src/lib.rsdiffbeforeafterboth
before · pallets/configuration/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use core::marker::PhantomData;2021use frame_support::{22	pallet,23	weights::{WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient, Weight},24	traits::Get,25};26use parity_scale_codec::{Decode, Encode, MaxEncodedLen};27use scale_info::TypeInfo;28use sp_arithmetic::traits::{BaseArithmetic, Unsigned};29use smallvec::smallvec;3031pub use pallet::*;32use sp_core::U256;33use sp_runtime::Perbill;3435#[pallet]36mod pallet {37	use super::*;38	use frame_support::{39		traits::Get,40		pallet_prelude::{StorageValue, ValueQuery, DispatchResult, OptionQuery},41		BoundedVec,42	};43	use frame_system::{pallet_prelude::OriginFor, ensure_root};44	use xcm::v1::MultiLocation;4546	#[pallet::config]47	pub trait Config: frame_system::Config {48		#[pallet::constant]49		type DefaultWeightToFeeCoefficient: Get<u32>;5051		#[pallet::constant]52		type DefaultMinGasPrice: Get<u64>;5354		#[pallet::constant]55		type MaxXcmAllowedLocations: Get<u32>;56		#[pallet::constant]57		type AppPromotionDailyRate: Get<Perbill>;58		#[pallet::constant]59		type DayRelayBlocks: Get<Self::BlockNumber>;60	}6162	#[pallet::error]63	pub enum Error<T> {64		InconsistentConfiguration,65	}6667	#[pallet::storage]68	pub type WeightToFeeCoefficientOverride<T: Config> = StorageValue<69		Value = u32,70		QueryKind = ValueQuery,71		OnEmpty = T::DefaultWeightToFeeCoefficient,72	>;7374	#[pallet::storage]75	pub type MinGasPriceOverride<T: Config> =76		StorageValue<Value = u64, QueryKind = ValueQuery, OnEmpty = T::DefaultMinGasPrice>;7778	#[pallet::storage]79	pub type XcmAllowedLocationsOverride<T: Config> = StorageValue<80		Value = BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>,81		QueryKind = OptionQuery,82	>;8384	#[pallet::storage]85	pub type AppPromomotionConfigurationOverride<T: Config> =86		StorageValue<Value = AppPromotionConfiguration<T::BlockNumber>, QueryKind = ValueQuery>;8788	#[pallet::call]89	impl<T: Config> Pallet<T> {90		#[pallet::weight(T::DbWeight::get().writes(1))]91		pub fn set_weight_to_fee_coefficient_override(92			origin: OriginFor<T>,93			coeff: Option<u32>,94		) -> DispatchResult {95			ensure_root(origin)?;96			if let Some(coeff) = coeff {97				<WeightToFeeCoefficientOverride<T>>::set(coeff);98			} else {99				<WeightToFeeCoefficientOverride<T>>::kill();100			}101			Ok(())102		}103104		#[pallet::weight(T::DbWeight::get().writes(1))]105		pub fn set_min_gas_price_override(106			origin: OriginFor<T>,107			coeff: Option<u64>,108		) -> DispatchResult {109			ensure_root(origin)?;110			if let Some(coeff) = coeff {111				<MinGasPriceOverride<T>>::set(coeff);112			} else {113				<MinGasPriceOverride<T>>::kill();114			}115			Ok(())116		}117118		#[pallet::weight(T::DbWeight::get().writes(1))]119		pub fn set_xcm_allowed_locations(120			origin: OriginFor<T>,121			locations: Option<BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>>,122		) -> DispatchResult {123			ensure_root(origin)?;124			<XcmAllowedLocationsOverride<T>>::set(locations);125			Ok(())126		}127128		#[pallet::weight(T::DbWeight::get().writes(1))]129		pub fn set_app_promotion_configuration_override(130			origin: OriginFor<T>,131			mut configuration: AppPromotionConfiguration<T::BlockNumber>,132		) -> DispatchResult {133			ensure_root(origin)?;134			if configuration.interval_income.is_some() {135				return Err(<Error<T>>::InconsistentConfiguration.into());136			}137138			configuration.interval_income = configuration.recalculation_interval.map(|b| {139				Perbill::from_rational(b, T::DayRelayBlocks::get())140					* T::AppPromotionDailyRate::get()141			});142143			<AppPromomotionConfigurationOverride<T>>::set(configuration);144145			Ok(())146		}147	}148149	#[pallet::pallet]150	#[pallet::generate_store(pub(super) trait Store)]151	pub struct Pallet<T>(_);152}153154pub struct WeightToFee<T, B>(PhantomData<(T, B)>);155156impl<T, B> WeightToFeePolynomial for WeightToFee<T, B>157where158	T: Config,159	B: BaseArithmetic + From<u32> + Copy + Unsigned,160{161	type Balance = B;162163	fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {164		smallvec!(WeightToFeeCoefficient {165			coeff_integer: <WeightToFeeCoefficientOverride<T>>::get().into(),166			coeff_frac: Perbill::zero(),167			negative: false,168			degree: 1,169		})170	}171}172173pub struct FeeCalculator<T>(PhantomData<T>);174impl<T: Config> fp_evm::FeeCalculator for FeeCalculator<T> {175	fn min_gas_price() -> (U256, Weight) {176		(177			<MinGasPriceOverride<T>>::get().into(),178			T::DbWeight::get().reads(1),179		)180	}181}182183#[derive(Encode, Decode, Clone, Debug, Default, TypeInfo, MaxEncodedLen, PartialEq, PartialOrd)]184pub struct AppPromotionConfiguration<BlockNumber> {185	/// In relay blocks.186	pub recalculation_interval: Option<BlockNumber>,187	/// In parachain blocks.188	pub pending_interval: Option<BlockNumber>,189	/// Value for `RecalculationInterval` based on 0.05% per 24h.190	pub interval_income: Option<Perbill>,191	/// Maximum allowable number of stakers calculated per call of the `app-promotion::PayoutStakers` extrinsic.192	pub max_stakers_per_calculation: Option<u8>,193}
after · pallets/configuration/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use core::marker::PhantomData;2021use frame_support::{22	pallet,23	weights::{WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient, Weight},24	traits::Get,25};26use parity_scale_codec::{Decode, Encode, MaxEncodedLen};27use scale_info::TypeInfo;28use sp_arithmetic::{29	per_things::{Perbill, PerThing},30	traits::{BaseArithmetic, Unsigned},31};32use smallvec::smallvec;3334pub use pallet::*;35use sp_core::U256;3637#[pallet]38mod pallet {39	use super::*;40	use frame_support::{41		traits::Get,42		pallet_prelude::{StorageValue, ValueQuery, DispatchResult, OptionQuery},43		BoundedVec,44	};45	use frame_system::{pallet_prelude::OriginFor, ensure_root};46	use xcm::v1::MultiLocation;4748	#[pallet::config]49	pub trait Config: frame_system::Config {50		#[pallet::constant]51		type DefaultWeightToFeeCoefficient: Get<u64>;52		#[pallet::constant]53		type DefaultMinGasPrice: Get<u64>;5455		#[pallet::constant]56		type MaxXcmAllowedLocations: Get<u32>;57		#[pallet::constant]58		type AppPromotionDailyRate: Get<Perbill>;59		#[pallet::constant]60		type DayRelayBlocks: Get<Self::BlockNumber>;61	}6263	#[pallet::error]64	pub enum Error<T> {65		InconsistentConfiguration,66	}6768	#[pallet::storage]69	pub type WeightToFeeCoefficientOverride<T: Config> = StorageValue<70		Value = u64,71		QueryKind = ValueQuery,72		OnEmpty = T::DefaultWeightToFeeCoefficient,73	>;7475	#[pallet::storage]76	pub type MinGasPriceOverride<T: Config> =77		StorageValue<Value = u64, QueryKind = ValueQuery, OnEmpty = T::DefaultMinGasPrice>;7879	#[pallet::storage]80	pub type XcmAllowedLocationsOverride<T: Config> = StorageValue<81		Value = BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>,82		QueryKind = OptionQuery,83	>;8485	#[pallet::storage]86	pub type AppPromomotionConfigurationOverride<T: Config> =87		StorageValue<Value = AppPromotionConfiguration<T::BlockNumber>, QueryKind = ValueQuery>;8889	#[pallet::call]90	impl<T: Config> Pallet<T> {91		#[pallet::weight(T::DbWeight::get().writes(1))]92		pub fn set_weight_to_fee_coefficient_override(93			origin: OriginFor<T>,94			coeff: Option<u64>,95		) -> DispatchResult {96			ensure_root(origin)?;97			if let Some(coeff) = coeff {98				<WeightToFeeCoefficientOverride<T>>::set(coeff);99			} else {100				<WeightToFeeCoefficientOverride<T>>::kill();101			}102			Ok(())103		}104105		#[pallet::weight(T::DbWeight::get().writes(1))]106		pub fn set_min_gas_price_override(107			origin: OriginFor<T>,108			coeff: Option<u64>,109		) -> DispatchResult {110			ensure_root(origin)?;111			if let Some(coeff) = coeff {112				<MinGasPriceOverride<T>>::set(coeff);113			} else {114				<MinGasPriceOverride<T>>::kill();115			}116			Ok(())117		}118119		#[pallet::weight(T::DbWeight::get().writes(1))]120		pub fn set_xcm_allowed_locations(121			origin: OriginFor<T>,122			locations: Option<BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>>,123		) -> DispatchResult {124			ensure_root(origin)?;125			<XcmAllowedLocationsOverride<T>>::set(locations);126			Ok(())127		}128129		#[pallet::weight(T::DbWeight::get().writes(1))]130		pub fn set_app_promotion_configuration_override(131			origin: OriginFor<T>,132			mut configuration: AppPromotionConfiguration<T::BlockNumber>,133		) -> DispatchResult {134			ensure_root(origin)?;135			if configuration.interval_income.is_some() {136				return Err(<Error<T>>::InconsistentConfiguration.into());137			}138139			configuration.interval_income = configuration.recalculation_interval.map(|b| {140				Perbill::from_rational(b, T::DayRelayBlocks::get())141					* T::AppPromotionDailyRate::get()142			});143144			<AppPromomotionConfigurationOverride<T>>::set(configuration);145146			Ok(())147		}148	}149150	#[pallet::pallet]151	#[pallet::generate_store(pub(super) trait Store)]152	pub struct Pallet<T>(_);153}154155pub struct WeightToFee<T, B>(PhantomData<(T, B)>);156157impl<T, B> WeightToFeePolynomial for WeightToFee<T, B>158where159	T: Config,160	B: BaseArithmetic + From<u32> + From<u64> + Copy + Unsigned,161{162	type Balance = B;163164	fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {165		smallvec!(WeightToFeeCoefficient {166			coeff_integer: (<WeightToFeeCoefficientOverride<T>>::get() / Perbill::ACCURACY as u64)167				.into(),168			coeff_frac: Perbill::from_parts(169				(<WeightToFeeCoefficientOverride<T>>::get() % Perbill::ACCURACY as u64) as u32170			),171			negative: false,172			degree: 1,173		})174	}175}176177pub struct FeeCalculator<T>(PhantomData<T>);178impl<T: Config> fp_evm::FeeCalculator for FeeCalculator<T> {179	fn min_gas_price() -> (U256, Weight) {180		(181			<MinGasPriceOverride<T>>::get().into(),182			T::DbWeight::get().reads(1),183		)184	}185}186187#[derive(Encode, Decode, Clone, Debug, Default, TypeInfo, MaxEncodedLen, PartialEq, PartialOrd)]188pub struct AppPromotionConfiguration<BlockNumber> {189	/// In relay blocks.190	pub recalculation_interval: Option<BlockNumber>,191	/// In parachain blocks.192	pub pending_interval: Option<BlockNumber>,193	/// Value for `RecalculationInterval` based on 0.05% per 24h.194	pub interval_income: Option<Perbill>,195	/// Maximum allowable number of stakers calculated per call of the `app-promotion::PayoutStakers` extrinsic.196	pub max_stakers_per_calculation: Option<u8>,197}
modifiedprimitives/common/src/constants.rsdiffbeforeafterboth
--- a/primitives/common/src/constants.rs
+++ b/primitives/common/src/constants.rs
@@ -43,10 +43,10 @@
 pub const UNIQUE: Balance = 100 * CENTIUNIQUE;
 
 // Targeting 0.1 UNQ per transfer
-pub const WEIGHT_TO_FEE_COEFF: u32 = /*<weight2fee>*/175_199_920/*</weight2fee>*/;
+pub const WEIGHT_TO_FEE_COEFF: u64 = /*<weight2fee>*/77_083_524_944_487_510/*</weight2fee>*/;
 
 // Targeting 0.15 UNQ per transfer via ETH
-pub const MIN_GAS_PRICE: u64 = /*<mingasprice>*/1_014_919_410_810/*</mingasprice>*/;
+pub const MIN_GAS_PRICE: u64 = /*<mingasprice>*/1_014_919_313_914/*</mingasprice>*/;
 
 /// We assume that ~10% of the block weight is consumed by `on_initalize` handlers.
 /// This is used to limit the maximal weight of a single extrinsic.
@@ -60,5 +60,5 @@
 	.set_proof_size(MAX_POV_SIZE as u64);
 
 parameter_types! {
-	pub const TransactionByteFee: Balance = 501 * MICROUNIQUE;
+	pub const TransactionByteFee: Balance = 501 * MICROUNIQUE / 2;
 }
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -104,7 +104,7 @@
 	pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;
 }
 impl pallet_configuration::Config for Runtime {
-	type DefaultWeightToFeeCoefficient = ConstU32<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;
+	type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;
 	type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;
 	type MaxXcmAllowedLocations = ConstU32<16>;
 	type AppPromotionDailyRate = AppPromotionDailyRate;
modifiedruntime/common/config/substrate.rsdiffbeforeafterboth
--- a/runtime/common/config/substrate.rs
+++ b/runtime/common/config/substrate.rs
@@ -28,10 +28,12 @@
 	traits::{BlakeTwo256, AccountIdLookup},
 	Perbill, Permill, Percent,
 };
+use sp_arithmetic::traits::One;
 use frame_system::{
 	limits::{BlockLength, BlockWeights},
 	EnsureRoot,
 };
+use pallet_transaction_payment::{Multiplier, ConstFeeMultiplier};
 use crate::{
 	runtime_common::DealWithFees, Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, PalletInfo,
 	System, Balances, Treasury, SS58Prefix, Version,
@@ -152,6 +154,8 @@
 	/// This value increases the priority of `Operational` transactions by adding
 	/// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.
 	pub const OperationalFeeMultiplier: u8 = 5;
+
+	pub FeeMultiplier: Multiplier = Multiplier::one();
 }
 
 impl pallet_transaction_payment::Config for Runtime {
@@ -160,7 +164,7 @@
 	type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
 	type OperationalFeeMultiplier = OperationalFeeMultiplier;
 	type WeightToFee = pallet_configuration::WeightToFee<Self, Balance>;
-	type FeeMultiplierUpdate = ();
+	type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
 }
 
 parameter_types! {
modifiedtests/src/calibrate.tsdiffbeforeafterboth
--- a/tests/src/calibrate.ts
+++ b/tests/src/calibrate.ts
@@ -1,59 +1,186 @@
 import {IKeyringPair} from '@polkadot/types/types';
-
 import {usingEthPlaygrounds, EthUniqueHelper} from './eth/util';
 
+class Fract {
+  static ZERO = new Fract(0n);
+  constructor(public readonly a: bigint, public readonly b: bigint = 1n) {
+    if (b === 0n) throw new Error('division by zero');
+    if (b < 0n) throw new Error('missing normalization');
+  }
 
-function linearRegression(points: { x: bigint, y: bigint }[]) {
-  let sumxy = 0n;
-  let sumx = 0n;
-  let sumy = 0n;
-  let sumx2 = 0n;
-  const n = points.length;
-  for (let i = 0; i < n; i++) {
-    const p = points[i];
-    sumxy += p.x * p.y;
-    sumx += p.x;
-    sumy += p.y;
-    sumx2 += p.x * p.x;
+  mul(other: Fract) {
+    return new Fract(this.a * other.a, this.b * other.b).optimize();
   }
 
-  const nb = BigInt(n);
+  div(other: Fract) {
+    return this.mul(other.inv());
+  }
 
-  const a = (nb * sumxy - sumx * sumy) / (nb * sumx2 - sumx * sumx);
-  const b = (sumy - a * sumx) / nb;
+  plus(other: Fract) {
+    if (this.b === other.b) {
+      return new Fract(this.a + other.a, this.b);
+    }
+    return new Fract(this.a * other.b + other.a * this.b, this.b * other.b).optimize();
+  }
 
-  return {a, b};
-}
+  minus(other: Fract) {
+    return this.plus(other.neg());
+  }
 
-// JS has no builtin function to calculate sqrt of bigint
-// https://stackoverflow.com/a/53684036/6190169
-function sqrt(value: bigint) {
-  if (value < 0n) {
-    throw 'square root of negative numbers is not supported';
+  neg() {
+    return new Fract(-this.a, this.b);
+  }
+  inv() {
+    if (this.a < 0) {
+      return new Fract(-this.b, -this.a);
+    } else {
+      return new Fract(this.b, this.a);
+    }
   }
 
-  if (value < 2n) {
-    return value;
+  optimize() {
+    function gcd(x: bigint, y: bigint) {
+      if (x < 0n)
+        x = -x;
+      if (y < 0n)
+        y = -y;
+      while(y) {
+        const t = y;
+        y = x % y;
+        x = t;
+      }
+      return x;
+    }
+    const v = gcd(this.a, this.b);
+    return new Fract(this.a / v, this.b / v);
+  }
+
+  toBigInt() {
+    return this.a / this.b;
+  }
+  toNumber() {
+    const v = this.optimize();
+    return Number(v.a) / Number(v.b);
+  }
+  toString() {
+    const v = this.optimize();
+    return `${v.a} / ${v.b}`;
   }
 
-  function newtonIteration(n: bigint, x0: bigint): bigint {
-    const x1 = ((n / x0) + x0) >> 1n;
-    if (x0 === x1 || x0 === (x1 - 1n)) {
-      return x0;
+  lt(other: Fract) {
+    return this.a * other.b < other.a * this.b;
+  }
+  eq(other: Fract) {
+    return this.a * other.b === other.a * this.b;
+  }
+
+  sqrt() {
+    if (this.a < 0n) {
+      throw 'square root of negative numbers is not supported';
+    }
+
+    if (this.lt(new Fract(2n))) {
+      return this;
+    }
+
+    function newtonIteration(n: Fract, x0: Fract): Fract {
+      const x1 = rpn(n, x0, '/', x0, '+', new Fract(2n), '/');
+      if (x0.eq(x1) || x0.eq(x1.minus(new Fract(1n)))) {
+        return x0;
+      }
+      return newtonIteration(n, x1);
     }
-    return newtonIteration(n, x1);
+
+    return newtonIteration(this, new Fract(1n));
   }
+}
 
-  return newtonIteration(value, 1n);
+type Op = Fract | '+' | '-' | '*' | '/' | 'dup' | Op[];
+function rpn(...ops: (Op)[]) {
+  const stack: Fract[] = [];
+  for (const op of ops) {
+    if (op instanceof Fract) {
+      stack.push(op);
+    } else if (op === '+') {
+      if (stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.plus(b));
+    } else if (op === '*') {
+      if (stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.mul(b));
+    } else if (op === '-') {
+      if (stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.minus(b));
+    } else if (op === '/') {
+      if (stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.div(b));
+    } else if (op === 'dup') {
+      if (stack.length < 1)
+        throw new Error('stack underflow');
+      const a = stack.pop()!;
+      stack.push(a);
+      stack.push(a);
+    } else if (Array.isArray(op)) {
+      stack.push(rpn(...op));
+    } else {
+      throw new Error(`unknown operand: ${op}`);
+    }
+  }
+  if (stack.length != 1)
+    throw new Error('one element should be left on stack');
+  return stack[0]!;
+}
+
+function linearRegression(points: { x: Fract, y: Fract }[]) {
+  let sumxy = Fract.ZERO;
+  let sumx = Fract.ZERO;
+  let sumy = Fract.ZERO;
+  let sumx2 = Fract.ZERO;
+  const n = points.length;
+  for (let i = 0; i < n; i++) {
+    const p = points[i];
+    sumxy = rpn(p.x, p.y, '*', sumxy, '+');
+    sumx = sumx.plus(p.x);
+    sumy = sumy.plus(p.y);
+    sumx2 = rpn(p.x, p.x, '*', sumx2, '+');
+  }
+
+  const nb = new Fract(BigInt(n));
+
+  const a = rpn(
+    [nb, sumxy, '*', sumx, sumy, '*', '-'],
+    [nb, sumx2, '*', sumx, sumx, '*', '-'],
+    '/',
+  );
+  const b = rpn(
+    [sumy, a, sumx, '*', '-'],
+    nb,
+    '/',
+  );
+
+  return {a, b};
 }
 
-function _error(points: { x: bigint, y: bigint }[], hypothesis: (a: bigint) => bigint) {
-  return sqrt(points.map(p => {
+const hypothesisLinear = (a: Fract, b: Fract) => (x: Fract) => rpn(x, a, '*', b, '+');
+
+function _error(points: { x: Fract, y: Fract }[], hypothesis: (a: Fract) => Fract) {
+  return points.map(p => {
     const v = hypothesis(p.x);
     const vv = p.y;
 
-    return (v - vv) ** 2n;
-  }).reduce((a, b) => a + b, 0n) / BigInt(points.length));
+    return rpn(v, vv, '-', 'dup', '*');
+  }).reduce((a, b) => a.plus(b), Fract.ZERO).sqrt().div(new Fract(BigInt(points.length)));
 }
 
 async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {
@@ -68,15 +195,15 @@
     await token.transfer(alice, {Substrate: bob.address});
     const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
 
-    console.log(`Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+    console.log(`\t[NFT transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
   }
 
   const api = helper.getApi();
-  const defaultCoeff = (api.consts.configuration.defaultWeightToFeeCoefficient as any).toBigInt();
+  const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();
   for (let i = -5; i < 5; i++) {
-    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(defaultCoeff + defaultCoeff / 1000n * BigInt(i))));
+    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i))));
 
-    const coefficient = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();
+    const coefficient = new Fract((await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt());
     const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
     const token = await collection.mintToken(alice, {Substrate: alice.address});
 
@@ -84,16 +211,18 @@
     await token.transfer(alice, {Substrate: bob.address});
     const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
 
-    const transferPrice = aliceBalanceBefore - aliceBalanceAfter;
+    const transferPrice = new Fract(aliceBalanceBefore - aliceBalanceAfter);
 
     dataPoints.push({x: transferPrice, y: coefficient});
   }
   const {a, b} = linearRegression(dataPoints);
 
-  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);
+  const hyp = hypothesisLinear(a, b);
+  // console.log(`\t[NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`);
 
-  const perfectValue = a * helper.balance.getOneTokenNominal() / 10n + b;
-  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toString())));
+  // 0.1 UNQ
+  const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(1n, 10n), '*'));
+  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toBigInt())));
 
   {
     const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
@@ -102,7 +231,7 @@
     await token.transfer(alice, {Substrate: bob.address});
     const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
 
-    console.log(`Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+    console.log(`\t[NFT transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
   }
 }
 
@@ -121,35 +250,37 @@
 
     const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));
 
-    console.log(`Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+    console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
   }
 
   const api = helper.getApi();
-  const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();
+  // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();
+  const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();
   for (let i = -8; i < 8; i++) {
-    const gasPrice = defaultCoeff + defaultCoeff / 100000n * BigInt(i);
+    const gasPrice = base + base / 100000n * BigInt(i);
     const gasPriceStr = '0x' + gasPrice.toString(16);
     await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice)));
 
-    const coefficient = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();
+    const coefficient = new Fract((await api.query.configuration.minGasPriceOverride() as any).toBigInt());
     const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
     const token = await collection.mintToken(alice, {Ethereum: caller});
 
     const address = helper.ethAddress.fromCollectionId(collection.collectionId);
     const contract = helper.ethNativeContract.collection(address, 'nft', caller);
 
-    const transferPrice = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS}));
+    const transferPrice = new Fract(await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS})));
 
     dataPoints.push({x: transferPrice, y: coefficient});
   }
 
   const {a, b} = linearRegression(dataPoints);
 
-  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);
+  const hyp = hypothesisLinear(a, b);
+  // console.log(`\t[ETH NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`);
 
-  // * 0.15 = * 10000 / 66666
-  const perfectValue = a * helper.balance.getOneTokenNominal() * 1000000n / 6666666n + b;
-  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toString())));
+  // 0.15 UNQ
+  const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(15n, 100n), '*'));
+  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toBigInt())));
 
   {
     const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
@@ -160,18 +291,26 @@
 
     const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));
 
-    console.log(`Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+    console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
   }
 }
 
 (async () => {
   await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
-    // Second run slightly reduces error sometimes, as price line is not actually straight, this is a curve
+    // Subsequent runs reduce error, as price line is not actually straight, this is a curve
+
+    const iterations = 3;
+
+    console.log('[Calibrate WeightToFee]');
+    for (let i = 0; i < iterations; i++) {
+      await calibrateWeightToFee(helper, privateKey);
+    }
 
-    await calibrateWeightToFee(helper, privateKey);
-    await calibrateWeightToFee(helper, privateKey);
+    console.log();
 
-    await calibrateMinGasPrice(helper, privateKey);
-    await calibrateMinGasPrice(helper, privateKey);
+    console.log('[Calibrate MinGasPrice]');
+    for (let i = 0; i < iterations; i++) {
+      await calibrateMinGasPrice(helper, privateKey);
+    }
   });
 })();
addedtests/src/eth/ethFeesAreCorrect.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/ethFeesAreCorrect.test.ts
@@ -0,0 +1,66 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://witww.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {itEth, usingEthPlaygrounds, expect} from './util';
+
+describe('Eth fees are correct', () => {
+  let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({filename: __filename});
+      [minter, alice] = await helper.arrange.createAccounts([100n, 200n], donor);
+    });
+  });
+
+
+  itEth('web3 fees are the same as evm.call fees', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const aliceEth = helper.address.substrateToEth(alice.address);
+
+    const {tokenId: tokenA} = await collection.mintToken(minter, {Ethereum: owner});
+    const {tokenId: tokenB} = await collection.mintToken(minter, {Ethereum: aliceEth});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    const balanceBeforeWeb3Transfer = await helper.balance.getEthereum(owner);
+    await contract.methods.transfer(receiver, tokenA).send({from: owner});
+    const balanceAfterWeb3Transfer = await helper.balance.getEthereum(owner);
+    const web3Diff = balanceBeforeWeb3Transfer - balanceAfterWeb3Transfer;
+
+    const encodedCall = contract.methods.transfer(receiver, tokenB)
+      .encodeABI();
+    
+    const balanceBeforeEvmCall = await helper.balance.getSubstrate(alice.address);
+    await helper.eth.sendEVM(
+      alice,
+      collectionAddress,
+      encodedCall,
+      '0',
+    );
+    const balanceAfterEvmCall = await helper.balance.getSubstrate(alice.address);
+    const evmCallDiff = balanceBeforeEvmCall - balanceAfterEvmCall;
+
+    expect(web3Diff).to.be.equal(evmCallDiff);
+  });
+});
modifiedtests/src/interfaces/augment-api-consts.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-consts.ts
+++ b/tests/src/interfaces/augment-api-consts.ts
@@ -82,7 +82,7 @@
       appPromotionDailyRate: Perbill & AugmentedConst<ApiType>;
       dayRelayBlocks: u32 & AugmentedConst<ApiType>;
       defaultMinGasPrice: u64 & AugmentedConst<ApiType>;
-      defaultWeightToFeeCoefficient: u32 & AugmentedConst<ApiType>;
+      defaultWeightToFeeCoefficient: u64 & AugmentedConst<ApiType>;
       maxXcmAllowedLocations: u32 & AugmentedConst<ApiType>;
       /**
        * Generic const
modifiedtests/src/interfaces/augment-api-query.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -162,7 +162,7 @@
     configuration: {
       appPromomotionConfigurationOverride: AugmentedQuery<ApiType, () => Observable<PalletConfigurationAppPromotionConfiguration>, []> & QueryableStorageEntry<ApiType, []>;
       minGasPriceOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      weightToFeeCoefficientOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      weightToFeeCoefficientOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
       xcmAllowedLocationsOverride: AugmentedQuery<ApiType, () => Observable<Option<Vec<XcmV1MultiLocation>>>, []> & QueryableStorageEntry<ApiType, []>;
       /**
        * Generic query
modifiedtests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -16,7 +16,7 @@
 import type { BlockHash } from '@polkadot/types/interfaces/chain';
 import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate';
 import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
-import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequest } from '@polkadot/types/interfaces/contracts';
+import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequestV1 } from '@polkadot/types/interfaces/contracts';
 import type { BlockStats } from '@polkadot/types/interfaces/dev';
 import type { CreatedBlock } from '@polkadot/types/interfaces/engine';
 import type { EthAccount, EthCallRequest, EthFeeHistory, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth';
@@ -24,7 +24,7 @@
 import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa';
 import type { MmrLeafBatchProof, MmrLeafProof } from '@polkadot/types/interfaces/mmr';
 import type { StorageKind } from '@polkadot/types/interfaces/offchain';
-import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment';
+import type { FeeDetails, RuntimeDispatchInfoV1 } from '@polkadot/types/interfaces/payment';
 import type { RpcMethods } from '@polkadot/types/interfaces/rpc';
 import type { AccountId, AccountId32, BlockNumber, H160, H256, H64, Hash, Header, Index, Justification, KeyValue, SignedBlock, StorageData } from '@polkadot/types/interfaces/runtime';
 import type { MigrationStatusResult, ReadProof, RuntimeVersion, TraceBlockResponse } from '@polkadot/types/interfaces/state';
@@ -174,7 +174,7 @@
        * @deprecated Use the runtime interface `api.call.contractsApi.instantiate` instead
        * Instantiate a new contract
        **/
-      instantiate: AugmentedRpc<(request: InstantiateRequest | { origin?: any; value?: any; gasLimit?: any; storageDepositLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ContractInstantiateResult>>;
+      instantiate: AugmentedRpc<(request: InstantiateRequestV1 | { origin?: any; value?: any; gasLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ContractInstantiateResult>>;
       /**
        * @deprecated Not available in newer versions of the contracts interfaces
        * Returns the projected time a given contract will be able to sustain paying its rent
@@ -426,13 +426,15 @@
     };
     payment: {
       /**
+       * @deprecated Use `api.call.transactionPaymentApi.queryFeeDetails` instead
        * Query the detailed fee of a given encoded extrinsic
        **/
       queryFeeDetails: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<FeeDetails>>;
       /**
+       * @deprecated Use `api.call.transactionPaymentApi.queryInfo` instead
        * Retrieves the fee information for an encoded extrinsic
        **/
-      queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<RuntimeDispatchInfo>>;
+      queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<RuntimeDispatchInfoV1>>;
     };
     rmrk: {
       /**
modifiedtests/src/interfaces/augment-api-runtime.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-runtime.ts
+++ b/tests/src/interfaces/augment-api-runtime.ts
@@ -6,7 +6,7 @@
 import '@polkadot/api-base/types/calls';
 
 import type { ApiTypes, AugmentedCall, DecoratedCallBase } from '@polkadot/api-base/types';
-import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u64 } from '@polkadot/types-codec';
+import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u32, u64 } from '@polkadot/types-codec';
 import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
 import type { CheckInherentsResult, InherentData } from '@polkadot/types/interfaces/blockbuilder';
 import type { BlockHash } from '@polkadot/types/interfaces/chain';
@@ -16,6 +16,7 @@
 import type { EvmAccount, EvmCallInfo, EvmCreateInfo } from '@polkadot/types/interfaces/evm';
 import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
 import type { OpaqueMetadata } from '@polkadot/types/interfaces/metadata';
+import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment';
 import type { AccountId, Block, H160, H256, Header, Index, KeyTypeId, Permill, SlotDuration } from '@polkadot/types/interfaces/runtime';
 import type { RuntimeVersion } from '@polkadot/types/interfaces/state';
 import type { ApplyExtrinsicResult, DispatchError } from '@polkadot/types/interfaces/system';
@@ -228,5 +229,20 @@
        **/
       [key: string]: DecoratedCallBase<ApiType>;
     };
+    /** 0x37c8bb1350a9a2a8/2 */
+    transactionPaymentApi: {
+      /**
+       * The transaction fee details
+       **/
+      queryFeeDetails: AugmentedCall<ApiType, (uxt: Extrinsic | IExtrinsic | string | Uint8Array, len: u32 | AnyNumber | Uint8Array) => Observable<FeeDetails>>;
+      /**
+       * The transaction info
+       **/
+      queryInfo: AugmentedCall<ApiType, (uxt: Extrinsic | IExtrinsic | string | Uint8Array, len: u32 | AnyNumber | Uint8Array) => Observable<RuntimeDispatchInfo>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
   } // AugmentedCalls
 } // declare module
modifiedtests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -217,7 +217,7 @@
     configuration: {
       setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletConfigurationAppPromotionConfiguration]>;
       setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
-      setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
+      setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
       setXcmAllowedLocations: AugmentedSubmittable<(locations: Option<Vec<XcmV1MultiLocation>> | null | Uint8Array | Vec<XcmV1MultiLocation> | (XcmV1MultiLocation | { parents?: any; interior?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Option<Vec<XcmV1MultiLocation>>]>;
       /**
        * Generic tx
@@ -1432,6 +1432,23 @@
        **/
       destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
       /**
+       * Repairs a collection if the data was somehow corrupted.
+       * 
+       * # Arguments
+       * 
+       * * `collection_id`: ID of the collection to repair.
+       **/
+      forceRepairCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * Repairs a token if the data was somehow corrupted.
+       * 
+       * # Arguments
+       * 
+       * * `collection_id`: ID of the collection the item belongs to.
+       * * `item_id`: ID of the item.
+       **/
+      forceRepairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
+      /**
        * Remove admin of a collection.
        * 
        * An admin address can remove itself. List of admins may become empty,
@@ -1474,15 +1491,6 @@
        * * `address`: ID of the address to be removed from the allowlist.
        **/
       removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Repairs a broken item
-       * 
-       * # Arguments
-       * 
-       * * `collection_id`: ID of the collection the item belongs to.
-       * * `item_id`: ID of the item.
-       **/
-      repairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
       /**
        * Re-partition a refungible token, while owning all of its parts/pieces.
        * 
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -24,7 +24,7 @@
 import type { StatementKind } from '@polkadot/types/interfaces/claims';
 import type { CollectiveOrigin, MemberCount, ProposalIndex, Votes, VotesTo230 } from '@polkadot/types/interfaces/collective';
 import type { AuthorityId, RawVRFOutput } from '@polkadot/types/interfaces/consensus';
-import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts';
+import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractExecResultU64, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractInstantiateResultU64, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts';
 import type { ContractConstructorSpecLatest, ContractConstructorSpecV0, ContractConstructorSpecV1, ContractConstructorSpecV2, ContractConstructorSpecV3, ContractContractSpecV0, ContractContractSpecV1, ContractContractSpecV2, ContractContractSpecV3, ContractContractSpecV4, ContractCryptoHasher, ContractDiscriminant, ContractDisplayName, ContractEventParamSpecLatest, ContractEventParamSpecV0, ContractEventParamSpecV2, ContractEventSpecLatest, ContractEventSpecV0, ContractEventSpecV1, ContractEventSpecV2, ContractLayoutArray, ContractLayoutCell, ContractLayoutEnum, ContractLayoutHash, ContractLayoutHashingStrategy, ContractLayoutKey, ContractLayoutStruct, ContractLayoutStructField, ContractMessageParamSpecLatest, ContractMessageParamSpecV0, ContractMessageParamSpecV2, ContractMessageSpecLatest, ContractMessageSpecV0, ContractMessageSpecV1, ContractMessageSpecV2, ContractMetadata, ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1, ContractMetadataV2, ContractMetadataV3, ContractMetadataV4, ContractProject, ContractProjectContract, ContractProjectInfo, ContractProjectSource, ContractProjectV0, ContractSelector, ContractStorageLayout, ContractTypeSpec } from '@polkadot/types/interfaces/contractsAbi';
 import type { FundIndex, FundInfo, LastContribution, TrieIndex } from '@polkadot/types/interfaces/crowdloan';
 import type { CollationInfo, CollationInfoV1, ConfigData, MessageId, OverweightIndex, PageCounter, PageIndexData } from '@polkadot/types/interfaces/cumulus';
@@ -47,7 +47,7 @@
 import type { StorageKind } from '@polkadot/types/interfaces/offchain';
 import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences';
 import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, CoreState, DisputeLocation, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DoubleVoteReport, DownwardMessage, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, GroupRotationInfo, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OccupiedCore, OccupiedCoreAssumption, OldV1SessionInfo, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PersistedValidationData, PvfCheckStatement, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, ScheduledCore, Scheduling, ScrapedOnChainVotes, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains';
-import type { FeeDetails, InclusionFee, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment';
+import type { FeeDetails, InclusionFee, RuntimeDispatchInfo, RuntimeDispatchInfoV1, RuntimeDispatchInfoV2 } from '@polkadot/types/interfaces/payment';
 import type { Approvals } from '@polkadot/types/interfaces/poll';
 import type { ProxyAnnouncement, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces/proxy';
 import type { AccountStatus, AccountValidity } from '@polkadot/types/interfaces/purchase';
@@ -273,10 +273,12 @@
     ContractExecResultTo255: ContractExecResultTo255;
     ContractExecResultTo260: ContractExecResultTo260;
     ContractExecResultTo267: ContractExecResultTo267;
+    ContractExecResultU64: ContractExecResultU64;
     ContractInfo: ContractInfo;
     ContractInstantiateResult: ContractInstantiateResult;
     ContractInstantiateResultTo267: ContractInstantiateResultTo267;
     ContractInstantiateResultTo299: ContractInstantiateResultTo299;
+    ContractInstantiateResultU64: ContractInstantiateResultU64;
     ContractLayoutArray: ContractLayoutArray;
     ContractLayoutCell: ContractLayoutCell;
     ContractLayoutEnum: ContractLayoutEnum;
@@ -1057,6 +1059,8 @@
     RpcMethods: RpcMethods;
     RuntimeDbWeight: RuntimeDbWeight;
     RuntimeDispatchInfo: RuntimeDispatchInfo;
+    RuntimeDispatchInfoV1: RuntimeDispatchInfoV1;
+    RuntimeDispatchInfoV2: RuntimeDispatchInfoV2;
     RuntimeVersion: RuntimeVersion;
     RuntimeVersionApi: RuntimeVersionApi;
     RuntimeVersionPartial: RuntimeVersionPartial;
modifiedtests/src/interfaces/default/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -1303,7 +1303,7 @@
 export interface PalletConfigurationCall extends Enum {
   readonly isSetWeightToFeeCoefficientOverride: boolean;
   readonly asSetWeightToFeeCoefficientOverride: {
-    readonly coeff: Option<u32>;
+    readonly coeff: Option<u64>;
   } & Struct;
   readonly isSetMinGasPriceOverride: boolean;
   readonly asSetMinGasPriceOverride: {
@@ -2319,12 +2319,16 @@
     readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
     readonly approve: bool;
   } & Struct;
-  readonly isRepairItem: boolean;
-  readonly asRepairItem: {
+  readonly isForceRepairCollection: boolean;
+  readonly asForceRepairCollection: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isForceRepairItem: boolean;
+  readonly asForceRepairItem: {
     readonly collectionId: u32;
     readonly itemId: u32;
   } & Struct;
-  readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'RepairItem';
+  readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
 }
 
 /** @name PalletUniqueError */
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -2282,8 +2282,11 @@
         operator: 'PalletEvmAccountBasicCrossAccountIdRepr',
         approve: 'bool',
       },
-      repair_item: {
+      force_repair_collection: {
         collectionId: 'u32',
+      },
+      force_repair_item: {
+        collectionId: 'u32',
         itemId: 'u32'
       }
     }
@@ -2452,7 +2455,7 @@
   PalletConfigurationCall: {
     _enum: {
       set_weight_to_fee_coefficient_override: {
-        coeff: 'Option<u32>',
+        coeff: 'Option<u64>',
       },
       set_min_gas_price_override: {
         coeff: 'Option<u64>',
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -2517,12 +2517,16 @@
       readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
       readonly approve: bool;
     } & Struct;
-    readonly isRepairItem: boolean;
-    readonly asRepairItem: {
+    readonly isForceRepairCollection: boolean;
+    readonly asForceRepairCollection: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isForceRepairItem: boolean;
+    readonly asForceRepairItem: {
       readonly collectionId: u32;
       readonly itemId: u32;
     } & Struct;
-    readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'RepairItem';
+    readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
   }
 
   /** @name UpDataStructsCollectionMode (237) */
@@ -2675,7 +2679,7 @@
   interface PalletConfigurationCall extends Enum {
     readonly isSetWeightToFeeCoefficientOverride: boolean;
     readonly asSetWeightToFeeCoefficientOverride: {
-      readonly coeff: Option<u32>;
+      readonly coeff: Option<u64>;
     } & Struct;
     readonly isSetMinGasPriceOverride: boolean;
     readonly asSetMinGasPriceOverride: {
modifiedtests/src/nesting/collectionProperties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/collectionProperties.test.ts
+++ b/tests/src/nesting/collectionProperties.test.ts
@@ -209,7 +209,7 @@
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
       const donor = await privateKey({filename: __filename});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
+      [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor);
     });
   });