git.delta.rocks / unique-network / refs/commits / d8a735e3ebdb

difftreelog

refactor! decouple pallet-collator-selection from pallet-configuration (#962)

Pavel Orlov2023-06-30parent: #1ecd8fc.patch.diff
in: master
* feat!: decoupling configuration&collators pallets

BREAKING CHANGE: pallet `collator-selection` no longer has a tight coupling with the `configuration` pallet

* refactor(collator-selection): type bounds, tests

* refactor(collator-selection): tests

* add ci step for banchmark tests

* add opsFee tests

---------

15 files changed

modified.github/workflows/yarn-dev.ymldiffbeforeafterboth
--- a/.github/workflows/yarn-dev.yml
+++ b/.github/workflows/yarn-dev.yml
@@ -87,6 +87,18 @@
         run: |
           echo "url is ${{ steps.test-report.outputs.runHtmlUrl }}"
 
+      - name: Run benchmark mintFee tests
+        working-directory: tests
+        run: |
+          yarn install
+          npx ts-node --esm ./src/benchmarks/mintFee/index.ts
+
+      - name: Run benchmark opsFee tests
+        working-directory: tests
+        run: |
+          yarn install
+          npx ts-node --esm ./src/benchmarks/opsFee/index.ts
+
       - name: Stop running containers
         if: always()                   # run this step always
         run: docker-compose -f ".docker/docker-compose.${{ matrix.network }}.yml" down
modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6371,7 +6371,7 @@
 
 [[package]]
 name = "pallet-collator-selection"
-version = "4.0.0"
+version = "5.0.0"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -6380,7 +6380,6 @@
  "pallet-aura",
  "pallet-authorship",
  "pallet-balances",
- "pallet-configuration",
  "pallet-session",
  "pallet-timestamp",
  "parity-scale-codec",
@@ -6435,7 +6434,7 @@
 
 [[package]]
 name = "pallet-configuration"
-version = "0.1.3"
+version = "0.2.0"
 dependencies = [
  "fp-evm",
  "frame-benchmarking",
modifiedpallets/collator-selection/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/collator-selection/CHANGELOG.md
+++ b/pallets/collator-selection/CHANGELOG.md
@@ -4,22 +4,28 @@
 
 <!-- bureaucracy goes here -->
 
+## [5.0.0] - 2023-06-27
+
+### Major change
+
+- The dependency (tight coupling) this pallet on the configuration pallet has been removed.
+
 ## [4.0.0] - 2022-12-29
 
 ### Added
 
 - Entire functionality moved over from cumulus/pallet-collator-selection (v3.0.0). Refactored business logic:
-   - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited. 
-      - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
-      - Active candidates may `offboard`, but they will retain their license.
-      - Deposit is returned, and candidacy possibly removed with `release_license`.
-      - License can be forcibly forfeited and candidacy removed with `force_release_license`.
-   - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
-   - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
-   - Remove `MinCandidates`.
-   - Minimal amount of invulnerables is now 1.
-   - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
-   - `KickThreshold` is made configurable.
-   - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
-   - Naming changes to better reflect the new functionality.
-   - More minor changes, tests, benchmarks, etc.
\ No newline at end of file
+  - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited.
+    - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
+    - Active candidates may `offboard`, but they will retain their license.
+    - Deposit is returned, and candidacy possibly removed with `release_license`.
+    - License can be forcibly forfeited and candidacy removed with `force_release_license`.
+  - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
+  - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
+  - Remove `MinCandidates`.
+  - Minimal amount of invulnerables is now 1.
+  - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
+  - `KickThreshold` is made configurable.
+  - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
+  - Naming changes to better reflect the new functionality.
+  - More minor changes, tests, benchmarks, etc.
modifiedpallets/collator-selection/Cargo.tomldiffbeforeafterboth
--- a/pallets/collator-selection/Cargo.toml
+++ b/pallets/collator-selection/Cargo.toml
@@ -6,7 +6,7 @@
 license = "GPLv3"
 name = "pallet-collator-selection"
 repository = "https://github.com/UniqueNetwork/unique-chain"
-version = "4.0.0"
+version = "5.0.0"
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
@@ -23,7 +23,6 @@
 frame-support = { workspace = true }
 frame-system = { workspace = true }
 pallet-authorship = { workspace = true }
-pallet-configuration = { workspace = true }
 pallet-session = { workspace = true }
 sp-runtime = { workspace = true }
 sp-staking = { workspace = true }
@@ -55,8 +54,6 @@
 	"frame-system/std",
 	"log/std",
 	"pallet-authorship/std",
-	'pallet-aura/std',
-	'pallet-balances/std',
 	"pallet-session/std",
 	"rand/std",
 	"scale-info/std",
@@ -64,6 +61,8 @@
 	"sp-runtime/std",
 	"sp-staking/std",
 	"sp-std/std",
+	'pallet-aura/std',
+	'pallet-balances/std',
 ]
 
 try-runtime = ["frame-support/try-runtime"]
modifiedpallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/benchmarking.rs
+++ b/pallets/collator-selection/src/benchmarking.rs
@@ -35,7 +35,7 @@
 use super::*;
 
 #[allow(unused)]
-use crate::Pallet as CollatorSelection;
+use crate::{Pallet as CollatorSelection, BalanceOf};
 use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
 use frame_support::{
 	assert_ok,
@@ -49,11 +49,6 @@
 use frame_system::{EventRecord, RawOrigin};
 use pallet_authorship::EventHandler;
 use pallet_session::{self as session, SessionManager};
-use pallet_configuration::{
-	self as configuration, BalanceOf,
-	CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
-	CollatorSelectionLicenseBondOverride as LicenseBond,
-};
 use sp_std::prelude::*;
 
 const SEED: u32 = 0;
@@ -117,7 +112,7 @@
 	validators.into_iter().map(|(who, _)| who).collect()
 }
 
-fn register_invulnerables<T: Config + configuration::Config>(count: u32) {
+fn register_invulnerables<T: Config>(count: u32) {
 	let candidates = (0..count)
 		.map(|c| account("candidate", c, SEED))
 		.collect::<Vec<_>>();
@@ -131,33 +126,27 @@
 	}
 }
 
-fn register_candidates<T: Config + configuration::Config>(count: u32) {
+fn register_candidates<T: Config>(count: u32) {
 	let candidates = (0..count)
 		.map(|c| account("candidate", c, SEED))
 		.collect::<Vec<_>>();
-	assert!(
-		<LicenseBond<T>>::get() > 0u32.into(),
-		"Bond cannot be zero!"
-	);
+	assert!(T::LicenseBond::get() > 0u32.into(), "Bond cannot be zero!");
 
 	for who in candidates {
-		T::Currency::set_balance(&who, <LicenseBond<T>>::get() * 2u32.into());
+		T::Currency::set_balance(&who, T::LicenseBond::get() * 2u32.into());
 		<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
 		<CollatorSelection<T>>::onboard(RawOrigin::Signed(who).into()).unwrap();
 	}
 }
 
-fn get_licenses<T: Config + configuration::Config>(count: u32) {
+fn get_licenses<T: Config>(count: u32) {
 	let candidates = (0..count)
 		.map(|c| account("candidate", c, SEED))
 		.collect::<Vec<_>>();
-	assert!(
-		<LicenseBond<T>>::get() > 0u32.into(),
-		"Bond cannot be zero!"
-	);
+	assert!(T::LicenseBond::get() > 0u32.into(), "Bond cannot be zero!");
 
 	for who in candidates {
-		T::Currency::set_balance(&who, <LicenseBond<T>>::get() * 2u32.into());
+		T::Currency::set_balance(&who, T::LicenseBond::get() * 2u32.into());
 		<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
 	}
 }
@@ -165,7 +154,7 @@
 /// `Currency::minimum_balance` was used originally, but in unique-chain, we have
 /// zero existential deposit, thus triggering zero bond assertion.
 fn balance_unit<T: Config>() -> BalanceOf<T> {
-	200u32.into()
+	T::LicenseBond::get()
 }
 
 /// Our benchmarking environment already has invulnerables registered.
@@ -173,7 +162,7 @@
 
 benchmarks! {
 	where_clause { where
-		T: pallet_authorship::Config + session::Config + configuration::Config
+		T: Config + pallet_authorship::Config + session::Config
 	}
 
 	// todo:collator this and all the following do not work for some reason, going all the way up to 10 in length
@@ -188,7 +177,7 @@
 
 		let new_invulnerable: T::AccountId = whitelisted_caller();
 		let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
-		T::Currency::set_balance(&new_invulnerable, bond.clone());
+		<T as Config>::Currency::set_balance(&new_invulnerable, bond);
 
 		<session::Pallet<T>>::set_keys(
 			RawOrigin::Signed(new_invulnerable.clone()).into(),
@@ -225,15 +214,13 @@
 
 	get_license {
 		let c in 1 .. T::MaxCollators::get() - 1;
-
-		<LicenseBond<T>>::put(balance_unit::<T>());
 
 		register_validators::<T>(c);
 		get_licenses::<T>(c);
 
 		let caller: T::AccountId = whitelisted_caller();
 		let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
-		T::Currency::set_balance(&caller, bond.clone());
+		T::Currency::set_balance(&caller, bond);
 
 		<session::Pallet<T>>::set_keys(
 			RawOrigin::Signed(caller.clone()).into(),
@@ -251,15 +238,12 @@
 	onboard {
 		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES - 1;
 
-		<LicenseBond<T>>::put(balance_unit::<T>());
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES + 1);
-
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
 
 		let caller: T::AccountId = whitelisted_caller();
 		let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
-		T::Currency::set_balance(&caller, bond.clone());
+		T::Currency::set_balance(&caller, bond);
 
 		let origin = RawOrigin::Signed(caller.clone());
 
@@ -279,9 +263,7 @@
 
 	// worst case is the last candidate leaving.
 	offboard {
-		let c in 1 .. T::MaxCollators::get();
-		<LicenseBond<T>>::put(balance_unit::<T>());
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
@@ -295,10 +277,8 @@
 
 	// worst case is the last candidate leaving.
 	release_license {
-		let c in 1 .. T::MaxCollators::get();
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 		let bond = balance_unit::<T>();
-		<LicenseBond<T>>::put(bond);
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
 
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
@@ -312,10 +292,8 @@
 
 	// worst case is the last candidate leaving.
 	force_release_license {
-		let c in 1 .. T::MaxCollators::get();
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 		let bond = balance_unit::<T>();
-		<LicenseBond<T>>::put(bond);
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
 
 		register_validators::<T>(c);
 		register_candidates::<T>(c);
@@ -334,7 +312,6 @@
 
 	// worst case is paying a non-existing candidate account.
 	note_author {
-		<LicenseBond<T>>::put(balance_unit::<T>());
 		T::Currency::set_balance(
 			&<CollatorSelection<T>>::account_id(),
 			balance_unit::<T>() * 4u32.into(),
@@ -353,11 +330,9 @@
 
 	// worst case for new session.
 	new_session {
-		let r in 1 .. T::MaxCollators::get();
-		let c in 1 .. T::MaxCollators::get();
+		let r in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
+		let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
 
-		<LicenseBond<T>>::put(balance_unit::<T>());
-		<DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
 		frame_system::Pallet::<T>::set_block_number(0u32.into());
 
 		register_validators::<T>(c);
modifiedpallets/collator-selection/src/lib.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/lib.rs
+++ b/pallets/collator-selection/src/lib.rs
@@ -90,6 +90,10 @@
 mod benchmarking;
 pub mod weights;
 
+use frame_support::traits::fungible::Inspect;
+
+type BalanceOf<T> =
+	<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
@@ -111,11 +115,6 @@
 	use frame_system::pallet_prelude::*;
 	use pallet_session::SessionManager;
 	use sp_runtime::{Perbill, traits::Convert};
-	use pallet_configuration::{
-		CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
-		CollatorSelectionLicenseBondOverride as LicenseBond,
-		CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf,
-	};
 	use sp_staking::SessionIndex;
 
 	/// A convertor from collators id. Since this pallet does not have stash/controller, this is
@@ -129,9 +128,12 @@
 
 	/// Configure the pallet by specifying the parameters and types on which it depends.
 	#[pallet::config]
-	pub trait Config: frame_system::Config + pallet_configuration::Config {
+	pub trait Config: frame_system::Config {
 		/// Overarching event type.
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+		type Currency: Mutate<Self::AccountId>
+			+ MutateHold<Self::AccountId>
+			+ BalancedHold<Self::AccountId>;
 
 		/// Origin that can dictate updating parameters of this pallet.
 		type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
@@ -163,7 +165,13 @@
 		type WeightInfo: WeightInfo;
 
 		#[pallet::constant]
-		type LicenceBondIdentifier: Get<<<Self as pallet_configuration::Config>::Currency as InspectHold<Self::AccountId>>::Reason>;
+		type LicenceBondIdentifier: Get<<Self::Currency as InspectHold<Self::AccountId>>::Reason>;
+
+		type DesiredCollators: Get<u32>;
+
+		type LicenseBond: Get<BalanceOf<Self>>;
+
+		type KickThreshold: Get<Self::BlockNumber>;
 	}
 
 	#[pallet::pallet]
@@ -365,7 +373,7 @@
 				Error::<T>::ValidatorNotRegistered
 			);
 
-			let deposit = <LicenseBond<T>>::get();
+			let deposit = T::LicenseBond::get();
 
 			T::Currency::hold(&T::LicenceBondIdentifier::get(), &who, deposit)?;
 			LicenseDepositOf::<T>::insert(who.clone(), deposit);
@@ -396,7 +404,7 @@
 			let length = <Candidates<T>>::decode_len().unwrap_or_default()
 				+ <Invulnerables<T>>::decode_len().unwrap_or_default();
 			ensure!(
-				(length as u32) < <DesiredCollators<T>>::get(),
+				(length as u32) < T::DesiredCollators::get(),
 				Error::<T>::TooManyCandidates
 			);
 			ensure!(
@@ -415,7 +423,7 @@
 						// First authored block is current block plus kick threshold to handle session delay
 						<LastAuthoredBlock<T>>::insert(
 							who.clone(),
-							frame_system::Pallet::<T>::block_number() + <KickThreshold<T>>::get(),
+							frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
 						);
 						Ok(candidates.len())
 					}
@@ -574,7 +582,7 @@
 			candidates: BoundedVec<T::AccountId, T::MaxCollators>,
 		) -> BoundedVec<T::AccountId, T::MaxCollators> {
 			let now = frame_system::Pallet::<T>::block_number();
-			let kick_threshold = <KickThreshold<T>>::get();
+			let kick_threshold = T::KickThreshold::get();
 			candidates
 				.into_iter()
 				.filter_map(|c| {
modifiedpallets/collator-selection/src/mock.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -63,7 +63,6 @@
 		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
 		CollatorSelection: collator_selection::{Pallet, Call, Storage, Event<T>},
 		Authorship: pallet_authorship::{Pallet, Storage},
-		Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>},
 	}
 );
 
@@ -216,21 +215,6 @@
 	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
 	pub const DayRelayBlocks: u32 = 1;
 	pub const LicenceBondIdentifier: [u8; 16] = *b"licenceidentifie";
-}
-
-impl pallet_configuration::Config for Test {
-	type RuntimeEvent = RuntimeEvent;
-	type Currency = Balances;
-	type DefaultCollatorSelectionMaxCollators = MaxCollators;
-	type DefaultCollatorSelectionKickThreshold = KickThreshold;
-	type DefaultCollatorSelectionLicenseBond = LicenseBond;
-	// the following constants we don't care about
-	type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient;
-	type DefaultMinGasPrice = DefaultMinGasPrice;
-	type MaxXcmAllowedLocations = MaxXcmAllowedLocations;
-	type AppPromotionDailyRate = AppPromotionDailyRate;
-	type DayRelayBlocks = DayRelayBlocks;
-	type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;
 }
 
 ord_parameter_types! {
@@ -246,11 +230,7 @@
 pub struct IsRegistered;
 impl ValidatorRegistration<u64> for IsRegistered {
 	fn is_registered(id: &u64) -> bool {
-		if *id == 7u64 {
-			false
-		} else {
-			true
-		}
+		*id != 7u64
 	}
 }
 
@@ -265,6 +245,10 @@
 	type ValidatorIdOf = IdentityCollator;
 	type ValidatorRegistration = IsRegistered;
 	type LicenceBondIdentifier = LicenceBondIdentifier;
+	type Currency = Balances;
+	type DesiredCollators = MaxCollators;
+	type LicenseBond = LicenseBond;
+	type KickThreshold = KickThreshold;
 	type WeightInfo = ();
 }
 
@@ -277,7 +261,11 @@
 
 	let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();
 
-	let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (33, ed)];
+	let balances: Vec<(u64, u64)> = (1..=<Test as Config>::DesiredCollators::get() as u64 + 1)
+		.map(|i| (i, 100))
+		.chain(core::iter::once((33, ed)))
+		.collect();
+
 	let keys = balances
 		.iter()
 		.map(|&(i, _)| {
modifiedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -30,19 +30,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate as collator_selection;
+use crate::{self as collator_selection, Config};
 use crate::{mock::*, Error};
 use frame_support::{
 	assert_noop, assert_ok,
 	traits::{fungible, GenesisBuild, OnInitialize},
 };
-use frame_system::RawOrigin;
 use sp_runtime::{traits::BadOrigin, TokenError};
-use pallet_configuration::{
-	CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
-	CollatorSelectionKickThresholdOverride as KickThreshold,
-	CollatorSelectionLicenseBondOverride as LicenseBond,
-};
 use scale_info::prelude::*;
 
 fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {
@@ -57,9 +51,6 @@
 #[test]
 fn basic_setup_works() {
 	new_test_ext().execute_with(|| {
-		assert_eq!(<DesiredCollators<Test>>::get(), 5);
-		assert_eq!(<LicenseBond<Test>>::get(), 10);
-
 		assert!(CollatorSelection::candidates().is_empty());
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 	});
@@ -120,51 +111,6 @@
 		assert_noop!(
 			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),
 			Error::<Test>::TooFewInvulnerables
-		);
-	});
-}
-
-#[test]
-fn set_desired_collators_works() {
-	new_test_ext().execute_with(|| {
-		// given
-		assert_eq!(<DesiredCollators<Test>>::get(), 5);
-
-		// can set
-		assert_ok!(Configuration::set_collator_selection_desired_collators(
-			RawOrigin::Root.into(),
-			Some(7)
-		));
-		assert_eq!(<DesiredCollators<Test>>::get(), 7);
-
-		// rejects bad origin
-		assert_noop!(
-			Configuration::set_collator_selection_desired_collators(
-				RuntimeOrigin::signed(1),
-				Some(8)
-			),
-			BadOrigin
-		);
-	});
-}
-
-#[test]
-fn set_license_bond() {
-	new_test_ext().execute_with(|| {
-		// given
-		assert_eq!(<LicenseBond<Test>>::get(), 10);
-
-		// can set
-		assert_ok!(Configuration::set_collator_selection_license_bond(
-			RawOrigin::Root.into(),
-			Some(7)
-		));
-		assert_eq!(<LicenseBond<Test>>::get(), 7);
-
-		// rejects bad origin.
-		assert_noop!(
-			Configuration::set_collator_selection_license_bond(RuntimeOrigin::signed(1), Some(8)),
-			BadOrigin
 		);
 	});
 }
@@ -187,26 +133,19 @@
 #[test]
 fn cannot_onboard_candidate_if_too_many() {
 	new_test_ext().execute_with(|| {
-		// reset desired candidates
-		<pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(0);
-
-		// can still get a license.
-		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));
-
-		// can't accept anyone anymore.
-		assert_noop!(
-			CollatorSelection::onboard(RuntimeOrigin::signed(4)),
-			Error::<Test>::TooManyCandidates,
-		);
-
-		// reset desired candidates to invulnerables + 1
-		<pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(3);
-		assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));
+		// can accept desired value of collators.
+		for c in 3u64..=(<Test as Config>::DesiredCollators::get()).into() {
+			assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(c)));
+			assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(c)));
+		}
 
 		// but no more.
-		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(5)));
+		let undesired_collator = (<Test as Config>::DesiredCollators::get() + 1) as u64;
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(
+			undesired_collator
+		)));
 		assert_noop!(
-			CollatorSelection::onboard(RuntimeOrigin::signed(5)),
+			CollatorSelection::onboard(RuntimeOrigin::signed(undesired_collator)),
 			Error::<Test>::TooManyCandidates,
 		);
 	})
@@ -227,8 +166,8 @@
 fn cannot_obtain_license_if_poor() {
 	new_test_ext().execute_with(|| {
 		let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();
-		assert_eq!(Balances::free_balance(&3), 100);
-		assert_eq!(Balances::free_balance(&33), ed);
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(33), ed);
 
 		// works
 		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
@@ -266,21 +205,18 @@
 #[test]
 fn becoming_candidate_works() {
 	new_test_ext().execute_with(|| {
-		// given
-		assert_eq!(<DesiredCollators<Test>>::get(), 5);
-		assert_eq!(<LicenseBond<Test>>::get(), 10);
 		assert_eq!(CollatorSelection::candidates(), Vec::new());
 		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
 
 		// take two endowed, non-invulnerables accounts.
-		assert_eq!(Balances::free_balance(&3), 100);
-		assert_eq!(Balances::free_balance(&4), 100);
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(Balances::free_balance(4), 100);
 
 		get_license_and_onboard(3);
 		get_license_and_onboard(4);
 
-		assert_eq!(Balances::free_balance(&3), 90);
-		assert_eq!(Balances::free_balance(&4), 90);
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(Balances::free_balance(4), 90);
 
 		assert_eq!(CollatorSelection::candidates().len(), 2);
 	});
@@ -513,7 +449,7 @@
 		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
 
 		assert_eq!(CollatorSelection::candidates(), vec![4]);
-		assert_eq!(<KickThreshold<Test>>::get(), 10);
+		// assert_eq!(<KickThreshold<Test>>::get(), 10);
 		assert_eq!(CollatorSelection::last_authored_block(4), 20);
 
 		initialize_to_block(30);
modifiedpallets/configuration/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/configuration/CHANGELOG.md
+++ b/pallets/configuration/CHANGELOG.md
@@ -3,7 +3,12 @@
 All notable changes to this project will be documented in this file.
 
 <!-- bureaucrate goes here -->
+## [0.2.0] - 2023-06-27
+
+### Major change
 
+- Architecture fixed: in the configuration of this pallet, bounds on the associated type were determined not by the functional requirements for this pallet itself, but by the pallet that had tight coupling with it.
+
 ## [0.1.3] - 2022-12-29
 
 ### Added
@@ -20,4 +25,4 @@
 
 ### Other changes
 
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
\ No newline at end of file
+- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
modifiedpallets/configuration/Cargo.tomldiffbeforeafterboth
--- a/pallets/configuration/Cargo.toml
+++ b/pallets/configuration/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 edition = "2021"
 name = "pallet-configuration"
-version = "0.1.3"
+version = "0.2.0"
 
 [dependencies]
 # Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.
@@ -22,11 +22,11 @@
 default = ["std"]
 runtime-benchmarks = ["frame-benchmarking"]
 std = [
+	"codec/std",
 	"fp-evm/std",
 	"frame-benchmarking/std",
 	"frame-support/std",
 	"frame-system/std",
-	"codec/std",
 	"sp-arithmetic/std",
 	"sp-core/std",
 	"sp-std/std",
modifiedpallets/configuration/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/configuration/src/benchmarking.rs
+++ b/pallets/configuration/src/benchmarking.rs
@@ -19,7 +19,7 @@
 use super::*;
 use frame_benchmarking::benchmarks;
 use frame_system::{EventRecord, RawOrigin};
-use frame_support::{assert_ok, traits::fungible::Inspect};
+use frame_support::assert_ok;
 
 fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
 	let events = frame_system::Pallet::<T>::events();
@@ -30,7 +30,10 @@
 }
 
 benchmarks! {
-	where_clause { where T: Config }
+	where_clause { where
+		T: Config,
+		T::Balance: From<u32>
+	}
 
 	set_weight_to_fee_coefficient_override {
 		let coeff: u64 = 999;
@@ -60,7 +63,7 @@
 		let max: u32 = 999;
 	}: {
 		assert_ok!(
-			<Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max.clone()))
+			<Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max))
 		);
 	}
 	verify {
@@ -68,10 +71,10 @@
 	}
 
 	set_collator_selection_license_bond {
-		let bond_cost: Option<BalanceOf<T>> = Some(T::Currency::minimum_balance() * 10u32.into());
+		let bond_cost: Option<T::Balance> = Some(1000u32.into());
 	}: {
 		assert_ok!(
-			<Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost.clone())
+			<Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost)
 		);
 	}
 	verify {
@@ -82,7 +85,7 @@
 		let threshold: Option<T::BlockNumber> = Some(900u32.into());
 	}: {
 		assert_ok!(
-			<Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold.clone())
+			<Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold)
 		);
 	}
 	verify {
modifiedpallets/configuration/src/lib.rsdiffbeforeafterboth
--- a/pallets/configuration/src/lib.rs
+++ b/pallets/configuration/src/lib.rs
@@ -22,6 +22,7 @@
 	pallet,
 	weights::{WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient, Weight},
 	traits::Get,
+	Parameter,
 };
 use codec::{Decode, Encode, MaxEncodedLen};
 use scale_info::TypeInfo;
@@ -42,27 +43,33 @@
 mod pallet {
 	use super::*;
 	use frame_support::{
-		traits::{fungible, Get},
-		pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType},
+		traits::{Get},
+		pallet_prelude::{
+			StorageValue, ValueQuery, DispatchResult, IsType, Member, MaybeSerializeDeserialize,
+		},
 		log,
+		dispatch::{Codec, fmt::Debug},
 	};
-	use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig};
-
+	use frame_system::{pallet_prelude::OriginFor, ensure_root};
+	use sp_arithmetic::{FixedPointOperand, traits::AtLeast32BitUnsigned};
 	pub use crate::weights::WeightInfo;
-	pub type BalanceOf<T> =
-		<<T as Config>::Currency as fungible::Inspect<<T as SystemConfig>::AccountId>>::Balance;
 
 	#[pallet::config]
 	pub trait Config: frame_system::Config {
 		/// Overarching event type.
 		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
 
-		type Currency: fungible::Inspect<Self::AccountId>
-			+ fungible::Mutate<Self::AccountId>
-			+ fungible::MutateFreeze<Self::AccountId>
-			+ fungible::InspectHold<Self::AccountId>
-			+ fungible::MutateHold<Self::AccountId>
-			+ fungible::BalancedHold<Self::AccountId>;
+		type Balance: Parameter
+			+ Member
+			+ AtLeast32BitUnsigned
+			+ Codec
+			+ Default
+			+ Copy
+			+ MaybeSerializeDeserialize
+			+ Debug
+			+ MaxEncodedLen
+			+ TypeInfo
+			+ FixedPointOperand;
 
 		#[pallet::constant]
 		type DefaultWeightToFeeCoefficient: Get<u64>;
@@ -79,7 +86,7 @@
 		#[pallet::constant]
 		type DefaultCollatorSelectionMaxCollators: Get<u32>;
 		#[pallet::constant]
-		type DefaultCollatorSelectionLicenseBond: Get<BalanceOf<Self>>;
+		type DefaultCollatorSelectionLicenseBond: Get<Self::Balance>;
 		#[pallet::constant]
 		type DefaultCollatorSelectionKickThreshold: Get<Self::BlockNumber>;
 
@@ -94,7 +101,7 @@
 			desired_collators: Option<u32>,
 		},
 		NewCollatorLicenseBond {
-			bond_cost: Option<BalanceOf<T>>,
+			bond_cost: Option<T::Balance>,
 		},
 		NewCollatorKickThreshold {
 			length_in_blocks: Option<T::BlockNumber>,
@@ -130,7 +137,7 @@
 
 	#[pallet::storage]
 	pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<
-		Value = BalanceOf<T>,
+		Value = T::Balance,
 		QueryKind = ValueQuery,
 		OnEmpty = T::DefaultCollatorSelectionLicenseBond,
 	>;
@@ -221,7 +228,7 @@
 		#[pallet::weight(T::WeightInfo::set_collator_selection_license_bond())]
 		pub fn set_collator_selection_license_bond(
 			origin: OriginFor<T>,
-			amount: Option<BalanceOf<T>>,
+			amount: Option<<T as Config>::Balance>,
 		) -> DispatchResult {
 			ensure_root(origin)?;
 			if let Some(amount) = amount {
modifiedruntime/common/config/pallets/collator_selection.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/collator_selection.rs
+++ b/runtime/common/config/pallets/collator_selection.rs
@@ -23,7 +23,10 @@
 };
 use sp_runtime::Perbill;
 use up_common::constants::{UNIQUE, MILLIUNIQUE};
-
+use pallet_configuration::{
+	CollatorSelectionKickThresholdOverride, CollatorSelectionLicenseBondOverride,
+	CollatorSelectionDesiredCollatorsOverride,
+};
 parameter_types! {
 	pub const SessionOffset: BlockNumber = 0;
 }
@@ -60,6 +63,9 @@
 	pub const MaxAdditionalFields: u32 = 100;
 	pub const MaxRegistrars: u32 = 20;
 	pub const LicenceBondIdentifier: [u8; 16] = *b"licenceidentifie";
+	pub LicenseBond: Balance =  CollatorSelectionLicenseBondOverride::<Runtime>::get();
+	pub DesiredCollators: u32 = CollatorSelectionDesiredCollatorsOverride::<Runtime>::get();
+	pub KickThreshold: BlockNumber = CollatorSelectionKickThresholdOverride::<Runtime>::get();
 }
 
 impl pallet_identity::Config for Runtime {
@@ -84,6 +90,7 @@
 
 impl pallet_collator_selection::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
+	type Currency = Balances;
 	// We allow root only to execute privileged collator selection operations.
 	type UpdateOrigin = EnsureRoot<AccountId>;
 	type TreasuryAccountId = TreasuryAccountId;
@@ -95,4 +102,7 @@
 	type ValidatorRegistration = Session;
 	type WeightInfo = pallet_collator_selection::weights::SubstrateWeight<Runtime>;
 	type LicenceBondIdentifier = LicenceBondIdentifier;
+	type DesiredCollators = DesiredCollators;
+	type LicenseBond = LicenseBond;
+	type KickThreshold = KickThreshold;
 }
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -129,7 +129,7 @@
 
 impl pallet_configuration::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
-	type Currency = Balances;
+	type Balance = Balance;
 	type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;
 	type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;
 	type DefaultCollatorSelectionMaxCollators = MaxCollators;
modifiedtests/src/benchmarks/mintFee/index.tsdiffbeforeafterboth
before · tests/src/benchmarks/mintFee/index.ts
1import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {4  ICrossAccountId,5} from '../../util/playgrounds/types';6import {IKeyringPair} from '@polkadot/types/types';7import {UniqueNFTCollection} from '../../util/playgrounds/unique';8import {Contract} from 'web3-eth-contract';9import {createObjectCsvWriter} from 'csv-writer';10import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from '../utils/common';11import {makeNames} from '../../util';12import {ContractImports} from '../../eth/util/playgrounds/types';1314const {dirname} = makeNames(import.meta.url);1516export const CONTRACT_IMPORT: ContractImports[] = [17  {18    fsPath: `${dirname}/../../eth/api/CollectionHelpers.sol`,19    solPath: 'eth/api/CollectionHelpers.sol',20  },21  {22    fsPath: `${dirname}/../../eth/api/ContractHelpers.sol`,23    solPath: 'eth/api/ContractHelpers.sol',24  },25  {26    fsPath: `${dirname}/../../eth/api/UniqueRefungibleToken.sol`,27    solPath: 'eth/api/UniqueRefungibleToken.sol',28  },29  {30    fsPath: `${dirname}/../../eth/api/UniqueRefungible.sol`,31    solPath: 'eth/api/UniqueRefungible.sol',32  },33  {34    fsPath: `${dirname}/../../eth/api/UniqueNFT.sol`,35    solPath: 'eth/api/UniqueNFT.sol',36  },37];3839interface IBenchmarkResultForProp {40	propertiesNumber: number;41	substrateFee: number;42	ethFee: number;43	ethBulkFee: number;44  ethMintCrossFee: number;45	evmProxyContractFee: number;46	evmProxyContractBulkFee: number;47}4849const main = async () => {50  const benchmarks = [51    'substrateFee',52    'ethFee',53    'ethBulkFee',54    'ethMintCrossFee',55    'evmProxyContractFee',56    'evmProxyContractBulkFee',57  ];58  const headers = [59    'propertiesNumber',60    ...benchmarks,61  ];626364  const csvWriter = createObjectCsvWriter({65    path: 'properties.csv',66    header: headers,67  });6869  await usingEthPlaygrounds(async (helper, privateKey) => {70    const CONTRACT_SOURCE = (71      await readFile(`${dirname}/proxyContract.sol`)72    ).toString();7374    const donor = await privateKey('//Alice'); // Seed from account with balance on this network75    const ethSigner = await helper.eth.createAccountWithBalance(donor);7677    const contract = await helper.ethContract.deployByCode(78      ethSigner,79      'ProxyMint',80      CONTRACT_SOURCE,81      CONTRACT_IMPORT,82    );8384    const fees = await benchMintFee(helper, privateKey, contract);85    console.log('Minting without properties');86    console.table(fees);8788    const result: IBenchmarkResultForProp[] = [];89    const csvResult: IBenchmarkResultForProp[] = [];9091    for(let i = 1; i <= 20; i++) {92      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {93        propertiesNumber: i,94      }) as any;9596      csvResult.push(benchResult);9798      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));99      for(const key of benchmarks) {100        const keyPercent = Math.round((benchResult[key] / minFee) * 100);101        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;102      }103104      result.push(benchResult);105    }106107    await csvWriter.writeRecords(csvResult);108109    console.log('Minting with properties');110    console.table(result, headers);111  });112};113114main()115  .then(() => process.exit(0))116  .catch((e) => {117    console.log(e);118    process.exit(1);119  });120121122123async function benchMintFee(124  helper: EthUniqueHelper,125  privateKey: (seed: string) => Promise<IKeyringPair>,126  proxyContract: Contract,127): Promise<{128	substrateFee: number;129	ethFee: number;130	evmProxyContractFee: number;131}> {132  const donor = await privateKey('//Alice');133  const substrateReceiver = await privateKey('//Bob');134  const ethSigner = await helper.eth.createAccountWithBalance(donor);135136  const nominal = helper.balance.getOneTokenNominal();137138  await helper.eth.transferBalanceFromSubstrate(139    donor,140    proxyContract.options.address,141    100n,142  );143144  const collection = (await createCollectionForBenchmarks(145    'nft',146    helper,147    privateKey,148    ethSigner,149    proxyContract.options.address,150    PERMISSIONS,151  )) as UniqueNFTCollection;152153  const substrateFee = await helper.arrange.calculcateFee(154    {Substrate: donor.address},155    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),156  );157158  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);159  const collectionContract = await helper.ethNativeContract.collection(160    collectionEthAddress,161    'nft',162  );163164  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);165166  const encodedCall = collectionContract.methods167    .mint(receiverEthAddress)168    .encodeABI();169170  const ethFee = await helper.arrange.calculcateFee(171    {Substrate: donor.address},172    async () => {173      await helper.eth.sendEVM(174        donor,175        collectionContract.options.address,176        encodedCall,177        '0',178      );179    },180  );181182  const evmProxyContractFee = await helper.arrange.calculcateFee(183    {Ethereum: ethSigner},184    async () => {185      await proxyContract.methods186        .mintToSubstrate(187          helper.ethAddress.fromCollectionId(collection.collectionId),188          substrateReceiver.addressRaw,189        )190        .send({from: ethSigner});191    },192  );193194  return {195    substrateFee: convertToTokens(substrateFee, nominal),196    ethFee: convertToTokens(ethFee, nominal),197    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),198  };199}200201async function benchMintWithProperties(202  helper: EthUniqueHelper,203  privateKey: (seed: string) => Promise<IKeyringPair>,204  proxyContract: Contract,205  setup: { propertiesNumber: number },206): Promise<IBenchmarkResultForProp> {207  const donor = await privateKey('//Alice'); // Seed from account with balance on this network208  const ethSigner = await helper.eth.createAccountWithBalance(donor);209210  const susbstrateReceiver = await privateKey('//Bob');211  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);212213  const nominal = helper.balance.getOneTokenNominal();214215  const substrateFee = await calculateFeeNftMintWithProperties(216    helper,217    privateKey,218    {Substrate: donor.address},219    ethSigner,220    proxyContract.options.address,221    async (collection) => {222      await collection.mintToken(223        donor,224        {Substrate: susbstrateReceiver.address},225        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})),226      );227    },228  );229230  const ethFee = await calculateFeeNftMintWithProperties(231    helper,232    privateKey,233    {Substrate: donor.address},234    ethSigner,235    proxyContract.options.address,236    async (collection) => {237      const evmContract = await helper.ethNativeContract.collection(238        helper.ethAddress.fromCollectionId(collection.collectionId),239        'nft',240        undefined,241        true,242      );243244      const subTokenId = await evmContract.methods.nextTokenId().call();245246      let encodedCall = evmContract.methods247        .mint(receiverEthAddress)248        .encodeABI();249250      await helper.eth.sendEVM(251        donor,252        evmContract.options.address,253        encodedCall,254        '0',255      );256257      for(const val of PROPERTIES.slice(0, setup.propertiesNumber)) {258        encodedCall = await evmContract.methods259          .setProperty(subTokenId, val.key, Buffer.from(val.value))260          .encodeABI();261262        await helper.eth.sendEVM(263          donor,264          evmContract.options.address,265          encodedCall,266          '0',267        );268      }269    },270  );271272  const ethBulkFee = await calculateFeeNftMintWithProperties(273    helper,274    privateKey,275    {Substrate: donor.address},276    ethSigner,277    proxyContract.options.address,278    async (collection) => {279      const evmContract = await helper.ethNativeContract.collection(280        helper.ethAddress.fromCollectionId(collection.collectionId),281        'nft',282      );283284      const subTokenId = await evmContract.methods.nextTokenId().call();285286      let encodedCall = evmContract.methods287        .mint(receiverEthAddress)288        .encodeABI();289290      await helper.eth.sendEVM(291        donor,292        evmContract.options.address,293        encodedCall,294        '0',295      );296297      encodedCall = await evmContract.methods298        .setProperties(299          subTokenId,300          PROPERTIES.slice(0, setup.propertiesNumber),301        )302        .encodeABI();303304      await helper.eth.sendEVM(305        donor,306        evmContract.options.address,307        encodedCall,308        '0',309      );310    },311  );312313  const ethMintCrossFee = await calculateFeeNftMintWithProperties(314    helper,315    privateKey,316    {Ethereum: ethSigner},317    ethSigner,318    proxyContract.options.address,319    async (collection) => {320      const evmContract = await helper.ethNativeContract.collection(321        helper.ethAddress.fromCollectionId(collection.collectionId),322        'nft',323      );324325      await evmContract.methods.mintCross(326        helper.ethCrossAccount.fromAddress(receiverEthAddress),327        PROPERTIES.slice(0, setup.propertiesNumber),328      )329        .send({from: ethSigner});330    },331  );332333  const proxyContractFee = await calculateFeeNftMintWithProperties(334    helper,335    privateKey,336    {Ethereum: ethSigner},337    ethSigner,338    proxyContract.options.address,339    async (collection) => {340      await proxyContract.methods341        .mintToSubstrateWithProperty(342          helper.ethAddress.fromCollectionId(collection.collectionId),343          susbstrateReceiver.addressRaw,344          PROPERTIES.slice(0, setup.propertiesNumber),345        )346        .send({from: ethSigner});347    },348  );349350  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(351    helper,352    privateKey,353    {Ethereum: ethSigner},354    ethSigner,355    proxyContract.options.address,356    async (collection) => {357      await proxyContract.methods358        .mintToSubstrateBulkProperty(359          helper.ethAddress.fromCollectionId(collection.collectionId),360          susbstrateReceiver.addressRaw,361          PROPERTIES.slice(0, setup.propertiesNumber),362        )363        .send({from: ethSigner, gas: 25_000_000});364    },365  );366367  return {368    propertiesNumber: setup.propertiesNumber,369    substrateFee: convertToTokens(substrateFee, nominal),370    ethFee: convertToTokens(ethFee, nominal),371    ethBulkFee: convertToTokens(ethBulkFee, nominal),372    ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),373    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),374    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),375  };376}377378async function calculateFeeNftMintWithProperties(379  helper: EthUniqueHelper,380  privateKey: (seed: string) => Promise<IKeyringPair>,381  payer: ICrossAccountId,382  ethSigner: string,383  proxyContractAddress: string,384  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,385): Promise<bigint> {386  const collection = (await createCollectionForBenchmarks(387    'nft',388    helper,389    privateKey,390    ethSigner,391    proxyContractAddress,392    PERMISSIONS,393  )) as UniqueNFTCollection;394  return helper.arrange.calculcateFee(payer, async () => {395    await calculatedCall(collection);396  });397}398399400
after · tests/src/benchmarks/mintFee/index.ts
1import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {4  ICrossAccountId,5} from '../../util/playgrounds/types';6import {IKeyringPair} from '@polkadot/types/types';7import {UniqueNFTCollection} from '../../util/playgrounds/unique';8import {Contract} from 'web3-eth-contract';9import {createObjectCsvWriter} from 'csv-writer';10import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from '../utils/common';11import {makeNames} from '../../util';12import {ContractImports} from '../../eth/util/playgrounds/types';1314const {dirname} = makeNames(import.meta.url);1516export const CONTRACT_IMPORT: ContractImports[] = [17  {18    fsPath: `${dirname}/../../eth/api/CollectionHelpers.sol`,19    solPath: 'eth/api/CollectionHelpers.sol',20  },21  {22    fsPath: `${dirname}/../../eth/api/ContractHelpers.sol`,23    solPath: 'eth/api/ContractHelpers.sol',24  },25  {26    fsPath: `${dirname}/../../eth/api/UniqueRefungibleToken.sol`,27    solPath: 'eth/api/UniqueRefungibleToken.sol',28  },29  {30    fsPath: `${dirname}/../../eth/api/UniqueRefungible.sol`,31    solPath: 'eth/api/UniqueRefungible.sol',32  },33  {34    fsPath: `${dirname}/../../eth/api/UniqueNFT.sol`,35    solPath: 'eth/api/UniqueNFT.sol',36  },37];3839interface IBenchmarkResultForProp {40	propertiesNumber: number;41	substrateFee: number;42	ethFee: number;43	ethBulkFee: number;44  ethMintCrossFee: number;45	evmProxyContractFee: number;46	evmProxyContractBulkFee: number;47}4849const main = async () => {50  const benchmarks = [51    'substrateFee',52    'ethFee',53    'ethBulkFee',54    'ethMintCrossFee',55    'evmProxyContractFee',56    'evmProxyContractBulkFee',57  ];58  const headers = [59    'propertiesNumber',60    ...benchmarks,61  ];626364  const csvWriter = createObjectCsvWriter({65    path: 'properties.csv',66    header: headers,67  });6869  await usingEthPlaygrounds(async (helper, privateKey) => {70    const CONTRACT_SOURCE = (71      await readFile(`${dirname}/proxyContract.sol`)72    ).toString();7374    const donor = await privateKey('//Alice'); // Seed from account with balance on this network75    const ethSigner = await helper.eth.createAccountWithBalance(donor);7677    const contract = await helper.ethContract.deployByCode(78      ethSigner,79      'ProxyMint',80      CONTRACT_SOURCE,81      CONTRACT_IMPORT,82    );8384    const fees = await benchMintFee(helper, privateKey, contract);85    console.log('Minting without properties');86    console.table(fees);8788    const result: IBenchmarkResultForProp[] = [];89    const csvResult: IBenchmarkResultForProp[] = [];9091    for(let i = 1; i <= 20; i++) {92      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {93        propertiesNumber: i,94      }) as any;9596      csvResult.push(benchResult);9798      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));99      for(const key of benchmarks) {100        const keyPercent = Math.round((benchResult[key] / minFee) * 100);101        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;102      }103104      result.push(benchResult);105    }106107    await csvWriter.writeRecords(csvResult);108109    console.log('Minting with properties');110    console.table(result, headers);111  });112};113114main()115  .then(() => process.exit(0))116  .catch((e) => {117    console.log(e);118    process.exit(1);119  });120121122123async function benchMintFee(124  helper: EthUniqueHelper,125  privateKey: (seed: string) => Promise<IKeyringPair>,126  proxyContract: Contract,127): Promise<{128	substrateFee: number;129	ethFee: number;130	evmProxyContractFee: number;131}> {132  const donor = await privateKey('//Alice');133  const substrateReceiver = await privateKey('//Bob');134  const ethSigner = await helper.eth.createAccountWithBalance(donor);135136  const nominal = helper.balance.getOneTokenNominal();137138  await helper.eth.transferBalanceFromSubstrate(139    donor,140    proxyContract.options.address,141    100n,142  );143144  const collection = (await createCollectionForBenchmarks(145    'nft',146    helper,147    privateKey,148    ethSigner,149    proxyContract.options.address,150    PERMISSIONS,151  )) as UniqueNFTCollection;152153  const substrateFee = await helper.arrange.calculcateFee(154    {Substrate: donor.address},155    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),156  );157158  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);159  const collectionContract = await helper.ethNativeContract.collection(160    collectionEthAddress,161    'nft',162  );163164  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);165166  const encodedCall = collectionContract.methods167    .mint(receiverEthAddress)168    .encodeABI();169170  const ethFee = await helper.arrange.calculcateFee(171    {Substrate: donor.address},172    async () => {173      await helper.eth.sendEVM(174        donor,175        collectionContract.options.address,176        encodedCall,177        '0',178      );179    },180  );181182  const evmProxyContractFee = await helper.arrange.calculcateFee(183    {Ethereum: ethSigner},184    async () => {185      await proxyContract.methods186        .mintToSubstrate(187          helper.ethAddress.fromCollectionId(collection.collectionId),188          substrateReceiver.addressRaw,189        )190        .send({from: ethSigner});191    },192  );193194  return {195    substrateFee: convertToTokens(substrateFee, nominal),196    ethFee: convertToTokens(ethFee, nominal),197    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),198  };199}200201async function benchMintWithProperties(202  helper: EthUniqueHelper,203  privateKey: (seed: string) => Promise<IKeyringPair>,204  proxyContract: Contract,205  setup: { propertiesNumber: number },206): Promise<IBenchmarkResultForProp> {207  const donor = await privateKey('//Alice'); // Seed from account with balance on this network208  const ethSigner = await helper.eth.createAccountWithBalance(donor);209210  const susbstrateReceiver = await privateKey('//Bob');211  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);212213  const nominal = helper.balance.getOneTokenNominal();214215  const substrateFee = await calculateFeeNftMintWithProperties(216    helper,217    privateKey,218    {Substrate: donor.address},219    ethSigner,220    proxyContract.options.address,221    async (collection) => {222      await collection.mintToken(223        donor,224        {Substrate: susbstrateReceiver.address},225        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})),226      );227    },228  );229230  const ethFee = await calculateFeeNftMintWithProperties(231    helper,232    privateKey,233    {Substrate: donor.address},234    ethSigner,235    proxyContract.options.address,236    async (collection) => {237      const evmContract = await helper.ethNativeContract.collection(238        helper.ethAddress.fromCollectionId(collection.collectionId),239        'nft',240        undefined,241        true,242      );243244      const subTokenId = await evmContract.methods.nextTokenId().call();245246      let encodedCall = evmContract.methods247        .mint(receiverEthAddress)248        .encodeABI();249250      await helper.eth.sendEVM(251        donor,252        evmContract.options.address,253        encodedCall,254        '0',255      );256257      for(const val of PROPERTIES.slice(0, setup.propertiesNumber)) {258        encodedCall = await evmContract.methods259          .setProperty(subTokenId, val.key, Buffer.from(val.value))260          .encodeABI();261262        await helper.eth.sendEVM(263          donor,264          evmContract.options.address,265          encodedCall,266          '0',267        );268      }269    },270  );271272  const ethBulkFee = await calculateFeeNftMintWithProperties(273    helper,274    privateKey,275    {Substrate: donor.address},276    ethSigner,277    proxyContract.options.address,278    async (collection) => {279      const evmContract = await helper.ethNativeContract.collection(280        helper.ethAddress.fromCollectionId(collection.collectionId),281        'nft',282      );283284      const subTokenId = await evmContract.methods.nextTokenId().call();285286      let encodedCall = evmContract.methods287        .mint(receiverEthAddress)288        .encodeABI();289290      await helper.eth.sendEVM(291        donor,292        evmContract.options.address,293        encodedCall,294        '0',295      );296297      encodedCall = await evmContract.methods298        .setProperties(299          subTokenId,300          PROPERTIES.slice(0, setup.propertiesNumber),301        )302        .encodeABI();303304      await helper.eth.sendEVM(305        donor,306        evmContract.options.address,307        encodedCall,308        '0',309      );310    },311  );312313  const ethMintCrossFee = await calculateFeeNftMintWithProperties(314    helper,315    privateKey,316    {Ethereum: ethSigner},317    ethSigner,318    proxyContract.options.address,319    async (collection) => {320      const evmContract = await helper.ethNativeContract.collection(321        helper.ethAddress.fromCollectionId(collection.collectionId),322        'nft',323      );324325      await evmContract.methods.mintCross(326        helper.ethCrossAccount.fromAddress(receiverEthAddress),327        PROPERTIES.slice(0, setup.propertiesNumber),328      )329        .send({from: ethSigner});330    },331  );332333  const proxyContractFee = await calculateFeeNftMintWithProperties(334    helper,335    privateKey,336    {Ethereum: ethSigner},337    ethSigner,338    proxyContract.options.address,339    async (collection) => {340      await proxyContract.methods341        .mintToSubstrateWithProperty(342          helper.ethAddress.fromCollectionId(collection.collectionId),343          susbstrateReceiver.addressRaw,344          PROPERTIES.slice(0, setup.propertiesNumber),345        )346        .send({from: ethSigner});347    },348  );349350  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(351    helper,352    privateKey,353    {Ethereum: ethSigner},354    ethSigner,355    proxyContract.options.address,356    async (collection) => {357      await proxyContract.methods358        .mintToSubstrateBulkProperty(359          helper.ethAddress.fromCollectionId(collection.collectionId),360          susbstrateReceiver.addressRaw,361          PROPERTIES.slice(0, setup.propertiesNumber),362        )363        .send({from: ethSigner});364    },365  );366367  return {368    propertiesNumber: setup.propertiesNumber,369    substrateFee: convertToTokens(substrateFee, nominal),370    ethFee: convertToTokens(ethFee, nominal),371    ethBulkFee: convertToTokens(ethBulkFee, nominal),372    ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),373    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),374    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),375  };376}377378async function calculateFeeNftMintWithProperties(379  helper: EthUniqueHelper,380  privateKey: (seed: string) => Promise<IKeyringPair>,381  payer: ICrossAccountId,382  ethSigner: string,383  proxyContractAddress: string,384  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,385): Promise<bigint> {386  const collection = (await createCollectionForBenchmarks(387    'nft',388    helper,389    privateKey,390    ethSigner,391    proxyContractAddress,392    PERMISSIONS,393  )) as UniqueNFTCollection;394  return helper.arrange.calculcateFee(payer, async () => {395    await calculatedCall(collection);396  });397}398399400