git.delta.rocks / unique-network / refs/commits / 70abe578b643

difftreelog

tests(collator-selection): integration tests + types + minor refactor of thee pallet

Fahrrader2022-12-23parent: #d41364f.patch.diff
in: master

20 files changed

modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -196,9 +196,9 @@
 					.cloned()
 					.map(|(acc, _)| acc)
 					.collect(),
+				desired_collators: 10,
 				license_bond: GENESIS_LICENSE_BOND,
 				kick_threshold: SESSION_LENGTH,
-				..Default::default()
 			},
 			session: SessionConfig {
 				keys: $initial_invulnerables
modifiedpallets/collator-selection/src/lib.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/lib.rs
+++ b/pallets/collator-selection/src/lib.rs
@@ -183,11 +183,8 @@
 	/// The (community, limited) collation candidates.
 	#[pallet::storage]
 	#[pallet::getter(fn candidates)]
-	pub type Candidates<T: Config> = StorageValue<
-		_,
-		BoundedVec<T::AccountId, T::MaxCollators>, //LicenseInfo<T::AccountId, BalanceOf<T>>, T::MaxCollators>, // license ID?
-		ValueQuery,
-	>;
+	pub type Candidates<T: Config> =
+		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;
 
 	/// Collator will be kicked if it does not produce a block within the threshold (does not apply to invulnerables).
 	///
@@ -348,7 +345,6 @@
 				T::ValidatorRegistration::is_registered(&validator_key),
 				Error::<T>::ValidatorNotRegistered
 			);
-			// ensure!(!Self::invulnerables().contains(&new), Error::<T>::AlreadyInvulnerable);
 			if Self::invulnerables().contains(&new) {
 				return Ok(().into());
 			}
@@ -371,7 +367,6 @@
 		) -> DispatchResultWithPostInfo {
 			T::UpdateOrigin::ensure_origin(origin)?;
 
-			// let index = Self::invulnerables().into_iter().position(|r| r == who).ok_or(Error::<T>::NotInvulnerable)?;
 			<Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {
 				if invulnerables.len() <= 1 {
 					return Err(Error::<T>::TooFewInvulnerables.into());
@@ -384,10 +379,6 @@
 				invulnerables.remove(index);
 				Ok(())
 			})?;
-			/*let bounded_invulnerables = BoundedVec::<_, T::MaxInvulnerables>::try_from(new)
-				.map_err(|_| Error::<T>::TooManyInvulnerables)?;
-
-			<Invulnerables<T>>::put(&bounded_invulnerables);*/
 			Self::deposit_event(Event::InvulnerableRemoved { invulnerable: who });
 			Ok(().into())
 		}
@@ -451,11 +442,6 @@
 				return Err(Error::<T>::AlreadyHoldingLicense.into());
 			}
 
-			/*ensure!(
-				!Self::invulnerables().contains(&who),
-				Error::<T>::AlreadyInvulnerable
-			);*/
-
 			let validator_key = T::ValidatorIdOf::convert(who.clone())
 				.ok_or(Error::<T>::NoAssociatedValidatorId)?;
 			ensure!(
@@ -464,34 +450,9 @@
 			);
 
 			let deposit = Self::license_bond();
-			// First authored block is current block plus kick threshold to handle session delay
-			/*let incoming = LicenseInfo {
-				who: who.clone(),
-				deposit,
-			};*/
 
 			T::Currency::reserve(&who, deposit)?;
 			Licenses::<T>::insert(who.clone(), deposit);
-
-			/*let current_count =
-			<Licenses<T>>::try_mutate(|licenses| -> Result<usize, DispatchError> {
-				if T::OriginPrivilegeCmp::cmp_privilege(&origin, &scheduled.origin) {
-					return Err(BadOrigin.into());
-				}
-				if candidates.iter().any(|candidate| *candidate == who) {
-					Err(Error::<T>::AlreadyHoldingLicense)?
-				} else {
-					T::Currency::reserve(&who, deposit)?;
-					candidates
-						.try_push(incoming)
-						.map_err(|_| Error::<T>::TooManyCandidates)?;
-					<LastAuthoredBlock<T>>::insert(
-						who.clone(),
-						frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),
-					);
-					Ok(candidates.len())
-				}
-			})?;*/
 
 			Self::deposit_event(Event::LicenseObtained {
 				account_id: who,
@@ -518,17 +479,11 @@
 				(length as u32) < Self::desired_collators(),
 				Error::<T>::TooManyCandidates
 			);
-			// todo:collator really need it?
 			ensure!(
 				!Self::invulnerables().contains(&who),
 				Error::<T>::AlreadyInvulnerable
 			);
 
-			/*let incoming = LicenseInfo {
-				who: who.clone(),
-				deposit,
-			};*/
-
 			let current_count =
 				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
 					if candidates.iter().any(|candidate| *candidate == who) {
@@ -552,17 +507,10 @@
 
 		/// Deregister `origin` as a collator candidate. Note that the collator can only leave on
 		/// session change. The license to `onboard` later at any other time will remain.
-		///
-		/// This call will fail if the total number of candidates would drop below `MinCandidates`. todo:collator maybe not
 		#[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight
 		pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
 			// leave_intent
 			let who = ensure_signed(origin)?;
-			/* todo:collator invulnerables and candidates should count against min candidates together
-			ensure!(
-				Self::candidates().len() as u32 > T::MinCandidates::get(),
-				Error::<T>::TooFewCandidates
-			);*/
 			let current_count = Self::try_remove_candidate(&who)?;
 
 			Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight
@@ -585,7 +533,7 @@
 		/// Note that the collator can only leave on session change.
 		/// The `LicenseBond` will be unreserved and returned immediately.
 		///
-		/// This call is not available to `Invulnerable` collators.
+		/// This call is, of course, not applicable to `Invulnerable` collators.
 		#[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight
 		pub fn force_revoke_license(
 			origin: OriginFor<T>,
@@ -606,6 +554,8 @@
 			T::PotId::get().into_account_truncating()
 		}
 
+		/// Removes a candidate and their license, optionally slashed and optionally ignoring,
+		/// whether or not they actually are a candidate.
 		fn try_remove_candidate_and_release_license(
 			who: &T::AccountId,
 			should_slash: bool,
@@ -687,7 +637,7 @@
 		/// Kicks out candidates that did not produce a block in the kick threshold
 		/// and **confiscates** their deposits to the treasury.
 		pub fn kick_stale_candidates(
-			candidates: BoundedVec<T::AccountId, T::MaxCollators>, //LicenseInfo<T::AccountId, BalanceOf<T>>
+			candidates: BoundedVec<T::AccountId, T::MaxCollators>,
 		) -> BoundedVec<T::AccountId, T::MaxCollators> {
 			let now = frame_system::Pallet::<T>::block_number();
 			let kick_threshold = Self::kick_threshold();
modifiedpallets/collator-selection/src/mock.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -223,13 +223,11 @@
 }
 
 impl Config for Test {
-	// todo:collator mocks and stocks
 	type RuntimeEvent = RuntimeEvent;
 	type Currency = Balances;
 	type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;
 	type PotId = PotId;
 	type MaxCollators = MaxCollators;
-	// type KickThreshold = Period;
 	type SlashRatio = SlashRatio;
 	type TreasuryAccountId = ();
 	type ValidatorId = <Self as frame_system::Config>::AccountId;
modifiedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -59,9 +59,6 @@
 	});
 }
 
-// todo:collator add more tests later
-// invulnerable after onboard + invulnerables can bypass desired_candidates
-
 #[test]
 fn it_should_add_invulnerables() {
 	new_test_ext().execute_with(|| {
modifiedruntime/common/mod.rsdiffbeforeafterboth
--- a/runtime/common/mod.rs
+++ b/runtime/common/mod.rs
@@ -191,7 +191,7 @@
 				RuntimeAppPublic,
 			};
 			use pallet_session::SessionManager;
-			use up_common::constants::GENESIS_LICENSE_BOND;
+			use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH};
 			use crate::config::pallets::collator_selection::MaxCollators;
 
 			let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);
@@ -241,6 +241,7 @@
 				.expect("Existing collators/invulnerables are more than MaxCollators");
 
 				<pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);
+				<pallet_collator_selection::KickThreshold<Runtime>>::put(SESSION_LENGTH);
 				<pallet_collator_selection::DesiredCollators<Runtime>>::put(MaxCollators::get());
 				<pallet_collator_selection::LicenseBond<Runtime>>::put(GENESIS_LICENSE_BOND);
 
modifiedtests/src/collatorSelection.seqtest.tsdiffbeforeafterboth
--- a/tests/src/collatorSelection.seqtest.ts
+++ b/tests/src/collatorSelection.seqtest.ts
@@ -17,6 +17,8 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';
 
+const MAX_INVULNERABLES = 10;
+
 async function resetInvulnerables() {
   await usingPlaygrounds(async (helper, privateKey) => {
     const superuser = await privateKey('//Alice');
@@ -28,6 +30,15 @@
         + 'Current invulnerables\' size: ' + invulnerables.length);
       
       let nonce = await helper.chain.getNonce(alice.address);
+      // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.
+      if (invulnerables.length + 2 >= MAX_INVULNERABLES) {
+        await Promise.all([
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),
+        ]);
+      }
+
+      nonce = await helper.chain.getNonce(alice.address);
       await Promise.all([
         helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
         helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
@@ -43,14 +54,19 @@
 }
 
 // todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).
-// + 18 tests: 5 (1+4) on session change
 describe('Integration Test: Collator Selection', () => {
   let superuser: IKeyringPair;
+  let previousLicenseBond = 0n;
+  let licenseBond = 0n;
 
   before(async function() {  
     await usingPlaygrounds(async (helper, privateKey) => {
       requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);
       superuser = await privateKey('//Alice');
+
+      previousLicenseBond = await helper.collatorSelection.getLicenseBond();
+      licenseBond = 10n * helper.balance.getOneTokenNominal();
+      await helper.getSudo().collatorSelection.setLicenseBond(superuser, licenseBond);
     });
   });
 
@@ -73,13 +89,11 @@
         charlie = await privateKey('//Charlie');
         dave = await privateKey('//Dave');
 
-        expect((await helper.collatorSelection.setOwnKeys(charlie))
+        expect((await helper.session.setOwnKeysFromAddress(charlie))
           .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.collatorSelection.setOwnKeys(dave))
+        expect((await helper.session.setOwnKeysFromAddress(dave))
           .status.toLowerCase()).to.be.equal('success');
   
-        // todo:collator check necessity + add RPC for invulnerables / just improve in general
-        // validators = await helper.callRpc('api.query.session.validators');
         const invulnerables = await helper.collatorSelection.getInvulnerables();
         if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
           console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 
@@ -116,19 +130,7 @@
       const newInvulnerables = await helper.collatorSelection.getInvulnerables();
       expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
   
-      const expectedSessionIndex = (await helper.callRpc('api.query.session.currentIndex')).toNumber() + 2;
-      let currentSessionIndex = -1;
-      console.log('Waiting for the session after the next.' 
-        + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
-  
-      while (currentSessionIndex < expectedSessionIndex) {
-        // eslint-disable-next-line no-async-promise-executor
-        currentSessionIndex = await expect(helper.wait.withTimeout(new Promise(async (resolve) => {
-          await helper.wait.newBlocks(1);
-          const res = (await helper.callRpc('api.query.session.currentIndex')).toNumber();
-          resolve(res);
-        }), 24000, 'The chain has stopped producing blocks!')).to.be.fulfilled;
-      }
+      await helper.wait.newSessions(2);
   
       const newValidators = await helper.callRpc('api.query.session.validators');
       expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
@@ -140,9 +142,6 @@
       expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;
     });
   
-    // todo:collator keyless invulnerables? will hang, so, a breaking test, eh
-    // register candidate without sudos and the like
-  
     after(async () => {
       await usingPlaygrounds(async (helper) => {
         if (await helper.arrange.isDevNode()) return;
@@ -162,9 +161,185 @@
     });
   });
 
-  // todo:collator make sure that there is enough session time for a set of tests
-  // 28 non-functioning collators, teehee.
+  describe('Getting and releasing licenses to collate', () => {
+    let charlie: IKeyringPair;
+    let dave: IKeyringPair;
+    let crowd: IKeyringPair[];
+    
+    before(async function() {  
+      await usingPlaygrounds(async (helper, privateKey) => {
+        charlie = await privateKey('//Charlie');
+        dave = await privateKey('//Dave');
+        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
+
+        // set session keys for everyone
+        expect((await helper.session.setOwnKeysFromAddress(charlie))
+          .status.toLowerCase()).to.be.equal('success');
+        expect((await helper.session.setOwnKeysFromAddress(dave))
+          .status.toLowerCase()).to.be.equal('success');
+        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
+      });
+    });
+
+    describe('Positive', () => {
+      itSub('Can lease and release a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        
+        // make sure it does not have any reserved funds
+        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(0n);
+
+        // getting a license reserves a license bond cost
+        await helper.collatorSelection.obtainLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
+        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(licenseBond);
+
+        // releasing a license un-reserves the license bond cost
+        await helper.collatorSelection.releaseLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
+        
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(0n);
+        expect(balance.free > 100n - licenseBond);
+      });
+
+      itSub('Can force revoke a license', async ({helper}) => {
+        const account = crowd.pop()!;
+
+        // getting a license reserves a license bond cost
+        const previousBalance = await helper.balance.getSubstrateFull(account.address);
+        await helper.collatorSelection.obtainLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
+
+        // force-releasing a license un-reserves the license bond cost as well
+        await helper.getSudo().collatorSelection.forceRevokeLicense(superuser, account.address);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved);
+
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(previousBalance.reserved);
+        expect(balance.free > previousBalance.free - licenseBond);
+      });
+    });
 
+    describe('Negative', () => {
+      itSub('Cannot get a license without session keys set', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([100n], superuser);
+        await expect(helper.collatorSelection.obtainLicense(account))
+          .to.be.rejectedWith(/collatorSelection.ValidatorNotRegistered/);
+      });
+
+      itSub('Cannot register a license twice', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.obtainLicense(account))
+          .to.be.rejectedWith(/collatorSelection.AlreadyHoldingLicense/);
+      });
+
+      itSub('Cannot release a license twice', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.releaseLicense(account);
+        await expect(helper.collatorSelection.releaseLicense(account))
+          .to.be.rejectedWith(/collatorSelection.NoLicense/);
+      });
+
+      itSub('Cannot force revoke a license as non-sudo', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.forceRevokeLicense(superuser, account.address))
+          .to.be.rejectedWith(/BadOrigin/);
+      });
+    });
+  });
+
+  describe('Onboarding, collating, and offboarding as collator candidates', () => {
+    // These two are the default invulnerables, and should return to be invulnerables after this suite.
+    let charlie: IKeyringPair;
+    let dave: IKeyringPair;
+    let crowd: IKeyringPair[];
+    
+    before(async function() {  
+      await usingPlaygrounds(async (helper, privateKey) => {
+        charlie = await privateKey('//Charlie');
+        dave = await privateKey('//Dave');
+        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
+
+        // set session keys for everyone
+        expect((await helper.session.setOwnKeysFromAddress(charlie))
+          .status.toLowerCase()).to.be.equal('success');
+        expect((await helper.session.setOwnKeysFromAddress(dave))
+          .status.toLowerCase()).to.be.equal('success');
+        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
+      });
+    });
+
+    describe('Positive', () => {
+      itSub('Can onboard and offboard repeatedly', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
+
+        await helper.collatorSelection.offboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
+
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
+
+        await helper.collatorSelection.offboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
+      });
+
+      itSub('Dithmarschen', async ({helper}) => {
+        // This one shouldn't even be able to produce blocks.
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.contain(account.address);
+
+        // Wait for 3 new sessions before checking that the collator will be kicked:
+        // one to get collator onboarded, and another two for the collator to fail
+        await helper.wait.newSessions(3);
+
+        expect(await helper.collatorSelection.getCandidates()).to.not.contain(account.address);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
+
+        // The account's reserved funds get slashed as a penalty
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(0n);
+        expect(balance.free < 100n - licenseBond);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Cannot onboard without a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        await expect(helper.collatorSelection.onboard(account))
+          .to.be.rejectedWith(/collatorSelection.NoLicense/);
+      });
+
+      itSub('Cannot offboard without a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        await expect(helper.collatorSelection.offboard(account))
+          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
+      });
+
+      itSub('Cannot offboard while not onboarded', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.offboard(account))
+          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
+      });
+
+      itSub('Cannot onboard while already onboarded', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        await expect(helper.collatorSelection.onboard(account))
+          .to.be.rejectedWith(/collatorSelection.AlreadyCandidate/);
+      });
+    });
+  });
+
   describe('Addition and removal of invulnerables', () => {
     before(async function() {
       await resetInvulnerables();
@@ -175,7 +350,7 @@
         const [account] = await helper.arrange.createAccounts([10n], superuser);
         const invulnerables = await helper.collatorSelection.getInvulnerables();
 
-        await helper.collatorSelection.setOwnKeys(account);
+        await helper.session.setOwnKeysFromAddress(account);
         await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);
         
         const newInvulnerables = await helper.collatorSelection.getInvulnerables();
@@ -184,7 +359,7 @@
 
       itSub('Removes an invulnerable', async ({helper}) => {
         const invulnerables = await helper.collatorSelection.getInvulnerables();
-        const lastInvulnerable = invulnerables.pop();
+        const lastInvulnerable = invulnerables.pop()!;
 
         await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);
         const newInvulnerables = await helper.collatorSelection.getInvulnerables();
@@ -203,16 +378,22 @@
         expect(newInvulnerables).to.have.all.members(invulnerables);
       });
 
+      itSub('Cannot remove a non-existent invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([0n], superuser);
+        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, account.address))
+          .to.be.rejectedWith(/collatorSelection.NotInvulnerable/);
+      });
+
       itSub('Cannot allow invulnerables to be empty', async ({helper}) => {
         const invulnerables = await helper.collatorSelection.getInvulnerables();
-        const lastInvulnerable = invulnerables.pop();
+        const lastInvulnerable = invulnerables.pop()!;
 
         let nonce = await helper.chain.getNonce(superuser.address);
         await Promise.all(invulnerables.map((i: any) => 
           helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));
 
         await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))
-          .to.be.rejected;//todo:collator With(/collatorSelection.TooFewInvulnerables/);
+          .to.be.rejectedWith(/collatorSelection.TooFewInvulnerables/);
 
         const newInvulnerables = await helper.collatorSelection.getInvulnerables();
         expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);
@@ -224,21 +405,24 @@
       });
 
       itSub('Cannot have too many invulnerables', async ({helper}) => {
+        // todo:collator make sure that there is enough session time for a set of tests
+        // 28 non-functioning collators, teehee.
+        
         const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
-        const invulnerablesUntilLimit = 30 - invulnerablesLength;
+        const invulnerablesUntilLimit = MAX_INVULNERABLES - invulnerablesLength;
         const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
         const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
 
         await Promise.all(newInvulnerables.map((i: IKeyringPair) => 
-          helper.collatorSelection.setOwnKeys(i)));
-        await helper.collatorSelection.setOwnKeys(lastInvulnerable);
+          helper.session.setOwnKeysFromAddress(i)));
+        await helper.session.setOwnKeysFromAddress(lastInvulnerable);
 
         let nonce = await helper.chain.getNonce(superuser.address);
         await Promise.all(newInvulnerables.map((i: IKeyringPair) => 
           helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));
 
         await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))
-          .to.be.rejected; // todo:collator With(/collatorSelection.TooManyInvulnerables/);
+          .to.be.rejectedWith(/collatorSelection.TooManyInvulnerables/);
         
         // restore the invulnerables to the previous state
         nonce = await helper.chain.getNonce(superuser.address);
@@ -250,7 +434,7 @@
         const [account] = await helper.arrange.createAccounts([10n], superuser);
         const invulnerables = await helper.collatorSelection.getInvulnerables();
 
-        await helper.collatorSelection.setOwnKeys(account);
+        await helper.session.setOwnKeysFromAddress(account);
         await expect(helper.collatorSelection.addInvulnerable(superuser, account.address))
           .to.be.rejectedWith(/BadOrigin/);
 
@@ -265,14 +449,19 @@
         expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
       });
     });
+  });
   
-    after(async () => {
-      // eslint-disable-next-line require-await
-      await usingPlaygrounds(async (helper) => {
-        if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;
-    
-        // todo:collator after
-      });
+  after(async () => {
+    // eslint-disable-next-line require-await
+    await usingPlaygrounds(async (helper) => {
+      if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;
+  
+      await helper.getSudo().collatorSelection.setLicenseBond(superuser, previousLicenseBond);
+      
+      const candidates = await helper.collatorSelection.getCandidates();
+      let nonce = await helper.chain.getNonce(superuser.address);
+      await Promise.all(candidates.map(candidate => 
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [candidate], true, {nonce: nonce++})));
     });
   });
 });
\ No newline at end of file
modifiedtests/src/interfaces/augment-api-consts.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-consts.ts
+++ b/tests/src/interfaces/augment-api-consts.ts
@@ -41,6 +41,18 @@
        **/
       [key: string]: Codec;
     };
+    authorship: {
+      /**
+       * The number of blocks back we should accept uncles.
+       * This means that we will deal with uncle-parents that are
+       * `UncleGenerations + 1` before `now`.
+       **/
+      uncleGenerations: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
     balances: {
       /**
        * The minimum amount required to keep an account open.
modifiedtests/src/interfaces/augment-api-errors.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -41,6 +41,40 @@
        **/
       [key: string]: AugmentedError<ApiType>;
     };
+    authorship: {
+      /**
+       * The uncle is genesis.
+       **/
+      GenesisUncle: AugmentedError<ApiType>;
+      /**
+       * The uncle parent not in the chain.
+       **/
+      InvalidUncleParent: AugmentedError<ApiType>;
+      /**
+       * The uncle isn't recent enough to be included.
+       **/
+      OldUncle: AugmentedError<ApiType>;
+      /**
+       * The uncle is too high in chain.
+       **/
+      TooHighUncle: AugmentedError<ApiType>;
+      /**
+       * Too many uncles.
+       **/
+      TooManyUncles: AugmentedError<ApiType>;
+      /**
+       * The uncle is already included.
+       **/
+      UncleAlreadyIncluded: AugmentedError<ApiType>;
+      /**
+       * Uncles already set in the block.
+       **/
+      UnclesAlreadySet: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
     balances: {
       /**
        * Beneficiary account must pre-exist
@@ -79,6 +113,64 @@
        **/
       [key: string]: AugmentedError<ApiType>;
     };
+    collatorSelection: {
+      /**
+       * User is already a candidate
+       **/
+      AlreadyCandidate: AugmentedError<ApiType>;
+      /**
+       * User already holds license to collate
+       **/
+      AlreadyHoldingLicense: AugmentedError<ApiType>;
+      /**
+       * User is already an Invulnerable
+       **/
+      AlreadyInvulnerable: AugmentedError<ApiType>;
+      /**
+       * Account has no associated validator ID
+       **/
+      NoAssociatedValidatorId: AugmentedError<ApiType>;
+      /**
+       * User does not hold a license to collate
+       **/
+      NoLicense: AugmentedError<ApiType>;
+      /**
+       * User is not a candidate
+       **/
+      NotCandidate: AugmentedError<ApiType>;
+      /**
+       * User is not an Invulnerable
+       **/
+      NotInvulnerable: AugmentedError<ApiType>;
+      /**
+       * Permission issue
+       **/
+      Permission: AugmentedError<ApiType>;
+      /**
+       * Too few invulnerables
+       **/
+      TooFewInvulnerables: AugmentedError<ApiType>;
+      /**
+       * Too many candidates
+       **/
+      TooManyCandidates: AugmentedError<ApiType>;
+      /**
+       * Too many invulnerables
+       **/
+      TooManyInvulnerables: AugmentedError<ApiType>;
+      /**
+       * Unknown error
+       **/
+      Unknown: AugmentedError<ApiType>;
+      /**
+       * Validator ID is not yet registered
+       **/
+      ValidatorNotRegistered: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
     common: {
       /**
        * Account token limit exceeded per collection
@@ -685,6 +777,32 @@
        **/
       [key: string]: AugmentedError<ApiType>;
     };
+    session: {
+      /**
+       * Registered duplicate key.
+       **/
+      DuplicatedKey: AugmentedError<ApiType>;
+      /**
+       * Invalid ownership proof.
+       **/
+      InvalidProof: AugmentedError<ApiType>;
+      /**
+       * Key setting account is not live, so it's impossible to associate keys.
+       **/
+      NoAccount: AugmentedError<ApiType>;
+      /**
+       * No associated validator ID for account.
+       **/
+      NoAssociatedValidatorId: AugmentedError<ApiType>;
+      /**
+       * No keys are associated with this account.
+       **/
+      NoKeys: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
     structure: {
       /**
        * While nesting, reached the breadth limit of nesting, exceeding the provided budget.
modifiedtests/src/interfaces/augment-api-events.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -100,6 +100,21 @@
        **/
       [key: string]: AugmentedEvent<ApiType>;
     };
+    collatorSelection: {
+      CandidateAdded: AugmentedEvent<ApiType, [accountId: AccountId32], { accountId: AccountId32 }>;
+      CandidateRemoved: AugmentedEvent<ApiType, [accountId: AccountId32], { accountId: AccountId32 }>;
+      InvulnerableAdded: AugmentedEvent<ApiType, [invulnerable: AccountId32], { invulnerable: AccountId32 }>;
+      InvulnerableRemoved: AugmentedEvent<ApiType, [invulnerable: AccountId32], { invulnerable: AccountId32 }>;
+      LicenseForfeited: AugmentedEvent<ApiType, [accountId: AccountId32, depositReturned: u128], { accountId: AccountId32, depositReturned: u128 }>;
+      LicenseObtained: AugmentedEvent<ApiType, [accountId: AccountId32, deposit: u128], { accountId: AccountId32, deposit: u128 }>;
+      NewDesiredCollators: AugmentedEvent<ApiType, [desiredCollators: u32], { desiredCollators: u32 }>;
+      NewKickThreshold: AugmentedEvent<ApiType, [lengthInBlocks: u32], { lengthInBlocks: u32 }>;
+      NewLicenseBond: AugmentedEvent<ApiType, [bondAmount: u128], { bondAmount: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
     common: {
       /**
        * Address was added to the allow list.
@@ -526,6 +541,17 @@
        **/
       [key: string]: AugmentedEvent<ApiType>;
     };
+    session: {
+      /**
+       * New session has happened. Note that the argument is the session index, not the
+       * block number as the type might suggest.
+       **/
+      NewSession: AugmentedEvent<ApiType, [sessionIndex: u32], { sessionIndex: u32 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
     structure: {
       /**
        * Executed call on behalf of the token.
modifiedtests/src/interfaces/augment-api-query.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -9,7 +9,7 @@
 import type { BTreeMap, Bytes, Option, U256, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
 import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
 import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime';
-import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportDispatchPerDispatchClassWeight, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletConfigurationAppPromotionConfiguration, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, XcmV1MultiLocation } from '@polkadot/types/lookup';
+import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportDispatchPerDispatchClassWeight, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OpalRuntimeRuntimeCommonSessionKeys, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletConfigurationAppPromotionConfiguration, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, XcmV1MultiLocation } from '@polkadot/types/lookup';
 import type { Observable } from '@polkadot/types/types';
 
 export type __AugmentedQuery<ApiType extends ApiTypes> = AugmentedQuery<ApiType, () => unknown>;
@@ -59,6 +59,24 @@
        **/
       [key: string]: QueryableStorageEntry<ApiType>;
     };
+    authorship: {
+      /**
+       * Author of current block.
+       **/
+      author: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Whether uncles were already set in this block.
+       **/
+      didSetUncles: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Uncles
+       **/
+      uncles: AugmentedQuery<ApiType, () => Observable<Vec<PalletAuthorshipUncleEntryItem>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
     balances: {
       /**
        * The Balances pallet example of storing the balance of an account.
@@ -117,6 +135,46 @@
        **/
       [key: string]: QueryableStorageEntry<ApiType>;
     };
+    collatorSelection: {
+      /**
+       * The (community, limited) collation candidates.
+       **/
+      candidates: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Desired number of candidates.
+       * 
+       * This should ideally always be less than [`Config::MaxCollators`] for weights to be correct.
+       **/
+      desiredCollators: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The invulnerable, fixed collators.
+       **/
+      invulnerables: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Collator will be kicked if it does not produce a block within the threshold (does not apply to invulnerables).
+       * 
+       * Should be a multiple of session or things will get inconsistent. todo:collator reword?
+       **/
+      kickThreshold: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Last block authored by collator.
+       **/
+      lastAuthoredBlock: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u32>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Fixed amount to deposit to become a collator.
+       * 
+       * When a collator calls `leave_intent` they immediately receive the deposit back.
+       **/
+      licenseBond: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The (community) collation license holders.
+       **/
+      licenses: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u128>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
     common: {
       /**
        * Storage of the amount of collection admins.
@@ -687,6 +745,46 @@
        **/
       [key: string]: QueryableStorageEntry<ApiType>;
     };
+    session: {
+      /**
+       * Current index of the session.
+       **/
+      currentIndex: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Indices of disabled validators.
+       * 
+       * The vec is always kept sorted so that we can find whether a given validator is
+       * disabled using binary search. It gets cleared when `on_session_ending` returns
+       * a new set of identities.
+       **/
+      disabledValidators: AugmentedQuery<ApiType, () => Observable<Vec<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The owner of a key. The key is the `KeyTypeId` + the encoded key.
+       **/
+      keyOwner: AugmentedQuery<ApiType, (arg: ITuple<[SpCoreCryptoKeyTypeId, Bytes]> | [SpCoreCryptoKeyTypeId | string | Uint8Array, Bytes | string | Uint8Array]) => Observable<Option<AccountId32>>, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]> & QueryableStorageEntry<ApiType, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]>;
+      /**
+       * The next session keys for a validator.
+       **/
+      nextKeys: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<OpalRuntimeRuntimeCommonSessionKeys>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * True if the underlying economic identities or weighting behind the validators
+       * has changed in the queued validator set.
+       **/
+      queuedChanged: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The queued keys for the next session. When the next session begins, these keys
+       * will be used to determine the validator's session keys.
+       **/
+      queuedKeys: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[AccountId32, OpalRuntimeRuntimeCommonSessionKeys]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current set of validators.
+       **/
+      validators: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
     structure: {
       /**
        * 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
@@ -9,7 +9,7 @@
 import type { Bytes, Compact, Option, U256, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
 import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types';
 import type { AccountId32, Call, H160, H256, MultiAddress, Perbill, Permill } from '@polkadot/types/interfaces/runtime';
-import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumLog, EthereumTransactionTransactionV2, OrmlVestingVestingSchedule, PalletConfigurationAppPromotionConfiguration, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsPartEquippableList, RmrkTraitsPartPartType, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, SpWeightsWeightV2Weight, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
+import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumLog, EthereumTransactionTransactionV2, OpalRuntimeRuntimeCommonSessionKeys, OrmlVestingVestingSchedule, PalletConfigurationAppPromotionConfiguration, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsPartEquippableList, RmrkTraitsPartPartType, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, SpRuntimeHeader, SpWeightsWeightV2Weight, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
 
 export type __AugmentedSubmittable = AugmentedSubmittable<() => unknown>;
 export type __SubmittableExtrinsic<ApiType extends ApiTypes> = SubmittableExtrinsic<ApiType>;
@@ -119,6 +119,16 @@
        **/
       [key: string]: SubmittableExtrinsicFunction<ApiType>;
     };
+    authorship: {
+      /**
+       * Provide a set of uncles.
+       **/
+      setUncles: AugmentedSubmittable<(newUncles: Vec<SpRuntimeHeader> | (SpRuntimeHeader | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<SpRuntimeHeader>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
     balances: {
       /**
        * Exactly as `transfer`, except the origin must be root and the source account may be
@@ -214,6 +224,71 @@
        **/
       [key: string]: SubmittableExtrinsicFunction<ApiType>;
     };
+    collatorSelection: {
+      /**
+       * Add a collator to the list of invulnerable (fixed) collators.
+       **/
+      addInvulnerable: AugmentedSubmittable<(updated: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+      /**
+       * Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.
+       * Note that the collator can only leave on session change.
+       * The `LicenseBond` will be unreserved and returned immediately.
+       * 
+       * This call is not available to `Invulnerable` collators.
+       **/
+      forceRevokeLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+      /**
+       * Purchase a license on block collation for this account.
+       * It does not make it a collator candidate, use `onboard` afterward. The account must
+       * (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.
+       * 
+       * This call is not available to `Invulnerable` collators.
+       **/
+      getLicense: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * Deregister `origin` as a collator candidate. Note that the collator can only leave on
+       * session change. The license to `onboard` later at any other time will remain.
+       * 
+       * This call will fail if the total number of candidates would drop below `MinCandidates`. todo:collator maybe not
+       **/
+      offboard: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * Register this account as a candidate for collators for next sessions.
+       * The account must already hold a license, and cannot offboard immediately during a session.
+       * 
+       * This call is not available to `Invulnerable` collators.
+       **/
+      onboard: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.
+       * 
+       * This call is not available to `Invulnerable` collators.
+       **/
+      releaseLicense: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * Remove a collator from the list of invulnerable (fixed) collators.
+       **/
+      removeInvulnerable: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+      /**
+       * Set the ideal number of collators. If lowering this number,
+       * then the number of running collators could be higher than this figure.
+       * Aside from that edge case, there should be no other way to have more collators than the desired number.
+       **/
+      setDesiredCollators: AugmentedSubmittable<(max: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * Set the length of the kick threshold.
+       * Note that if the length is not a multiple of the session period, it might get inconsistent.
+       **/
+      setKickThreshold: AugmentedSubmittable<(kickThreshold: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * Set the candidacy bond amount.
+       **/
+      setLicenseBond: AugmentedSubmittable<(bond: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
     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>]>;
@@ -839,6 +914,48 @@
        **/
       [key: string]: SubmittableExtrinsicFunction<ApiType>;
     };
+    session: {
+      /**
+       * Removes any session key(s) of the function caller.
+       * 
+       * This doesn't take effect until the next session.
+       * 
+       * The dispatch origin of this function must be Signed and the account must be either be
+       * convertible to a validator ID using the chain's typical addressing system (this usually
+       * means being a controller account) or directly convertible into a validator ID (which
+       * usually means being a stash account).
+       * 
+       * # <weight>
+       * - Complexity: `O(1)` in number of key types. Actual cost depends on the number of length
+       * of `T::Keys::key_ids()` which is fixed.
+       * - DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account`
+       * - DbWrites: `NextKeys`, `origin account`
+       * - DbWrites per key id: `KeyOwner`
+       * # </weight>
+       **/
+      purgeKeys: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * Sets the session key(s) of the function caller to `keys`.
+       * Allows an account to set its session key prior to becoming a validator.
+       * This doesn't take effect until the next session.
+       * 
+       * The dispatch origin of this function must be signed.
+       * 
+       * # <weight>
+       * - Complexity: `O(1)`. Actual cost depends on the number of length of
+       * `T::Keys::key_ids()` which is fixed.
+       * - DbReads: `origin account`, `T::ValidatorIdOf`, `NextKeys`
+       * - DbWrites: `origin account`, `NextKeys`
+       * - DbReads per key id: `KeyOwner`
+       * - DbWrites per key id: `KeyOwner`
+       * # </weight>
+       **/
+      setKeys: AugmentedSubmittable<(keys: OpalRuntimeRuntimeCommonSessionKeys | { aura?: any } | string | Uint8Array, proof: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeRuntimeCommonSessionKeys, Bytes]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
     structure: {
       /**
        * Generic tx
@@ -1432,6 +1549,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 +1608,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
@@ -5,7 +5,7 @@
 // this is required to allow for ambient/previous definitions
 import '@polkadot/types/types/registry';
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletAuthorshipCall, PalletAuthorshipError, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeBlakeTwo256, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeHeader, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
 import type { Data, StorageKey } from '@polkadot/types';
 import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
 import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
@@ -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;
@@ -771,6 +773,7 @@
     OldV1SessionInfo: OldV1SessionInfo;
     OpalRuntimeRuntime: OpalRuntimeRuntime;
     OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance;
+    OpalRuntimeRuntimeCommonSessionKeys: OpalRuntimeRuntimeCommonSessionKeys;
     OpaqueCall: OpaqueCall;
     OpaqueKeyOwnershipProof: OpaqueKeyOwnershipProof;
     OpaqueMetadata: OpaqueMetadata;
@@ -815,6 +818,9 @@
     PalletAppPromotionCall: PalletAppPromotionCall;
     PalletAppPromotionError: PalletAppPromotionError;
     PalletAppPromotionEvent: PalletAppPromotionEvent;
+    PalletAuthorshipCall: PalletAuthorshipCall;
+    PalletAuthorshipError: PalletAuthorshipError;
+    PalletAuthorshipUncleEntryItem: PalletAuthorshipUncleEntryItem;
     PalletBalancesAccountData: PalletBalancesAccountData;
     PalletBalancesBalanceLock: PalletBalancesBalanceLock;
     PalletBalancesCall: PalletBalancesCall;
@@ -825,6 +831,9 @@
     PalletBalancesReserveData: PalletBalancesReserveData;
     PalletCallMetadataLatest: PalletCallMetadataLatest;
     PalletCallMetadataV14: PalletCallMetadataV14;
+    PalletCollatorSelectionCall: PalletCollatorSelectionCall;
+    PalletCollatorSelectionError: PalletCollatorSelectionError;
+    PalletCollatorSelectionEvent: PalletCollatorSelectionEvent;
     PalletCommonError: PalletCommonError;
     PalletCommonEvent: PalletCommonEvent;
     PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration;
@@ -875,6 +884,9 @@
     PalletRmrkEquipCall: PalletRmrkEquipCall;
     PalletRmrkEquipError: PalletRmrkEquipError;
     PalletRmrkEquipEvent: PalletRmrkEquipEvent;
+    PalletSessionCall: PalletSessionCall;
+    PalletSessionError: PalletSessionError;
+    PalletSessionEvent: PalletSessionEvent;
     PalletsOrigin: PalletsOrigin;
     PalletStorageMetadataLatest: PalletStorageMetadataLatest;
     PalletStorageMetadataV14: PalletStorageMetadataV14;
@@ -1057,6 +1069,8 @@
     RpcMethods: RpcMethods;
     RuntimeDbWeight: RuntimeDbWeight;
     RuntimeDispatchInfo: RuntimeDispatchInfo;
+    RuntimeDispatchInfoV1: RuntimeDispatchInfoV1;
+    RuntimeDispatchInfoV2: RuntimeDispatchInfoV2;
     RuntimeVersion: RuntimeVersion;
     RuntimeVersionApi: RuntimeVersionApi;
     RuntimeVersionPartial: RuntimeVersionPartial;
@@ -1172,14 +1186,19 @@
     SolutionSupports: SolutionSupports;
     SpanIndex: SpanIndex;
     SpanRecord: SpanRecord;
+    SpConsensusAuraSr25519AppSr25519Public: SpConsensusAuraSr25519AppSr25519Public;
+    SpCoreCryptoKeyTypeId: SpCoreCryptoKeyTypeId;
     SpCoreEcdsaSignature: SpCoreEcdsaSignature;
     SpCoreEd25519Signature: SpCoreEd25519Signature;
+    SpCoreSr25519Public: SpCoreSr25519Public;
     SpCoreSr25519Signature: SpCoreSr25519Signature;
     SpecVersion: SpecVersion;
     SpRuntimeArithmeticError: SpRuntimeArithmeticError;
+    SpRuntimeBlakeTwo256: SpRuntimeBlakeTwo256;
     SpRuntimeDigest: SpRuntimeDigest;
     SpRuntimeDigestDigestItem: SpRuntimeDigestDigestItem;
     SpRuntimeDispatchError: SpRuntimeDispatchError;
+    SpRuntimeHeader: SpRuntimeHeader;
     SpRuntimeModuleError: SpRuntimeModuleError;
     SpRuntimeMultiSignature: SpRuntimeMultiSignature;
     SpRuntimeTokenError: SpRuntimeTokenError;
modifiedtests/src/interfaces/default/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -699,6 +699,11 @@
 /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance */
 export interface OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance extends Null {}
 
+/** @name OpalRuntimeRuntimeCommonSessionKeys */
+export interface OpalRuntimeRuntimeCommonSessionKeys extends Struct {
+  readonly aura: SpConsensusAuraSr25519AppSr25519Public;
+}
+
 /** @name OrmlTokensAccountData */
 export interface OrmlTokensAccountData extends Struct {
   readonly free: u128;
@@ -1056,6 +1061,36 @@
   readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
 }
 
+/** @name PalletAuthorshipCall */
+export interface PalletAuthorshipCall extends Enum {
+  readonly isSetUncles: boolean;
+  readonly asSetUncles: {
+    readonly newUncles: Vec<SpRuntimeHeader>;
+  } & Struct;
+  readonly type: 'SetUncles';
+}
+
+/** @name PalletAuthorshipError */
+export interface PalletAuthorshipError extends Enum {
+  readonly isInvalidUncleParent: boolean;
+  readonly isUnclesAlreadySet: boolean;
+  readonly isTooManyUncles: boolean;
+  readonly isGenesisUncle: boolean;
+  readonly isTooHighUncle: boolean;
+  readonly isUncleAlreadyIncluded: boolean;
+  readonly isOldUncle: boolean;
+  readonly type: 'InvalidUncleParent' | 'UnclesAlreadySet' | 'TooManyUncles' | 'GenesisUncle' | 'TooHighUncle' | 'UncleAlreadyIncluded' | 'OldUncle';
+}
+
+/** @name PalletAuthorshipUncleEntryItem */
+export interface PalletAuthorshipUncleEntryItem extends Enum {
+  readonly isInclusionHeight: boolean;
+  readonly asInclusionHeight: u32;
+  readonly isUncle: boolean;
+  readonly asUncle: ITuple<[H256, Option<AccountId32>]>;
+  readonly type: 'InclusionHeight' | 'Uncle';
+}
+
 /** @name PalletBalancesAccountData */
 export interface PalletBalancesAccountData extends Struct {
   readonly free: u128;
@@ -1201,6 +1236,100 @@
   readonly amount: u128;
 }
 
+/** @name PalletCollatorSelectionCall */
+export interface PalletCollatorSelectionCall extends Enum {
+  readonly isAddInvulnerable: boolean;
+  readonly asAddInvulnerable: {
+    readonly new_: AccountId32;
+  } & Struct;
+  readonly isRemoveInvulnerable: boolean;
+  readonly asRemoveInvulnerable: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly isSetDesiredCollators: boolean;
+  readonly asSetDesiredCollators: {
+    readonly max: u32;
+  } & Struct;
+  readonly isSetLicenseBond: boolean;
+  readonly asSetLicenseBond: {
+    readonly bond: u128;
+  } & Struct;
+  readonly isSetKickThreshold: boolean;
+  readonly asSetKickThreshold: {
+    readonly kickThreshold: u32;
+  } & Struct;
+  readonly isGetLicense: boolean;
+  readonly isOnboard: boolean;
+  readonly isOffboard: boolean;
+  readonly isReleaseLicense: boolean;
+  readonly isForceRevokeLicense: boolean;
+  readonly asForceRevokeLicense: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'SetDesiredCollators' | 'SetLicenseBond' | 'SetKickThreshold' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceRevokeLicense';
+}
+
+/** @name PalletCollatorSelectionError */
+export interface PalletCollatorSelectionError extends Enum {
+  readonly isTooManyCandidates: boolean;
+  readonly isUnknown: boolean;
+  readonly isPermission: boolean;
+  readonly isAlreadyHoldingLicense: boolean;
+  readonly isNoLicense: boolean;
+  readonly isAlreadyCandidate: boolean;
+  readonly isNotCandidate: boolean;
+  readonly isTooManyInvulnerables: boolean;
+  readonly isTooFewInvulnerables: boolean;
+  readonly isAlreadyInvulnerable: boolean;
+  readonly isNotInvulnerable: boolean;
+  readonly isNoAssociatedValidatorId: boolean;
+  readonly isValidatorNotRegistered: boolean;
+  readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered';
+}
+
+/** @name PalletCollatorSelectionEvent */
+export interface PalletCollatorSelectionEvent extends Enum {
+  readonly isNewDesiredCollators: boolean;
+  readonly asNewDesiredCollators: {
+    readonly desiredCollators: u32;
+  } & Struct;
+  readonly isNewLicenseBond: boolean;
+  readonly asNewLicenseBond: {
+    readonly bondAmount: u128;
+  } & Struct;
+  readonly isNewKickThreshold: boolean;
+  readonly asNewKickThreshold: {
+    readonly lengthInBlocks: u32;
+  } & Struct;
+  readonly isInvulnerableAdded: boolean;
+  readonly asInvulnerableAdded: {
+    readonly invulnerable: AccountId32;
+  } & Struct;
+  readonly isInvulnerableRemoved: boolean;
+  readonly asInvulnerableRemoved: {
+    readonly invulnerable: AccountId32;
+  } & Struct;
+  readonly isLicenseObtained: boolean;
+  readonly asLicenseObtained: {
+    readonly accountId: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isLicenseForfeited: boolean;
+  readonly asLicenseForfeited: {
+    readonly accountId: AccountId32;
+    readonly depositReturned: u128;
+  } & Struct;
+  readonly isCandidateAdded: boolean;
+  readonly asCandidateAdded: {
+    readonly accountId: AccountId32;
+  } & Struct;
+  readonly isCandidateRemoved: boolean;
+  readonly asCandidateRemoved: {
+    readonly accountId: AccountId32;
+  } & Struct;
+  readonly type: 'NewDesiredCollators' | 'NewLicenseBond' | 'NewKickThreshold' | 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseForfeited' | 'CandidateAdded' | 'CandidateRemoved';
+}
+
 /** @name PalletCommonError */
 export interface PalletCommonError extends Enum {
   readonly isCollectionNotFound: boolean;
@@ -1938,6 +2067,36 @@
   readonly type: 'BaseCreated' | 'EquippablesUpdated';
 }
 
+/** @name PalletSessionCall */
+export interface PalletSessionCall extends Enum {
+  readonly isSetKeys: boolean;
+  readonly asSetKeys: {
+    readonly keys_: OpalRuntimeRuntimeCommonSessionKeys;
+    readonly proof: Bytes;
+  } & Struct;
+  readonly isPurgeKeys: boolean;
+  readonly type: 'SetKeys' | 'PurgeKeys';
+}
+
+/** @name PalletSessionError */
+export interface PalletSessionError extends Enum {
+  readonly isInvalidProof: boolean;
+  readonly isNoAssociatedValidatorId: boolean;
+  readonly isDuplicatedKey: boolean;
+  readonly isNoKeys: boolean;
+  readonly isNoAccount: boolean;
+  readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount';
+}
+
+/** @name PalletSessionEvent */
+export interface PalletSessionEvent extends Enum {
+  readonly isNewSession: boolean;
+  readonly asNewSession: {
+    readonly sessionIndex: u32;
+  } & Struct;
+  readonly type: 'NewSession';
+}
+
 /** @name PalletStructureCall */
 export interface PalletStructureCall extends Null {}
 
@@ -2319,12 +2478,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 */
@@ -2665,12 +2828,21 @@
   readonly value: Bytes;
 }
 
+/** @name SpConsensusAuraSr25519AppSr25519Public */
+export interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {}
+
+/** @name SpCoreCryptoKeyTypeId */
+export interface SpCoreCryptoKeyTypeId extends U8aFixed {}
+
 /** @name SpCoreEcdsaSignature */
 export interface SpCoreEcdsaSignature extends U8aFixed {}
 
 /** @name SpCoreEd25519Signature */
 export interface SpCoreEd25519Signature extends U8aFixed {}
 
+/** @name SpCoreSr25519Public */
+export interface SpCoreSr25519Public extends U8aFixed {}
+
 /** @name SpCoreSr25519Signature */
 export interface SpCoreSr25519Signature extends U8aFixed {}
 
@@ -2682,6 +2854,9 @@
   readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero';
 }
 
+/** @name SpRuntimeBlakeTwo256 */
+export interface SpRuntimeBlakeTwo256 extends Null {}
+
 /** @name SpRuntimeDigest */
 export interface SpRuntimeDigest extends Struct {
   readonly logs: Vec<SpRuntimeDigestDigestItem>;
@@ -2723,6 +2898,15 @@
   readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable';
 }
 
+/** @name SpRuntimeHeader */
+export interface SpRuntimeHeader extends Struct {
+  readonly parentHash: H256;
+  readonly number: Compact<u32>;
+  readonly stateRoot: H256;
+  readonly extrinsicsRoot: H256;
+  readonly digest: SpRuntimeDigest;
+}
+
 /** @name SpRuntimeModuleError */
 export interface SpRuntimeModuleError extends Struct {
   readonly index: u8;
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -184,8 +184,54 @@
     }
   },
   /**
-   * Lookup30: pallet_balances::pallet::Event<T, I>
+   * Lookup30: pallet_collator_selection::pallet::Event<T>
+   **/
+  PalletCollatorSelectionEvent: {
+    _enum: {
+      NewDesiredCollators: {
+        desiredCollators: 'u32',
+      },
+      NewLicenseBond: {
+        bondAmount: 'u128',
+      },
+      NewKickThreshold: {
+        lengthInBlocks: 'u32',
+      },
+      InvulnerableAdded: {
+        invulnerable: 'AccountId32',
+      },
+      InvulnerableRemoved: {
+        invulnerable: 'AccountId32',
+      },
+      LicenseObtained: {
+        accountId: 'AccountId32',
+        deposit: 'u128',
+      },
+      LicenseForfeited: {
+        accountId: 'AccountId32',
+        depositReturned: 'u128',
+      },
+      CandidateAdded: {
+        accountId: 'AccountId32',
+      },
+      CandidateRemoved: {
+        accountId: 'AccountId32'
+      }
+    }
+  },
+  /**
+   * Lookup31: pallet_session::pallet::Event
    **/
+  PalletSessionEvent: {
+    _enum: {
+      NewSession: {
+        sessionIndex: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup32: pallet_balances::pallet::Event<T, I>
+   **/
   PalletBalancesEvent: {
     _enum: {
       Endowed: {
@@ -235,13 +281,13 @@
     }
   },
   /**
-   * Lookup31: frame_support::traits::tokens::misc::BalanceStatus
+   * Lookup33: frame_support::traits::tokens::misc::BalanceStatus
    **/
   FrameSupportTokensMiscBalanceStatus: {
     _enum: ['Free', 'Reserved']
   },
   /**
-   * Lookup32: pallet_transaction_payment::pallet::Event<T>
+   * Lookup34: pallet_transaction_payment::pallet::Event<T>
    **/
   PalletTransactionPaymentEvent: {
     _enum: {
@@ -253,7 +299,7 @@
     }
   },
   /**
-   * Lookup33: pallet_treasury::pallet::Event<T, I>
+   * Lookup35: pallet_treasury::pallet::Event<T, I>
    **/
   PalletTreasuryEvent: {
     _enum: {
@@ -289,7 +335,7 @@
     }
   },
   /**
-   * Lookup34: pallet_sudo::pallet::Event<T>
+   * Lookup36: pallet_sudo::pallet::Event<T>
    **/
   PalletSudoEvent: {
     _enum: {
@@ -305,7 +351,7 @@
     }
   },
   /**
-   * Lookup38: orml_vesting::module::Event<T>
+   * Lookup40: orml_vesting::module::Event<T>
    **/
   OrmlVestingModuleEvent: {
     _enum: {
@@ -324,7 +370,7 @@
     }
   },
   /**
-   * Lookup39: orml_vesting::VestingSchedule<BlockNumber, Balance>
+   * Lookup41: orml_vesting::VestingSchedule<BlockNumber, Balance>
    **/
   OrmlVestingVestingSchedule: {
     start: 'u32',
@@ -333,7 +379,7 @@
     perPeriod: 'Compact<u128>'
   },
   /**
-   * Lookup41: orml_xtokens::module::Event<T>
+   * Lookup43: orml_xtokens::module::Event<T>
    **/
   OrmlXtokensModuleEvent: {
     _enum: {
@@ -346,18 +392,18 @@
     }
   },
   /**
-   * Lookup42: xcm::v1::multiasset::MultiAssets
+   * Lookup44: xcm::v1::multiasset::MultiAssets
    **/
   XcmV1MultiassetMultiAssets: 'Vec<XcmV1MultiAsset>',
   /**
-   * Lookup44: xcm::v1::multiasset::MultiAsset
+   * Lookup46: xcm::v1::multiasset::MultiAsset
    **/
   XcmV1MultiAsset: {
     id: 'XcmV1MultiassetAssetId',
     fun: 'XcmV1MultiassetFungibility'
   },
   /**
-   * Lookup45: xcm::v1::multiasset::AssetId
+   * Lookup47: xcm::v1::multiasset::AssetId
    **/
   XcmV1MultiassetAssetId: {
     _enum: {
@@ -366,14 +412,14 @@
     }
   },
   /**
-   * Lookup46: xcm::v1::multilocation::MultiLocation
+   * Lookup48: xcm::v1::multilocation::MultiLocation
    **/
   XcmV1MultiLocation: {
     parents: 'u8',
     interior: 'XcmV1MultilocationJunctions'
   },
   /**
-   * Lookup47: xcm::v1::multilocation::Junctions
+   * Lookup49: xcm::v1::multilocation::Junctions
    **/
   XcmV1MultilocationJunctions: {
     _enum: {
@@ -389,7 +435,7 @@
     }
   },
   /**
-   * Lookup48: xcm::v1::junction::Junction
+   * Lookup50: xcm::v1::junction::Junction
    **/
   XcmV1Junction: {
     _enum: {
@@ -417,7 +463,7 @@
     }
   },
   /**
-   * Lookup50: xcm::v0::junction::NetworkId
+   * Lookup52: xcm::v0::junction::NetworkId
    **/
   XcmV0JunctionNetworkId: {
     _enum: {
@@ -428,7 +474,7 @@
     }
   },
   /**
-   * Lookup53: xcm::v0::junction::BodyId
+   * Lookup55: xcm::v0::junction::BodyId
    **/
   XcmV0JunctionBodyId: {
     _enum: {
@@ -442,7 +488,7 @@
     }
   },
   /**
-   * Lookup54: xcm::v0::junction::BodyPart
+   * Lookup56: xcm::v0::junction::BodyPart
    **/
   XcmV0JunctionBodyPart: {
     _enum: {
@@ -465,7 +511,7 @@
     }
   },
   /**
-   * Lookup55: xcm::v1::multiasset::Fungibility
+   * Lookup57: xcm::v1::multiasset::Fungibility
    **/
   XcmV1MultiassetFungibility: {
     _enum: {
@@ -474,7 +520,7 @@
     }
   },
   /**
-   * Lookup56: xcm::v1::multiasset::AssetInstance
+   * Lookup58: xcm::v1::multiasset::AssetInstance
    **/
   XcmV1MultiassetAssetInstance: {
     _enum: {
@@ -488,7 +534,7 @@
     }
   },
   /**
-   * Lookup59: orml_tokens::module::Event<T>
+   * Lookup61: orml_tokens::module::Event<T>
    **/
   OrmlTokensModuleEvent: {
     _enum: {
@@ -565,7 +611,7 @@
     }
   },
   /**
-   * Lookup60: pallet_foreign_assets::AssetIds
+   * Lookup62: pallet_foreign_assets::AssetIds
    **/
   PalletForeignAssetsAssetIds: {
     _enum: {
@@ -574,13 +620,13 @@
     }
   },
   /**
-   * Lookup61: pallet_foreign_assets::NativeCurrency
+   * Lookup63: pallet_foreign_assets::NativeCurrency
    **/
   PalletForeignAssetsNativeCurrency: {
     _enum: ['Here', 'Parent']
   },
   /**
-   * Lookup62: cumulus_pallet_xcmp_queue::pallet::Event<T>
+   * Lookup64: cumulus_pallet_xcmp_queue::pallet::Event<T>
    **/
   CumulusPalletXcmpQueueEvent: {
     _enum: {
@@ -618,7 +664,7 @@
     }
   },
   /**
-   * Lookup64: xcm::v2::traits::Error
+   * Lookup66: xcm::v2::traits::Error
    **/
   XcmV2TraitsError: {
     _enum: {
@@ -651,7 +697,7 @@
     }
   },
   /**
-   * Lookup66: pallet_xcm::pallet::Event<T>
+   * Lookup68: pallet_xcm::pallet::Event<T>
    **/
   PalletXcmEvent: {
     _enum: {
@@ -675,7 +721,7 @@
     }
   },
   /**
-   * Lookup67: xcm::v2::traits::Outcome
+   * Lookup69: xcm::v2::traits::Outcome
    **/
   XcmV2TraitsOutcome: {
     _enum: {
@@ -685,11 +731,11 @@
     }
   },
   /**
-   * Lookup68: xcm::v2::Xcm<RuntimeCall>
+   * Lookup70: xcm::v2::Xcm<RuntimeCall>
    **/
   XcmV2Xcm: 'Vec<XcmV2Instruction>',
   /**
-   * Lookup70: xcm::v2::Instruction<RuntimeCall>
+   * Lookup72: xcm::v2::Instruction<RuntimeCall>
    **/
   XcmV2Instruction: {
     _enum: {
@@ -787,7 +833,7 @@
     }
   },
   /**
-   * Lookup71: xcm::v2::Response
+   * Lookup73: xcm::v2::Response
    **/
   XcmV2Response: {
     _enum: {
@@ -798,19 +844,19 @@
     }
   },
   /**
-   * Lookup74: xcm::v0::OriginKind
+   * Lookup76: xcm::v0::OriginKind
    **/
   XcmV0OriginKind: {
     _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm']
   },
   /**
-   * Lookup75: xcm::double_encoded::DoubleEncoded<T>
+   * Lookup77: xcm::double_encoded::DoubleEncoded<T>
    **/
   XcmDoubleEncoded: {
     encoded: 'Bytes'
   },
   /**
-   * Lookup76: xcm::v1::multiasset::MultiAssetFilter
+   * Lookup78: xcm::v1::multiasset::MultiAssetFilter
    **/
   XcmV1MultiassetMultiAssetFilter: {
     _enum: {
@@ -819,7 +865,7 @@
     }
   },
   /**
-   * Lookup77: xcm::v1::multiasset::WildMultiAsset
+   * Lookup79: xcm::v1::multiasset::WildMultiAsset
    **/
   XcmV1MultiassetWildMultiAsset: {
     _enum: {
@@ -831,13 +877,13 @@
     }
   },
   /**
-   * Lookup78: xcm::v1::multiasset::WildFungibility
+   * Lookup80: xcm::v1::multiasset::WildFungibility
    **/
   XcmV1MultiassetWildFungibility: {
     _enum: ['Fungible', 'NonFungible']
   },
   /**
-   * Lookup79: xcm::v2::WeightLimit
+   * Lookup81: xcm::v2::WeightLimit
    **/
   XcmV2WeightLimit: {
     _enum: {
@@ -846,7 +892,7 @@
     }
   },
   /**
-   * Lookup81: xcm::VersionedMultiAssets
+   * Lookup83: xcm::VersionedMultiAssets
    **/
   XcmVersionedMultiAssets: {
     _enum: {
@@ -855,7 +901,7 @@
     }
   },
   /**
-   * Lookup83: xcm::v0::multi_asset::MultiAsset
+   * Lookup85: xcm::v0::multi_asset::MultiAsset
    **/
   XcmV0MultiAsset: {
     _enum: {
@@ -894,7 +940,7 @@
     }
   },
   /**
-   * Lookup84: xcm::v0::multi_location::MultiLocation
+   * Lookup86: xcm::v0::multi_location::MultiLocation
    **/
   XcmV0MultiLocation: {
     _enum: {
@@ -910,7 +956,7 @@
     }
   },
   /**
-   * Lookup85: xcm::v0::junction::Junction
+   * Lookup87: xcm::v0::junction::Junction
    **/
   XcmV0Junction: {
     _enum: {
@@ -939,7 +985,7 @@
     }
   },
   /**
-   * Lookup86: xcm::VersionedMultiLocation
+   * Lookup88: xcm::VersionedMultiLocation
    **/
   XcmVersionedMultiLocation: {
     _enum: {
@@ -948,7 +994,7 @@
     }
   },
   /**
-   * Lookup87: cumulus_pallet_xcm::pallet::Event<T>
+   * Lookup89: cumulus_pallet_xcm::pallet::Event<T>
    **/
   CumulusPalletXcmEvent: {
     _enum: {
@@ -958,7 +1004,7 @@
     }
   },
   /**
-   * Lookup88: cumulus_pallet_dmp_queue::pallet::Event<T>
+   * Lookup90: cumulus_pallet_dmp_queue::pallet::Event<T>
    **/
   CumulusPalletDmpQueueEvent: {
     _enum: {
@@ -989,7 +1035,7 @@
     }
   },
   /**
-   * Lookup89: pallet_common::pallet::Event<T>
+   * Lookup91: pallet_common::pallet::Event<T>
    **/
   PalletCommonEvent: {
     _enum: {
@@ -1018,7 +1064,7 @@
     }
   },
   /**
-   * Lookup92: pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>
+   * Lookup94: pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>
    **/
   PalletEvmAccountBasicCrossAccountIdRepr: {
     _enum: {
@@ -1027,7 +1073,7 @@
     }
   },
   /**
-   * Lookup96: pallet_structure::pallet::Event<T>
+   * Lookup98: pallet_structure::pallet::Event<T>
    **/
   PalletStructureEvent: {
     _enum: {
@@ -1035,7 +1081,7 @@
     }
   },
   /**
-   * Lookup97: pallet_rmrk_core::pallet::Event<T>
+   * Lookup99: pallet_rmrk_core::pallet::Event<T>
    **/
   PalletRmrkCoreEvent: {
     _enum: {
@@ -1112,7 +1158,7 @@
     }
   },
   /**
-   * Lookup98: rmrk_traits::nft::AccountIdOrCollectionNftTuple<sp_core::crypto::AccountId32>
+   * Lookup100: rmrk_traits::nft::AccountIdOrCollectionNftTuple<sp_core::crypto::AccountId32>
    **/
   RmrkTraitsNftAccountIdOrCollectionNftTuple: {
     _enum: {
@@ -1121,7 +1167,7 @@
     }
   },
   /**
-   * Lookup102: pallet_rmrk_equip::pallet::Event<T>
+   * Lookup104: pallet_rmrk_equip::pallet::Event<T>
    **/
   PalletRmrkEquipEvent: {
     _enum: {
@@ -1136,7 +1182,7 @@
     }
   },
   /**
-   * Lookup103: pallet_app_promotion::pallet::Event<T>
+   * Lookup105: pallet_app_promotion::pallet::Event<T>
    **/
   PalletAppPromotionEvent: {
     _enum: {
@@ -1147,7 +1193,7 @@
     }
   },
   /**
-   * Lookup104: pallet_foreign_assets::module::Event<T>
+   * Lookup106: pallet_foreign_assets::module::Event<T>
    **/
   PalletForeignAssetsModuleEvent: {
     _enum: {
@@ -1172,7 +1218,7 @@
     }
   },
   /**
-   * Lookup105: pallet_foreign_assets::module::AssetMetadata<Balance>
+   * Lookup107: pallet_foreign_assets::module::AssetMetadata<Balance>
    **/
   PalletForeignAssetsModuleAssetMetadata: {
     name: 'Bytes',
@@ -1181,7 +1227,7 @@
     minimalBalance: 'u128'
   },
   /**
-   * Lookup106: pallet_evm::pallet::Event<T>
+   * Lookup108: pallet_evm::pallet::Event<T>
    **/
   PalletEvmEvent: {
     _enum: {
@@ -1203,7 +1249,7 @@
     }
   },
   /**
-   * Lookup107: ethereum::log::Log
+   * Lookup109: ethereum::log::Log
    **/
   EthereumLog: {
     address: 'H160',
@@ -1211,7 +1257,7 @@
     data: 'Bytes'
   },
   /**
-   * Lookup109: pallet_ethereum::pallet::Event
+   * Lookup111: pallet_ethereum::pallet::Event
    **/
   PalletEthereumEvent: {
     _enum: {
@@ -1224,7 +1270,7 @@
     }
   },
   /**
-   * Lookup110: evm_core::error::ExitReason
+   * Lookup112: evm_core::error::ExitReason
    **/
   EvmCoreErrorExitReason: {
     _enum: {
@@ -1235,13 +1281,13 @@
     }
   },
   /**
-   * Lookup111: evm_core::error::ExitSucceed
+   * Lookup113: evm_core::error::ExitSucceed
    **/
   EvmCoreErrorExitSucceed: {
     _enum: ['Stopped', 'Returned', 'Suicided']
   },
   /**
-   * Lookup112: evm_core::error::ExitError
+   * Lookup114: evm_core::error::ExitError
    **/
   EvmCoreErrorExitError: {
     _enum: {
@@ -1263,13 +1309,13 @@
     }
   },
   /**
-   * Lookup115: evm_core::error::ExitRevert
+   * Lookup117: evm_core::error::ExitRevert
    **/
   EvmCoreErrorExitRevert: {
     _enum: ['Reverted']
   },
   /**
-   * Lookup116: evm_core::error::ExitFatal
+   * Lookup118: evm_core::error::ExitFatal
    **/
   EvmCoreErrorExitFatal: {
     _enum: {
@@ -1280,7 +1326,7 @@
     }
   },
   /**
-   * Lookup117: pallet_evm_contract_helpers::pallet::Event<T>
+   * Lookup119: pallet_evm_contract_helpers::pallet::Event<T>
    **/
   PalletEvmContractHelpersEvent: {
     _enum: {
@@ -1290,25 +1336,25 @@
     }
   },
   /**
-   * Lookup118: pallet_evm_migration::pallet::Event<T>
+   * Lookup120: pallet_evm_migration::pallet::Event<T>
    **/
   PalletEvmMigrationEvent: {
     _enum: ['TestEvent']
   },
   /**
-   * Lookup119: pallet_maintenance::pallet::Event<T>
+   * Lookup121: pallet_maintenance::pallet::Event<T>
    **/
   PalletMaintenanceEvent: {
     _enum: ['MaintenanceEnabled', 'MaintenanceDisabled']
   },
   /**
-   * Lookup120: pallet_test_utils::pallet::Event<T>
+   * Lookup122: pallet_test_utils::pallet::Event<T>
    **/
   PalletTestUtilsEvent: {
     _enum: ['ValueIsSet', 'ShouldRollback', 'BatchCompleted']
   },
   /**
-   * Lookup121: frame_system::Phase
+   * Lookup123: frame_system::Phase
    **/
   FrameSystemPhase: {
     _enum: {
@@ -1318,14 +1364,14 @@
     }
   },
   /**
-   * Lookup124: frame_system::LastRuntimeUpgradeInfo
+   * Lookup126: frame_system::LastRuntimeUpgradeInfo
    **/
   FrameSystemLastRuntimeUpgradeInfo: {
     specVersion: 'Compact<u32>',
     specName: 'Text'
   },
   /**
-   * Lookup125: frame_system::pallet::Call<T>
+   * Lookup127: frame_system::pallet::Call<T>
    **/
   FrameSystemCall: {
     _enum: {
@@ -1363,7 +1409,7 @@
     }
   },
   /**
-   * Lookup130: frame_system::limits::BlockWeights
+   * Lookup132: frame_system::limits::BlockWeights
    **/
   FrameSystemLimitsBlockWeights: {
     baseBlock: 'SpWeightsWeightV2Weight',
@@ -1371,7 +1417,7 @@
     perClass: 'FrameSupportDispatchPerDispatchClassWeightsPerClass'
   },
   /**
-   * Lookup131: frame_support::dispatch::PerDispatchClass<frame_system::limits::WeightsPerClass>
+   * Lookup133: frame_support::dispatch::PerDispatchClass<frame_system::limits::WeightsPerClass>
    **/
   FrameSupportDispatchPerDispatchClassWeightsPerClass: {
     normal: 'FrameSystemLimitsWeightsPerClass',
@@ -1379,7 +1425,7 @@
     mandatory: 'FrameSystemLimitsWeightsPerClass'
   },
   /**
-   * Lookup132: frame_system::limits::WeightsPerClass
+   * Lookup134: frame_system::limits::WeightsPerClass
    **/
   FrameSystemLimitsWeightsPerClass: {
     baseExtrinsic: 'SpWeightsWeightV2Weight',
@@ -1388,13 +1434,13 @@
     reserved: 'Option<SpWeightsWeightV2Weight>'
   },
   /**
-   * Lookup134: frame_system::limits::BlockLength
+   * Lookup136: frame_system::limits::BlockLength
    **/
   FrameSystemLimitsBlockLength: {
     max: 'FrameSupportDispatchPerDispatchClassU32'
   },
   /**
-   * Lookup135: frame_support::dispatch::PerDispatchClass<T>
+   * Lookup137: frame_support::dispatch::PerDispatchClass<T>
    **/
   FrameSupportDispatchPerDispatchClassU32: {
     normal: 'u32',
@@ -1402,14 +1448,14 @@
     mandatory: 'u32'
   },
   /**
-   * Lookup136: sp_weights::RuntimeDbWeight
+   * Lookup138: sp_weights::RuntimeDbWeight
    **/
   SpWeightsRuntimeDbWeight: {
     read: 'u64',
     write: 'u64'
   },
   /**
-   * Lookup137: sp_version::RuntimeVersion
+   * Lookup139: sp_version::RuntimeVersion
    **/
   SpVersionRuntimeVersion: {
     specName: 'Text',
@@ -1422,13 +1468,13 @@
     stateVersion: 'u8'
   },
   /**
-   * Lookup142: frame_system::pallet::Error<T>
+   * Lookup144: frame_system::pallet::Error<T>
    **/
   FrameSystemError: {
     _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered']
   },
   /**
-   * Lookup143: polkadot_primitives::v2::PersistedValidationData<primitive_types::H256, N>
+   * Lookup145: polkadot_primitives::v2::PersistedValidationData<primitive_types::H256, N>
    **/
   PolkadotPrimitivesV2PersistedValidationData: {
     parentHead: 'Bytes',
@@ -1437,19 +1483,19 @@
     maxPovSize: 'u32'
   },
   /**
-   * Lookup146: polkadot_primitives::v2::UpgradeRestriction
+   * Lookup148: polkadot_primitives::v2::UpgradeRestriction
    **/
   PolkadotPrimitivesV2UpgradeRestriction: {
     _enum: ['Present']
   },
   /**
-   * Lookup147: sp_trie::storage_proof::StorageProof
+   * Lookup149: sp_trie::storage_proof::StorageProof
    **/
   SpTrieStorageProof: {
     trieNodes: 'BTreeSet<Bytes>'
   },
   /**
-   * Lookup149: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot
+   * Lookup151: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot
    **/
   CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: {
     dmqMqcHead: 'H256',
@@ -1458,7 +1504,7 @@
     egressChannels: 'Vec<(u32,PolkadotPrimitivesV2AbridgedHrmpChannel)>'
   },
   /**
-   * Lookup152: polkadot_primitives::v2::AbridgedHrmpChannel
+   * Lookup154: polkadot_primitives::v2::AbridgedHrmpChannel
    **/
   PolkadotPrimitivesV2AbridgedHrmpChannel: {
     maxCapacity: 'u32',
@@ -1469,7 +1515,7 @@
     mqcHead: 'Option<H256>'
   },
   /**
-   * Lookup153: polkadot_primitives::v2::AbridgedHostConfiguration
+   * Lookup155: polkadot_primitives::v2::AbridgedHostConfiguration
    **/
   PolkadotPrimitivesV2AbridgedHostConfiguration: {
     maxCodeSize: 'u32',
@@ -1483,14 +1529,14 @@
     validationUpgradeDelay: 'u32'
   },
   /**
-   * Lookup159: polkadot_core_primitives::OutboundHrmpMessage<polkadot_parachain::primitives::Id>
+   * Lookup161: polkadot_core_primitives::OutboundHrmpMessage<polkadot_parachain::primitives::Id>
    **/
   PolkadotCorePrimitivesOutboundHrmpMessage: {
     recipient: 'u32',
     data: 'Bytes'
   },
   /**
-   * Lookup160: cumulus_pallet_parachain_system::pallet::Call<T>
+   * Lookup162: cumulus_pallet_parachain_system::pallet::Call<T>
    **/
   CumulusPalletParachainSystemCall: {
     _enum: {
@@ -1509,7 +1555,7 @@
     }
   },
   /**
-   * Lookup161: cumulus_primitives_parachain_inherent::ParachainInherentData
+   * Lookup163: cumulus_primitives_parachain_inherent::ParachainInherentData
    **/
   CumulusPrimitivesParachainInherentParachainInherentData: {
     validationData: 'PolkadotPrimitivesV2PersistedValidationData',
@@ -1518,54 +1564,170 @@
     horizontalMessages: 'BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>'
   },
   /**
-   * Lookup163: polkadot_core_primitives::InboundDownwardMessage<BlockNumber>
+   * Lookup165: polkadot_core_primitives::InboundDownwardMessage<BlockNumber>
    **/
   PolkadotCorePrimitivesInboundDownwardMessage: {
     sentAt: 'u32',
     msg: 'Bytes'
   },
   /**
-   * Lookup166: polkadot_core_primitives::InboundHrmpMessage<BlockNumber>
+   * Lookup168: polkadot_core_primitives::InboundHrmpMessage<BlockNumber>
    **/
   PolkadotCorePrimitivesInboundHrmpMessage: {
     sentAt: 'u32',
     data: 'Bytes'
   },
   /**
-   * Lookup169: cumulus_pallet_parachain_system::pallet::Error<T>
+   * Lookup171: cumulus_pallet_parachain_system::pallet::Error<T>
    **/
   CumulusPalletParachainSystemError: {
     _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized']
   },
   /**
-   * Lookup171: pallet_balances::BalanceLock<Balance>
+   * Lookup173: pallet_authorship::UncleEntryItem<BlockNumber, primitive_types::H256, sp_core::crypto::AccountId32>
+   **/
+  PalletAuthorshipUncleEntryItem: {
+    _enum: {
+      InclusionHeight: 'u32',
+      Uncle: '(H256,Option<AccountId32>)'
+    }
+  },
+  /**
+   * Lookup175: pallet_authorship::pallet::Call<T>
+   **/
+  PalletAuthorshipCall: {
+    _enum: {
+      set_uncles: {
+        newUncles: 'Vec<SpRuntimeHeader>'
+      }
+    }
+  },
+  /**
+   * Lookup177: sp_runtime::generic::header::Header<Number, sp_runtime::traits::BlakeTwo256>
+   **/
+  SpRuntimeHeader: {
+    parentHash: 'H256',
+    number: 'Compact<u32>',
+    stateRoot: 'H256',
+    extrinsicsRoot: 'H256',
+    digest: 'SpRuntimeDigest'
+  },
+  /**
+   * Lookup178: sp_runtime::traits::BlakeTwo256
+   **/
+  SpRuntimeBlakeTwo256: 'Null',
+  /**
+   * Lookup179: pallet_authorship::pallet::Error<T>
+   **/
+  PalletAuthorshipError: {
+    _enum: ['InvalidUncleParent', 'UnclesAlreadySet', 'TooManyUncles', 'GenesisUncle', 'TooHighUncle', 'UncleAlreadyIncluded', 'OldUncle']
+  },
+  /**
+   * Lookup182: pallet_collator_selection::pallet::Call<T>
+   **/
+  PalletCollatorSelectionCall: {
+    _enum: {
+      add_invulnerable: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'AccountId32',
+      },
+      remove_invulnerable: {
+        who: 'AccountId32',
+      },
+      set_desired_collators: {
+        max: 'u32',
+      },
+      set_license_bond: {
+        bond: 'u128',
+      },
+      set_kick_threshold: {
+        kickThreshold: 'u32',
+      },
+      get_license: 'Null',
+      onboard: 'Null',
+      offboard: 'Null',
+      release_license: 'Null',
+      force_revoke_license: {
+        who: 'AccountId32'
+      }
+    }
+  },
+  /**
+   * Lookup183: pallet_collator_selection::pallet::Error<T>
    **/
+  PalletCollatorSelectionError: {
+    _enum: ['TooManyCandidates', 'Unknown', 'Permission', 'AlreadyHoldingLicense', 'NoLicense', 'AlreadyCandidate', 'NotCandidate', 'TooManyInvulnerables', 'TooFewInvulnerables', 'AlreadyInvulnerable', 'NotInvulnerable', 'NoAssociatedValidatorId', 'ValidatorNotRegistered']
+  },
+  /**
+   * Lookup186: opal_runtime::runtime_common::SessionKeys
+   **/
+  OpalRuntimeRuntimeCommonSessionKeys: {
+    aura: 'SpConsensusAuraSr25519AppSr25519Public'
+  },
+  /**
+   * Lookup187: sp_consensus_aura::sr25519::app_sr25519::Public
+   **/
+  SpConsensusAuraSr25519AppSr25519Public: 'SpCoreSr25519Public',
+  /**
+   * Lookup188: sp_core::sr25519::Public
+   **/
+  SpCoreSr25519Public: '[u8;32]',
+  /**
+   * Lookup191: sp_core::crypto::KeyTypeId
+   **/
+  SpCoreCryptoKeyTypeId: '[u8;4]',
+  /**
+   * Lookup192: pallet_session::pallet::Call<T>
+   **/
+  PalletSessionCall: {
+    _enum: {
+      set_keys: {
+        _alias: {
+          keys_: 'keys',
+        },
+        keys_: 'OpalRuntimeRuntimeCommonSessionKeys',
+        proof: 'Bytes',
+      },
+      purge_keys: 'Null'
+    }
+  },
+  /**
+   * Lookup193: pallet_session::pallet::Error<T>
+   **/
+  PalletSessionError: {
+    _enum: ['InvalidProof', 'NoAssociatedValidatorId', 'DuplicatedKey', 'NoKeys', 'NoAccount']
+  },
+  /**
+   * Lookup195: pallet_balances::BalanceLock<Balance>
+   **/
   PalletBalancesBalanceLock: {
     id: '[u8;8]',
     amount: 'u128',
     reasons: 'PalletBalancesReasons'
   },
   /**
-   * Lookup172: pallet_balances::Reasons
+   * Lookup196: pallet_balances::Reasons
    **/
   PalletBalancesReasons: {
     _enum: ['Fee', 'Misc', 'All']
   },
   /**
-   * Lookup175: pallet_balances::ReserveData<ReserveIdentifier, Balance>
+   * Lookup199: pallet_balances::ReserveData<ReserveIdentifier, Balance>
    **/
   PalletBalancesReserveData: {
     id: '[u8;16]',
     amount: 'u128'
   },
   /**
-   * Lookup177: pallet_balances::Releases
+   * Lookup201: pallet_balances::Releases
    **/
   PalletBalancesReleases: {
     _enum: ['V1_0_0', 'V2_0_0']
   },
   /**
-   * Lookup178: pallet_balances::pallet::Call<T, I>
+   * Lookup202: pallet_balances::pallet::Call<T, I>
    **/
   PalletBalancesCall: {
     _enum: {
@@ -1598,13 +1760,13 @@
     }
   },
   /**
-   * Lookup181: pallet_balances::pallet::Error<T, I>
+   * Lookup205: pallet_balances::pallet::Error<T, I>
    **/
   PalletBalancesError: {
     _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'KeepAlive', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves']
   },
   /**
-   * Lookup183: pallet_timestamp::pallet::Call<T>
+   * Lookup207: pallet_timestamp::pallet::Call<T>
    **/
   PalletTimestampCall: {
     _enum: {
@@ -1614,13 +1776,13 @@
     }
   },
   /**
-   * Lookup185: pallet_transaction_payment::Releases
+   * Lookup209: pallet_transaction_payment::Releases
    **/
   PalletTransactionPaymentReleases: {
     _enum: ['V1Ancient', 'V2']
   },
   /**
-   * Lookup186: pallet_treasury::Proposal<sp_core::crypto::AccountId32, Balance>
+   * Lookup210: pallet_treasury::Proposal<sp_core::crypto::AccountId32, Balance>
    **/
   PalletTreasuryProposal: {
     proposer: 'AccountId32',
@@ -1629,7 +1791,7 @@
     bond: 'u128'
   },
   /**
-   * Lookup189: pallet_treasury::pallet::Call<T, I>
+   * Lookup212: pallet_treasury::pallet::Call<T, I>
    **/
   PalletTreasuryCall: {
     _enum: {
@@ -1653,17 +1815,17 @@
     }
   },
   /**
-   * Lookup192: frame_support::PalletId
+   * Lookup215: frame_support::PalletId
    **/
   FrameSupportPalletId: '[u8;8]',
   /**
-   * Lookup193: pallet_treasury::pallet::Error<T, I>
+   * Lookup216: pallet_treasury::pallet::Error<T, I>
    **/
   PalletTreasuryError: {
     _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved']
   },
   /**
-   * Lookup194: pallet_sudo::pallet::Call<T>
+   * Lookup217: pallet_sudo::pallet::Call<T>
    **/
   PalletSudoCall: {
     _enum: {
@@ -1687,7 +1849,7 @@
     }
   },
   /**
-   * Lookup196: orml_vesting::module::Call<T>
+   * Lookup219: orml_vesting::module::Call<T>
    **/
   OrmlVestingModuleCall: {
     _enum: {
@@ -1706,7 +1868,7 @@
     }
   },
   /**
-   * Lookup198: orml_xtokens::module::Call<T>
+   * Lookup221: orml_xtokens::module::Call<T>
    **/
   OrmlXtokensModuleCall: {
     _enum: {
@@ -1749,7 +1911,7 @@
     }
   },
   /**
-   * Lookup199: xcm::VersionedMultiAsset
+   * Lookup222: xcm::VersionedMultiAsset
    **/
   XcmVersionedMultiAsset: {
     _enum: {
@@ -1758,7 +1920,7 @@
     }
   },
   /**
-   * Lookup202: orml_tokens::module::Call<T>
+   * Lookup225: orml_tokens::module::Call<T>
    **/
   OrmlTokensModuleCall: {
     _enum: {
@@ -1792,7 +1954,7 @@
     }
   },
   /**
-   * Lookup203: cumulus_pallet_xcmp_queue::pallet::Call<T>
+   * Lookup226: cumulus_pallet_xcmp_queue::pallet::Call<T>
    **/
   CumulusPalletXcmpQueueCall: {
     _enum: {
@@ -1841,7 +2003,7 @@
     }
   },
   /**
-   * Lookup204: pallet_xcm::pallet::Call<T>
+   * Lookup227: pallet_xcm::pallet::Call<T>
    **/
   PalletXcmCall: {
     _enum: {
@@ -1895,7 +2057,7 @@
     }
   },
   /**
-   * Lookup205: xcm::VersionedXcm<RuntimeCall>
+   * Lookup228: xcm::VersionedXcm<RuntimeCall>
    **/
   XcmVersionedXcm: {
     _enum: {
@@ -1905,7 +2067,7 @@
     }
   },
   /**
-   * Lookup206: xcm::v0::Xcm<RuntimeCall>
+   * Lookup229: xcm::v0::Xcm<RuntimeCall>
    **/
   XcmV0Xcm: {
     _enum: {
@@ -1959,7 +2121,7 @@
     }
   },
   /**
-   * Lookup208: xcm::v0::order::Order<RuntimeCall>
+   * Lookup231: xcm::v0::order::Order<RuntimeCall>
    **/
   XcmV0Order: {
     _enum: {
@@ -2002,7 +2164,7 @@
     }
   },
   /**
-   * Lookup210: xcm::v0::Response
+   * Lookup233: xcm::v0::Response
    **/
   XcmV0Response: {
     _enum: {
@@ -2010,7 +2172,7 @@
     }
   },
   /**
-   * Lookup211: xcm::v1::Xcm<RuntimeCall>
+   * Lookup234: xcm::v1::Xcm<RuntimeCall>
    **/
   XcmV1Xcm: {
     _enum: {
@@ -2069,7 +2231,7 @@
     }
   },
   /**
-   * Lookup213: xcm::v1::order::Order<RuntimeCall>
+   * Lookup236: xcm::v1::order::Order<RuntimeCall>
    **/
   XcmV1Order: {
     _enum: {
@@ -2114,7 +2276,7 @@
     }
   },
   /**
-   * Lookup215: xcm::v1::Response
+   * Lookup238: xcm::v1::Response
    **/
   XcmV1Response: {
     _enum: {
@@ -2123,11 +2285,11 @@
     }
   },
   /**
-   * Lookup229: cumulus_pallet_xcm::pallet::Call<T>
+   * Lookup252: cumulus_pallet_xcm::pallet::Call<T>
    **/
   CumulusPalletXcmCall: 'Null',
   /**
-   * Lookup230: cumulus_pallet_dmp_queue::pallet::Call<T>
+   * Lookup253: cumulus_pallet_dmp_queue::pallet::Call<T>
    **/
   CumulusPalletDmpQueueCall: {
     _enum: {
@@ -2138,7 +2300,7 @@
     }
   },
   /**
-   * Lookup231: pallet_inflation::pallet::Call<T>
+   * Lookup254: pallet_inflation::pallet::Call<T>
    **/
   PalletInflationCall: {
     _enum: {
@@ -2148,7 +2310,7 @@
     }
   },
   /**
-   * Lookup232: pallet_unique::Call<T>
+   * Lookup255: pallet_unique::Call<T>
    **/
   PalletUniqueCall: {
     _enum: {
@@ -2282,14 +2444,17 @@
         operator: 'PalletEvmAccountBasicCrossAccountIdRepr',
         approve: 'bool',
       },
-      repair_item: {
+      force_repair_collection: {
+        collectionId: 'u32',
+      },
+      force_repair_item: {
         collectionId: 'u32',
         itemId: 'u32'
       }
     }
   },
   /**
-   * Lookup237: up_data_structs::CollectionMode
+   * Lookup260: up_data_structs::CollectionMode
    **/
   UpDataStructsCollectionMode: {
     _enum: {
@@ -2299,7 +2464,7 @@
     }
   },
   /**
-   * Lookup238: up_data_structs::CreateCollectionData<sp_core::crypto::AccountId32>
+   * Lookup261: up_data_structs::CreateCollectionData<sp_core::crypto::AccountId32>
    **/
   UpDataStructsCreateCollectionData: {
     mode: 'UpDataStructsCollectionMode',
@@ -2314,13 +2479,13 @@
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup240: up_data_structs::AccessMode
+   * Lookup263: up_data_structs::AccessMode
    **/
   UpDataStructsAccessMode: {
     _enum: ['Normal', 'AllowList']
   },
   /**
-   * Lookup242: up_data_structs::CollectionLimits
+   * Lookup265: up_data_structs::CollectionLimits
    **/
   UpDataStructsCollectionLimits: {
     accountTokenOwnershipLimit: 'Option<u32>',
@@ -2334,7 +2499,7 @@
     transfersEnabled: 'Option<bool>'
   },
   /**
-   * Lookup244: up_data_structs::SponsoringRateLimit
+   * Lookup267: up_data_structs::SponsoringRateLimit
    **/
   UpDataStructsSponsoringRateLimit: {
     _enum: {
@@ -2343,7 +2508,7 @@
     }
   },
   /**
-   * Lookup247: up_data_structs::CollectionPermissions
+   * Lookup270: up_data_structs::CollectionPermissions
    **/
   UpDataStructsCollectionPermissions: {
     access: 'Option<UpDataStructsAccessMode>',
@@ -2351,7 +2516,7 @@
     nesting: 'Option<UpDataStructsNestingPermissions>'
   },
   /**
-   * Lookup249: up_data_structs::NestingPermissions
+   * Lookup272: up_data_structs::NestingPermissions
    **/
   UpDataStructsNestingPermissions: {
     tokenOwner: 'bool',
@@ -2359,18 +2524,18 @@
     restricted: 'Option<UpDataStructsOwnerRestrictedSet>'
   },
   /**
-   * Lookup251: up_data_structs::OwnerRestrictedSet
+   * Lookup274: up_data_structs::OwnerRestrictedSet
    **/
   UpDataStructsOwnerRestrictedSet: 'BTreeSet<u32>',
   /**
-   * Lookup256: up_data_structs::PropertyKeyPermission
+   * Lookup279: up_data_structs::PropertyKeyPermission
    **/
   UpDataStructsPropertyKeyPermission: {
     key: 'Bytes',
     permission: 'UpDataStructsPropertyPermission'
   },
   /**
-   * Lookup257: up_data_structs::PropertyPermission
+   * Lookup280: up_data_structs::PropertyPermission
    **/
   UpDataStructsPropertyPermission: {
     mutable: 'bool',
@@ -2378,14 +2543,14 @@
     tokenOwner: 'bool'
   },
   /**
-   * Lookup260: up_data_structs::Property
+   * Lookup283: up_data_structs::Property
    **/
   UpDataStructsProperty: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup263: up_data_structs::CreateItemData
+   * Lookup286: up_data_structs::CreateItemData
    **/
   UpDataStructsCreateItemData: {
     _enum: {
@@ -2395,26 +2560,26 @@
     }
   },
   /**
-   * Lookup264: up_data_structs::CreateNftData
+   * Lookup287: up_data_structs::CreateNftData
    **/
   UpDataStructsCreateNftData: {
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup265: up_data_structs::CreateFungibleData
+   * Lookup288: up_data_structs::CreateFungibleData
    **/
   UpDataStructsCreateFungibleData: {
     value: 'u128'
   },
   /**
-   * Lookup266: up_data_structs::CreateReFungibleData
+   * Lookup289: up_data_structs::CreateReFungibleData
    **/
   UpDataStructsCreateReFungibleData: {
     pieces: 'u128',
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup269: up_data_structs::CreateItemExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup292: up_data_structs::CreateItemExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateItemExData: {
     _enum: {
@@ -2425,14 +2590,14 @@
     }
   },
   /**
-   * Lookup271: up_data_structs::CreateNftExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup294: up_data_structs::CreateNftExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateNftExData: {
     properties: 'Vec<UpDataStructsProperty>',
     owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
   },
   /**
-   * Lookup278: up_data_structs::CreateRefungibleExSingleOwner<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup301: up_data_structs::CreateRefungibleExSingleOwner<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateRefungibleExSingleOwner: {
     user: 'PalletEvmAccountBasicCrossAccountIdRepr',
@@ -2440,14 +2605,14 @@
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup280: up_data_structs::CreateRefungibleExMultipleOwners<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup303: up_data_structs::CreateRefungibleExMultipleOwners<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateRefungibleExMultipleOwners: {
     users: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>',
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup281: pallet_configuration::pallet::Call<T>
+   * Lookup304: pallet_configuration::pallet::Call<T>
    **/
   PalletConfigurationCall: {
     _enum: {
@@ -2466,7 +2631,7 @@
     }
   },
   /**
-   * Lookup286: pallet_configuration::AppPromotionConfiguration<BlockNumber>
+   * Lookup309: pallet_configuration::AppPromotionConfiguration<BlockNumber>
    **/
   PalletConfigurationAppPromotionConfiguration: {
     recalculationInterval: 'Option<u32>',
@@ -2475,15 +2640,15 @@
     maxStakersPerCalculation: 'Option<u8>'
   },
   /**
-   * Lookup289: pallet_template_transaction_payment::Call<T>
+   * Lookup312: pallet_template_transaction_payment::Call<T>
    **/
   PalletTemplateTransactionPaymentCall: 'Null',
   /**
-   * Lookup290: pallet_structure::pallet::Call<T>
+   * Lookup313: pallet_structure::pallet::Call<T>
    **/
   PalletStructureCall: 'Null',
   /**
-   * Lookup291: pallet_rmrk_core::pallet::Call<T>
+   * Lookup314: pallet_rmrk_core::pallet::Call<T>
    **/
   PalletRmrkCoreCall: {
     _enum: {
@@ -2574,7 +2739,7 @@
     }
   },
   /**
-   * Lookup297: rmrk_traits::resource::ResourceTypes<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup320: rmrk_traits::resource::ResourceTypes<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceResourceTypes: {
     _enum: {
@@ -2584,7 +2749,7 @@
     }
   },
   /**
-   * Lookup299: rmrk_traits::resource::BasicResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup322: rmrk_traits::resource::BasicResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceBasicResource: {
     src: 'Option<Bytes>',
@@ -2593,7 +2758,7 @@
     thumb: 'Option<Bytes>'
   },
   /**
-   * Lookup301: rmrk_traits::resource::ComposableResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup324: rmrk_traits::resource::ComposableResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceComposableResource: {
     parts: 'Vec<u32>',
@@ -2604,7 +2769,7 @@
     thumb: 'Option<Bytes>'
   },
   /**
-   * Lookup302: rmrk_traits::resource::SlotResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup325: rmrk_traits::resource::SlotResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceSlotResource: {
     base: 'u32',
@@ -2615,7 +2780,7 @@
     thumb: 'Option<Bytes>'
   },
   /**
-   * Lookup305: pallet_rmrk_equip::pallet::Call<T>
+   * Lookup328: pallet_rmrk_equip::pallet::Call<T>
    **/
   PalletRmrkEquipCall: {
     _enum: {
@@ -2636,7 +2801,7 @@
     }
   },
   /**
-   * Lookup308: rmrk_traits::part::PartType<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup331: rmrk_traits::part::PartType<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartPartType: {
     _enum: {
@@ -2645,7 +2810,7 @@
     }
   },
   /**
-   * Lookup310: rmrk_traits::part::FixedPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup333: rmrk_traits::part::FixedPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartFixedPart: {
     id: 'u32',
@@ -2653,7 +2818,7 @@
     src: 'Bytes'
   },
   /**
-   * Lookup311: rmrk_traits::part::SlotPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup334: rmrk_traits::part::SlotPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartSlotPart: {
     id: 'u32',
@@ -2662,7 +2827,7 @@
     z: 'u32'
   },
   /**
-   * Lookup312: rmrk_traits::part::EquippableList<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup335: rmrk_traits::part::EquippableList<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartEquippableList: {
     _enum: {
@@ -2672,7 +2837,7 @@
     }
   },
   /**
-   * Lookup314: rmrk_traits::theme::Theme<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>, S>>
+   * Lookup337: rmrk_traits::theme::Theme<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>, S>>
    **/
   RmrkTraitsTheme: {
     name: 'Bytes',
@@ -2680,14 +2845,14 @@
     inherit: 'bool'
   },
   /**
-   * Lookup316: rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup339: rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsThemeThemeProperty: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup318: pallet_app_promotion::pallet::Call<T>
+   * Lookup341: pallet_app_promotion::pallet::Call<T>
    **/
   PalletAppPromotionCall: {
     _enum: {
@@ -2716,7 +2881,7 @@
     }
   },
   /**
-   * Lookup319: pallet_foreign_assets::module::Call<T>
+   * Lookup342: pallet_foreign_assets::module::Call<T>
    **/
   PalletForeignAssetsModuleCall: {
     _enum: {
@@ -2733,7 +2898,7 @@
     }
   },
   /**
-   * Lookup320: pallet_evm::pallet::Call<T>
+   * Lookup343: pallet_evm::pallet::Call<T>
    **/
   PalletEvmCall: {
     _enum: {
@@ -2776,7 +2941,7 @@
     }
   },
   /**
-   * Lookup326: pallet_ethereum::pallet::Call<T>
+   * Lookup349: pallet_ethereum::pallet::Call<T>
    **/
   PalletEthereumCall: {
     _enum: {
@@ -2786,7 +2951,7 @@
     }
   },
   /**
-   * Lookup327: ethereum::transaction::TransactionV2
+   * Lookup350: ethereum::transaction::TransactionV2
    **/
   EthereumTransactionTransactionV2: {
     _enum: {
@@ -2796,7 +2961,7 @@
     }
   },
   /**
-   * Lookup328: ethereum::transaction::LegacyTransaction
+   * Lookup351: ethereum::transaction::LegacyTransaction
    **/
   EthereumTransactionLegacyTransaction: {
     nonce: 'U256',
@@ -2808,7 +2973,7 @@
     signature: 'EthereumTransactionTransactionSignature'
   },
   /**
-   * Lookup329: ethereum::transaction::TransactionAction
+   * Lookup352: ethereum::transaction::TransactionAction
    **/
   EthereumTransactionTransactionAction: {
     _enum: {
@@ -2817,7 +2982,7 @@
     }
   },
   /**
-   * Lookup330: ethereum::transaction::TransactionSignature
+   * Lookup353: ethereum::transaction::TransactionSignature
    **/
   EthereumTransactionTransactionSignature: {
     v: 'u64',
@@ -2825,7 +2990,7 @@
     s: 'H256'
   },
   /**
-   * Lookup332: ethereum::transaction::EIP2930Transaction
+   * Lookup355: ethereum::transaction::EIP2930Transaction
    **/
   EthereumTransactionEip2930Transaction: {
     chainId: 'u64',
@@ -2841,14 +3006,14 @@
     s: 'H256'
   },
   /**
-   * Lookup334: ethereum::transaction::AccessListItem
+   * Lookup357: ethereum::transaction::AccessListItem
    **/
   EthereumTransactionAccessListItem: {
     address: 'H160',
     storageKeys: 'Vec<H256>'
   },
   /**
-   * Lookup335: ethereum::transaction::EIP1559Transaction
+   * Lookup358: ethereum::transaction::EIP1559Transaction
    **/
   EthereumTransactionEip1559Transaction: {
     chainId: 'u64',
@@ -2865,7 +3030,7 @@
     s: 'H256'
   },
   /**
-   * Lookup336: pallet_evm_migration::pallet::Call<T>
+   * Lookup359: pallet_evm_migration::pallet::Call<T>
    **/
   PalletEvmMigrationCall: {
     _enum: {
@@ -2889,13 +3054,13 @@
     }
   },
   /**
-   * Lookup340: pallet_maintenance::pallet::Call<T>
+   * Lookup363: pallet_maintenance::pallet::Call<T>
    **/
   PalletMaintenanceCall: {
     _enum: ['enable', 'disable']
   },
   /**
-   * Lookup341: pallet_test_utils::pallet::Call<T>
+   * Lookup364: pallet_test_utils::pallet::Call<T>
    **/
   PalletTestUtilsCall: {
     _enum: {
@@ -2914,32 +3079,32 @@
     }
   },
   /**
-   * Lookup343: pallet_sudo::pallet::Error<T>
+   * Lookup366: pallet_sudo::pallet::Error<T>
    **/
   PalletSudoError: {
     _enum: ['RequireSudo']
   },
   /**
-   * Lookup345: orml_vesting::module::Error<T>
+   * Lookup368: orml_vesting::module::Error<T>
    **/
   OrmlVestingModuleError: {
     _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded']
   },
   /**
-   * Lookup346: orml_xtokens::module::Error<T>
+   * Lookup369: orml_xtokens::module::Error<T>
    **/
   OrmlXtokensModuleError: {
     _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined']
   },
   /**
-   * Lookup349: orml_tokens::BalanceLock<Balance>
+   * Lookup372: orml_tokens::BalanceLock<Balance>
    **/
   OrmlTokensBalanceLock: {
     id: '[u8;8]',
     amount: 'u128'
   },
   /**
-   * Lookup351: orml_tokens::AccountData<Balance>
+   * Lookup374: orml_tokens::AccountData<Balance>
    **/
   OrmlTokensAccountData: {
     free: 'u128',
@@ -2947,20 +3112,20 @@
     frozen: 'u128'
   },
   /**
-   * Lookup353: orml_tokens::ReserveData<ReserveIdentifier, Balance>
+   * Lookup376: orml_tokens::ReserveData<ReserveIdentifier, Balance>
    **/
   OrmlTokensReserveData: {
     id: 'Null',
     amount: 'u128'
   },
   /**
-   * Lookup355: orml_tokens::module::Error<T>
+   * Lookup378: orml_tokens::module::Error<T>
    **/
   OrmlTokensModuleError: {
     _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves']
   },
   /**
-   * Lookup357: cumulus_pallet_xcmp_queue::InboundChannelDetails
+   * Lookup380: cumulus_pallet_xcmp_queue::InboundChannelDetails
    **/
   CumulusPalletXcmpQueueInboundChannelDetails: {
     sender: 'u32',
@@ -2968,19 +3133,19 @@
     messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesXcmpMessageFormat)>'
   },
   /**
-   * Lookup358: cumulus_pallet_xcmp_queue::InboundState
+   * Lookup381: cumulus_pallet_xcmp_queue::InboundState
    **/
   CumulusPalletXcmpQueueInboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup361: polkadot_parachain::primitives::XcmpMessageFormat
+   * Lookup384: polkadot_parachain::primitives::XcmpMessageFormat
    **/
   PolkadotParachainPrimitivesXcmpMessageFormat: {
     _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals']
   },
   /**
-   * Lookup364: cumulus_pallet_xcmp_queue::OutboundChannelDetails
+   * Lookup387: cumulus_pallet_xcmp_queue::OutboundChannelDetails
    **/
   CumulusPalletXcmpQueueOutboundChannelDetails: {
     recipient: 'u32',
@@ -2990,13 +3155,13 @@
     lastIndex: 'u16'
   },
   /**
-   * Lookup365: cumulus_pallet_xcmp_queue::OutboundState
+   * Lookup388: cumulus_pallet_xcmp_queue::OutboundState
    **/
   CumulusPalletXcmpQueueOutboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup367: cumulus_pallet_xcmp_queue::QueueConfigData
+   * Lookup390: cumulus_pallet_xcmp_queue::QueueConfigData
    **/
   CumulusPalletXcmpQueueQueueConfigData: {
     suspendThreshold: 'u32',
@@ -3007,29 +3172,29 @@
     xcmpMaxIndividualWeight: 'SpWeightsWeightV2Weight'
   },
   /**
-   * Lookup369: cumulus_pallet_xcmp_queue::pallet::Error<T>
+   * Lookup392: cumulus_pallet_xcmp_queue::pallet::Error<T>
    **/
   CumulusPalletXcmpQueueError: {
     _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit']
   },
   /**
-   * Lookup370: pallet_xcm::pallet::Error<T>
+   * Lookup393: pallet_xcm::pallet::Error<T>
    **/
   PalletXcmError: {
     _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed']
   },
   /**
-   * Lookup371: cumulus_pallet_xcm::pallet::Error<T>
+   * Lookup394: cumulus_pallet_xcm::pallet::Error<T>
    **/
   CumulusPalletXcmError: 'Null',
   /**
-   * Lookup372: cumulus_pallet_dmp_queue::ConfigData
+   * Lookup395: cumulus_pallet_dmp_queue::ConfigData
    **/
   CumulusPalletDmpQueueConfigData: {
     maxIndividual: 'SpWeightsWeightV2Weight'
   },
   /**
-   * Lookup373: cumulus_pallet_dmp_queue::PageIndexData
+   * Lookup396: cumulus_pallet_dmp_queue::PageIndexData
    **/
   CumulusPalletDmpQueuePageIndexData: {
     beginUsed: 'u32',
@@ -3037,25 +3202,25 @@
     overweightCount: 'u64'
   },
   /**
-   * Lookup376: cumulus_pallet_dmp_queue::pallet::Error<T>
+   * Lookup399: cumulus_pallet_dmp_queue::pallet::Error<T>
    **/
   CumulusPalletDmpQueueError: {
     _enum: ['Unknown', 'OverLimit']
   },
   /**
-   * Lookup380: pallet_unique::Error<T>
+   * Lookup403: pallet_unique::Error<T>
    **/
   PalletUniqueError: {
     _enum: ['CollectionDecimalPointLimitExceeded', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection']
   },
   /**
-   * Lookup381: pallet_configuration::pallet::Error<T>
+   * Lookup404: pallet_configuration::pallet::Error<T>
    **/
   PalletConfigurationError: {
     _enum: ['InconsistentConfiguration']
   },
   /**
-   * Lookup382: up_data_structs::Collection<sp_core::crypto::AccountId32>
+   * Lookup405: up_data_structs::Collection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsCollection: {
     owner: 'AccountId32',
@@ -3069,7 +3234,7 @@
     flags: '[u8;1]'
   },
   /**
-   * Lookup383: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
+   * Lookup406: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
    **/
   UpDataStructsSponsorshipStateAccountId32: {
     _enum: {
@@ -3079,7 +3244,7 @@
     }
   },
   /**
-   * Lookup385: up_data_structs::Properties
+   * Lookup408: up_data_structs::Properties
    **/
   UpDataStructsProperties: {
     map: 'UpDataStructsPropertiesMapBoundedVec',
@@ -3087,15 +3252,15 @@
     spaceLimit: 'u32'
   },
   /**
-   * Lookup386: up_data_structs::PropertiesMap<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup409: up_data_structs::PropertiesMap<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   UpDataStructsPropertiesMapBoundedVec: 'BTreeMap<Bytes, Bytes>',
   /**
-   * Lookup391: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
+   * Lookup414: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
    **/
   UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap<Bytes, UpDataStructsPropertyPermission>',
   /**
-   * Lookup398: up_data_structs::CollectionStats
+   * Lookup421: up_data_structs::CollectionStats
    **/
   UpDataStructsCollectionStats: {
     created: 'u32',
@@ -3103,18 +3268,18 @@
     alive: 'u32'
   },
   /**
-   * Lookup399: up_data_structs::TokenChild
+   * Lookup422: up_data_structs::TokenChild
    **/
   UpDataStructsTokenChild: {
     token: 'u32',
     collection: 'u32'
   },
   /**
-   * Lookup400: PhantomType::up_data_structs<T>
+   * Lookup423: PhantomType::up_data_structs<T>
    **/
   PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,RmrkTraitsCollectionCollectionInfo,RmrkTraitsNftNftInfo,RmrkTraitsResourceResourceInfo,RmrkTraitsPropertyPropertyInfo,RmrkTraitsBaseBaseInfo,RmrkTraitsPartPartType,RmrkTraitsTheme,RmrkTraitsNftNftChild);0]',
   /**
-   * Lookup402: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup425: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsTokenData: {
     properties: 'Vec<UpDataStructsProperty>',
@@ -3122,7 +3287,7 @@
     pieces: 'u128'
   },
   /**
-   * Lookup404: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
+   * Lookup427: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsRpcCollection: {
     owner: 'AccountId32',
@@ -3139,14 +3304,14 @@
     flags: 'UpDataStructsRpcCollectionFlags'
   },
   /**
-   * Lookup405: up_data_structs::RpcCollectionFlags
+   * Lookup428: up_data_structs::RpcCollectionFlags
    **/
   UpDataStructsRpcCollectionFlags: {
     foreign: 'bool',
     erc721metadata: 'bool'
   },
   /**
-   * Lookup406: rmrk_traits::collection::CollectionInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::crypto::AccountId32>
+   * Lookup429: rmrk_traits::collection::CollectionInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::crypto::AccountId32>
    **/
   RmrkTraitsCollectionCollectionInfo: {
     issuer: 'AccountId32',
@@ -3156,7 +3321,7 @@
     nftsCount: 'u32'
   },
   /**
-   * Lookup407: rmrk_traits::nft::NftInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup430: rmrk_traits::nft::NftInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsNftNftInfo: {
     owner: 'RmrkTraitsNftAccountIdOrCollectionNftTuple',
@@ -3166,14 +3331,14 @@
     pending: 'bool'
   },
   /**
-   * Lookup409: rmrk_traits::nft::RoyaltyInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill>
+   * Lookup432: rmrk_traits::nft::RoyaltyInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill>
    **/
   RmrkTraitsNftRoyaltyInfo: {
     recipient: 'AccountId32',
     amount: 'Permill'
   },
   /**
-   * Lookup410: rmrk_traits::resource::ResourceInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup433: rmrk_traits::resource::ResourceInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceResourceInfo: {
     id: 'u32',
@@ -3182,14 +3347,14 @@
     pendingRemoval: 'bool'
   },
   /**
-   * Lookup411: rmrk_traits::property::PropertyInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup434: rmrk_traits::property::PropertyInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPropertyPropertyInfo: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup412: rmrk_traits::base::BaseInfo<sp_core::crypto::AccountId32, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup435: rmrk_traits::base::BaseInfo<sp_core::crypto::AccountId32, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsBaseBaseInfo: {
     issuer: 'AccountId32',
@@ -3197,92 +3362,92 @@
     symbol: 'Bytes'
   },
   /**
-   * Lookup413: rmrk_traits::nft::NftChild
+   * Lookup436: rmrk_traits::nft::NftChild
    **/
   RmrkTraitsNftNftChild: {
     collectionId: 'u32',
     nftId: 'u32'
   },
   /**
-   * Lookup415: pallet_common::pallet::Error<T>
+   * Lookup438: pallet_common::pallet::Error<T>
    **/
   PalletCommonError: {
     _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal', 'ConfirmSponsorshipFail', 'UserIsNotCollectionAdmin']
   },
   /**
-   * Lookup417: pallet_fungible::pallet::Error<T>
+   * Lookup440: pallet_fungible::pallet::Error<T>
    **/
   PalletFungibleError: {
     _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingAllowanceForAllNotAllowed', 'FungibleTokensAreAlwaysValid']
   },
   /**
-   * Lookup418: pallet_refungible::ItemData
+   * Lookup441: pallet_refungible::ItemData
    **/
   PalletRefungibleItemData: {
     constData: 'Bytes'
   },
   /**
-   * Lookup423: pallet_refungible::pallet::Error<T>
+   * Lookup446: pallet_refungible::pallet::Error<T>
    **/
   PalletRefungibleError: {
     _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
   },
   /**
-   * Lookup424: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup447: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   PalletNonfungibleItemData: {
     owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
   },
   /**
-   * Lookup426: up_data_structs::PropertyScope
+   * Lookup449: up_data_structs::PropertyScope
    **/
   UpDataStructsPropertyScope: {
     _enum: ['None', 'Rmrk']
   },
   /**
-   * Lookup428: pallet_nonfungible::pallet::Error<T>
+   * Lookup451: pallet_nonfungible::pallet::Error<T>
    **/
   PalletNonfungibleError: {
     _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren']
   },
   /**
-   * Lookup429: pallet_structure::pallet::Error<T>
+   * Lookup452: pallet_structure::pallet::Error<T>
    **/
   PalletStructureError: {
     _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound']
   },
   /**
-   * Lookup430: pallet_rmrk_core::pallet::Error<T>
+   * Lookup453: pallet_rmrk_core::pallet::Error<T>
    **/
   PalletRmrkCoreError: {
     _enum: ['CorruptedCollectionType', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'RmrkPropertyIsNotFound', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'CannotRejectNonPendingNft', 'ResourceNotPending', 'NoAvailableResourceId']
   },
   /**
-   * Lookup432: pallet_rmrk_equip::pallet::Error<T>
+   * Lookup455: pallet_rmrk_equip::pallet::Error<T>
    **/
   PalletRmrkEquipError: {
     _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst', 'PartDoesntExist', 'NoEquippableOnFixedPart']
   },
   /**
-   * Lookup438: pallet_app_promotion::pallet::Error<T>
+   * Lookup461: pallet_app_promotion::pallet::Error<T>
    **/
   PalletAppPromotionError: {
     _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'IncorrectLockedBalanceOperation']
   },
   /**
-   * Lookup439: pallet_foreign_assets::module::Error<T>
+   * Lookup462: pallet_foreign_assets::module::Error<T>
    **/
   PalletForeignAssetsModuleError: {
     _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted']
   },
   /**
-   * Lookup441: pallet_evm::pallet::Error<T>
+   * Lookup464: pallet_evm::pallet::Error<T>
    **/
   PalletEvmError: {
     _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce', 'GasLimitTooLow', 'GasLimitTooHigh', 'Undefined', 'Reentrancy']
   },
   /**
-   * Lookup444: fp_rpc::TransactionStatus
+   * Lookup467: fp_rpc::TransactionStatus
    **/
   FpRpcTransactionStatus: {
     transactionHash: 'H256',
@@ -3294,11 +3459,11 @@
     logsBloom: 'EthbloomBloom'
   },
   /**
-   * Lookup446: ethbloom::Bloom
+   * Lookup469: ethbloom::Bloom
    **/
   EthbloomBloom: '[u8;256]',
   /**
-   * Lookup448: ethereum::receipt::ReceiptV3
+   * Lookup471: ethereum::receipt::ReceiptV3
    **/
   EthereumReceiptReceiptV3: {
     _enum: {
@@ -3308,7 +3473,7 @@
     }
   },
   /**
-   * Lookup449: ethereum::receipt::EIP658ReceiptData
+   * Lookup472: ethereum::receipt::EIP658ReceiptData
    **/
   EthereumReceiptEip658ReceiptData: {
     statusCode: 'u8',
@@ -3317,7 +3482,7 @@
     logs: 'Vec<EthereumLog>'
   },
   /**
-   * Lookup450: ethereum::block::Block<ethereum::transaction::TransactionV2>
+   * Lookup473: ethereum::block::Block<ethereum::transaction::TransactionV2>
    **/
   EthereumBlock: {
     header: 'EthereumHeader',
@@ -3325,7 +3490,7 @@
     ommers: 'Vec<EthereumHeader>'
   },
   /**
-   * Lookup451: ethereum::header::Header
+   * Lookup474: ethereum::header::Header
    **/
   EthereumHeader: {
     parentHash: 'H256',
@@ -3345,23 +3510,23 @@
     nonce: 'EthereumTypesHashH64'
   },
   /**
-   * Lookup452: ethereum_types::hash::H64
+   * Lookup475: ethereum_types::hash::H64
    **/
   EthereumTypesHashH64: '[u8;8]',
   /**
-   * Lookup457: pallet_ethereum::pallet::Error<T>
+   * Lookup480: pallet_ethereum::pallet::Error<T>
    **/
   PalletEthereumError: {
     _enum: ['InvalidSignature', 'PreLogExists']
   },
   /**
-   * Lookup458: pallet_evm_coder_substrate::pallet::Error<T>
+   * Lookup481: pallet_evm_coder_substrate::pallet::Error<T>
    **/
   PalletEvmCoderSubstrateError: {
     _enum: ['OutOfGas', 'OutOfFund']
   },
   /**
-   * Lookup459: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup482: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: {
     _enum: {
@@ -3371,35 +3536,35 @@
     }
   },
   /**
-   * Lookup460: pallet_evm_contract_helpers::SponsoringModeT
+   * Lookup483: pallet_evm_contract_helpers::SponsoringModeT
    **/
   PalletEvmContractHelpersSponsoringModeT: {
     _enum: ['Disabled', 'Allowlisted', 'Generous']
   },
   /**
-   * Lookup466: pallet_evm_contract_helpers::pallet::Error<T>
+   * Lookup489: pallet_evm_contract_helpers::pallet::Error<T>
    **/
   PalletEvmContractHelpersError: {
     _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit']
   },
   /**
-   * Lookup467: pallet_evm_migration::pallet::Error<T>
+   * Lookup490: pallet_evm_migration::pallet::Error<T>
    **/
   PalletEvmMigrationError: {
     _enum: ['AccountNotEmpty', 'AccountIsNotMigrating', 'BadEvent']
   },
   /**
-   * Lookup468: pallet_maintenance::pallet::Error<T>
+   * Lookup491: pallet_maintenance::pallet::Error<T>
    **/
   PalletMaintenanceError: 'Null',
   /**
-   * Lookup469: pallet_test_utils::pallet::Error<T>
+   * Lookup492: pallet_test_utils::pallet::Error<T>
    **/
   PalletTestUtilsError: {
     _enum: ['TestPalletDisabled', 'TriggerRollback']
   },
   /**
-   * Lookup471: sp_runtime::MultiSignature
+   * Lookup494: sp_runtime::MultiSignature
    **/
   SpRuntimeMultiSignature: {
     _enum: {
@@ -3409,51 +3574,51 @@
     }
   },
   /**
-   * Lookup472: sp_core::ed25519::Signature
+   * Lookup495: sp_core::ed25519::Signature
    **/
   SpCoreEd25519Signature: '[u8;64]',
   /**
-   * Lookup474: sp_core::sr25519::Signature
+   * Lookup497: sp_core::sr25519::Signature
    **/
   SpCoreSr25519Signature: '[u8;64]',
   /**
-   * Lookup475: sp_core::ecdsa::Signature
+   * Lookup498: sp_core::ecdsa::Signature
    **/
   SpCoreEcdsaSignature: '[u8;65]',
   /**
-   * Lookup478: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
+   * Lookup501: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
    **/
   FrameSystemExtensionsCheckSpecVersion: 'Null',
   /**
-   * Lookup479: frame_system::extensions::check_tx_version::CheckTxVersion<T>
+   * Lookup502: frame_system::extensions::check_tx_version::CheckTxVersion<T>
    **/
   FrameSystemExtensionsCheckTxVersion: 'Null',
   /**
-   * Lookup480: frame_system::extensions::check_genesis::CheckGenesis<T>
+   * Lookup503: frame_system::extensions::check_genesis::CheckGenesis<T>
    **/
   FrameSystemExtensionsCheckGenesis: 'Null',
   /**
-   * Lookup483: frame_system::extensions::check_nonce::CheckNonce<T>
+   * Lookup506: frame_system::extensions::check_nonce::CheckNonce<T>
    **/
   FrameSystemExtensionsCheckNonce: 'Compact<u32>',
   /**
-   * Lookup484: frame_system::extensions::check_weight::CheckWeight<T>
+   * Lookup507: frame_system::extensions::check_weight::CheckWeight<T>
    **/
   FrameSystemExtensionsCheckWeight: 'Null',
   /**
-   * Lookup485: opal_runtime::runtime_common::maintenance::CheckMaintenance
+   * Lookup508: opal_runtime::runtime_common::maintenance::CheckMaintenance
    **/
   OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: 'Null',
   /**
-   * Lookup486: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
+   * Lookup509: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
    **/
   PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact<u128>',
   /**
-   * Lookup487: opal_runtime::Runtime
+   * Lookup510: opal_runtime::Runtime
    **/
   OpalRuntimeRuntime: 'Null',
   /**
-   * Lookup488: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
+   * Lookup511: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
    **/
   PalletEthereumFakeTransactionFinalizer: 'Null'
 };
modifiedtests/src/interfaces/registry.tsdiffbeforeafterboth
--- a/tests/src/interfaces/registry.ts
+++ b/tests/src/interfaces/registry.ts
@@ -5,7 +5,7 @@
 // this is required to allow for ambient/previous definitions
 import '@polkadot/types/types/registry';
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletAuthorshipCall, PalletAuthorshipError, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeBlakeTwo256, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeHeader, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
 
 declare module '@polkadot/types/types/registry' {
   interface InterfaceTypes {
@@ -75,6 +75,7 @@
     FrameSystemPhase: FrameSystemPhase;
     OpalRuntimeRuntime: OpalRuntimeRuntime;
     OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance;
+    OpalRuntimeRuntimeCommonSessionKeys: OpalRuntimeRuntimeCommonSessionKeys;
     OrmlTokensAccountData: OrmlTokensAccountData;
     OrmlTokensBalanceLock: OrmlTokensBalanceLock;
     OrmlTokensModuleCall: OrmlTokensModuleCall;
@@ -91,6 +92,9 @@
     PalletAppPromotionCall: PalletAppPromotionCall;
     PalletAppPromotionError: PalletAppPromotionError;
     PalletAppPromotionEvent: PalletAppPromotionEvent;
+    PalletAuthorshipCall: PalletAuthorshipCall;
+    PalletAuthorshipError: PalletAuthorshipError;
+    PalletAuthorshipUncleEntryItem: PalletAuthorshipUncleEntryItem;
     PalletBalancesAccountData: PalletBalancesAccountData;
     PalletBalancesBalanceLock: PalletBalancesBalanceLock;
     PalletBalancesCall: PalletBalancesCall;
@@ -99,6 +103,9 @@
     PalletBalancesReasons: PalletBalancesReasons;
     PalletBalancesReleases: PalletBalancesReleases;
     PalletBalancesReserveData: PalletBalancesReserveData;
+    PalletCollatorSelectionCall: PalletCollatorSelectionCall;
+    PalletCollatorSelectionError: PalletCollatorSelectionError;
+    PalletCollatorSelectionEvent: PalletCollatorSelectionEvent;
     PalletCommonError: PalletCommonError;
     PalletCommonEvent: PalletCommonEvent;
     PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration;
@@ -140,6 +147,9 @@
     PalletRmrkEquipCall: PalletRmrkEquipCall;
     PalletRmrkEquipError: PalletRmrkEquipError;
     PalletRmrkEquipEvent: PalletRmrkEquipEvent;
+    PalletSessionCall: PalletSessionCall;
+    PalletSessionError: PalletSessionError;
+    PalletSessionEvent: PalletSessionEvent;
     PalletStructureCall: PalletStructureCall;
     PalletStructureError: PalletStructureError;
     PalletStructureEvent: PalletStructureEvent;
@@ -190,13 +200,18 @@
     RmrkTraitsResourceSlotResource: RmrkTraitsResourceSlotResource;
     RmrkTraitsTheme: RmrkTraitsTheme;
     RmrkTraitsThemeThemeProperty: RmrkTraitsThemeThemeProperty;
+    SpConsensusAuraSr25519AppSr25519Public: SpConsensusAuraSr25519AppSr25519Public;
+    SpCoreCryptoKeyTypeId: SpCoreCryptoKeyTypeId;
     SpCoreEcdsaSignature: SpCoreEcdsaSignature;
     SpCoreEd25519Signature: SpCoreEd25519Signature;
+    SpCoreSr25519Public: SpCoreSr25519Public;
     SpCoreSr25519Signature: SpCoreSr25519Signature;
     SpRuntimeArithmeticError: SpRuntimeArithmeticError;
+    SpRuntimeBlakeTwo256: SpRuntimeBlakeTwo256;
     SpRuntimeDigest: SpRuntimeDigest;
     SpRuntimeDigestDigestItem: SpRuntimeDigestDigestItem;
     SpRuntimeDispatchError: SpRuntimeDispatchError;
+    SpRuntimeHeader: SpRuntimeHeader;
     SpRuntimeModuleError: SpRuntimeModuleError;
     SpRuntimeMultiSignature: SpRuntimeMultiSignature;
     SpRuntimeTokenError: SpRuntimeTokenError;
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -196,7 +196,59 @@
     readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed';
   }
 
-  /** @name PalletBalancesEvent (30) */
+  /** @name PalletCollatorSelectionEvent (30) */
+  interface PalletCollatorSelectionEvent extends Enum {
+    readonly isNewDesiredCollators: boolean;
+    readonly asNewDesiredCollators: {
+      readonly desiredCollators: u32;
+    } & Struct;
+    readonly isNewLicenseBond: boolean;
+    readonly asNewLicenseBond: {
+      readonly bondAmount: u128;
+    } & Struct;
+    readonly isNewKickThreshold: boolean;
+    readonly asNewKickThreshold: {
+      readonly lengthInBlocks: u32;
+    } & Struct;
+    readonly isInvulnerableAdded: boolean;
+    readonly asInvulnerableAdded: {
+      readonly invulnerable: AccountId32;
+    } & Struct;
+    readonly isInvulnerableRemoved: boolean;
+    readonly asInvulnerableRemoved: {
+      readonly invulnerable: AccountId32;
+    } & Struct;
+    readonly isLicenseObtained: boolean;
+    readonly asLicenseObtained: {
+      readonly accountId: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isLicenseForfeited: boolean;
+    readonly asLicenseForfeited: {
+      readonly accountId: AccountId32;
+      readonly depositReturned: u128;
+    } & Struct;
+    readonly isCandidateAdded: boolean;
+    readonly asCandidateAdded: {
+      readonly accountId: AccountId32;
+    } & Struct;
+    readonly isCandidateRemoved: boolean;
+    readonly asCandidateRemoved: {
+      readonly accountId: AccountId32;
+    } & Struct;
+    readonly type: 'NewDesiredCollators' | 'NewLicenseBond' | 'NewKickThreshold' | 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseForfeited' | 'CandidateAdded' | 'CandidateRemoved';
+  }
+
+  /** @name PalletSessionEvent (31) */
+  interface PalletSessionEvent extends Enum {
+    readonly isNewSession: boolean;
+    readonly asNewSession: {
+      readonly sessionIndex: u32;
+    } & Struct;
+    readonly type: 'NewSession';
+  }
+
+  /** @name PalletBalancesEvent (32) */
   interface PalletBalancesEvent extends Enum {
     readonly isEndowed: boolean;
     readonly asEndowed: {
@@ -255,14 +307,14 @@
     readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed';
   }
 
-  /** @name FrameSupportTokensMiscBalanceStatus (31) */
+  /** @name FrameSupportTokensMiscBalanceStatus (33) */
   interface FrameSupportTokensMiscBalanceStatus extends Enum {
     readonly isFree: boolean;
     readonly isReserved: boolean;
     readonly type: 'Free' | 'Reserved';
   }
 
-  /** @name PalletTransactionPaymentEvent (32) */
+  /** @name PalletTransactionPaymentEvent (34) */
   interface PalletTransactionPaymentEvent extends Enum {
     readonly isTransactionFeePaid: boolean;
     readonly asTransactionFeePaid: {
@@ -273,7 +325,7 @@
     readonly type: 'TransactionFeePaid';
   }
 
-  /** @name PalletTreasuryEvent (33) */
+  /** @name PalletTreasuryEvent (35) */
   interface PalletTreasuryEvent extends Enum {
     readonly isProposed: boolean;
     readonly asProposed: {
@@ -315,7 +367,7 @@
     readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved';
   }
 
-  /** @name PalletSudoEvent (34) */
+  /** @name PalletSudoEvent (36) */
   interface PalletSudoEvent extends Enum {
     readonly isSudid: boolean;
     readonly asSudid: {
@@ -332,7 +384,7 @@
     readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone';
   }
 
-  /** @name OrmlVestingModuleEvent (38) */
+  /** @name OrmlVestingModuleEvent (40) */
   interface OrmlVestingModuleEvent extends Enum {
     readonly isVestingScheduleAdded: boolean;
     readonly asVestingScheduleAdded: {
@@ -352,7 +404,7 @@
     readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated';
   }
 
-  /** @name OrmlVestingVestingSchedule (39) */
+  /** @name OrmlVestingVestingSchedule (41) */
   interface OrmlVestingVestingSchedule extends Struct {
     readonly start: u32;
     readonly period: u32;
@@ -360,7 +412,7 @@
     readonly perPeriod: Compact<u128>;
   }
 
-  /** @name OrmlXtokensModuleEvent (41) */
+  /** @name OrmlXtokensModuleEvent (43) */
   interface OrmlXtokensModuleEvent extends Enum {
     readonly isTransferredMultiAssets: boolean;
     readonly asTransferredMultiAssets: {
@@ -372,16 +424,16 @@
     readonly type: 'TransferredMultiAssets';
   }
 
-  /** @name XcmV1MultiassetMultiAssets (42) */
+  /** @name XcmV1MultiassetMultiAssets (44) */
   interface XcmV1MultiassetMultiAssets extends Vec<XcmV1MultiAsset> {}
 
-  /** @name XcmV1MultiAsset (44) */
+  /** @name XcmV1MultiAsset (46) */
   interface XcmV1MultiAsset extends Struct {
     readonly id: XcmV1MultiassetAssetId;
     readonly fun: XcmV1MultiassetFungibility;
   }
 
-  /** @name XcmV1MultiassetAssetId (45) */
+  /** @name XcmV1MultiassetAssetId (47) */
   interface XcmV1MultiassetAssetId extends Enum {
     readonly isConcrete: boolean;
     readonly asConcrete: XcmV1MultiLocation;
@@ -390,13 +442,13 @@
     readonly type: 'Concrete' | 'Abstract';
   }
 
-  /** @name XcmV1MultiLocation (46) */
+  /** @name XcmV1MultiLocation (48) */
   interface XcmV1MultiLocation extends Struct {
     readonly parents: u8;
     readonly interior: XcmV1MultilocationJunctions;
   }
 
-  /** @name XcmV1MultilocationJunctions (47) */
+  /** @name XcmV1MultilocationJunctions (49) */
   interface XcmV1MultilocationJunctions extends Enum {
     readonly isHere: boolean;
     readonly isX1: boolean;
@@ -418,7 +470,7 @@
     readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
   }
 
-  /** @name XcmV1Junction (48) */
+  /** @name XcmV1Junction (50) */
   interface XcmV1Junction extends Enum {
     readonly isParachain: boolean;
     readonly asParachain: Compact<u32>;
@@ -452,7 +504,7 @@
     readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality';
   }
 
-  /** @name XcmV0JunctionNetworkId (50) */
+  /** @name XcmV0JunctionNetworkId (52) */
   interface XcmV0JunctionNetworkId extends Enum {
     readonly isAny: boolean;
     readonly isNamed: boolean;
@@ -462,7 +514,7 @@
     readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama';
   }
 
-  /** @name XcmV0JunctionBodyId (53) */
+  /** @name XcmV0JunctionBodyId (55) */
   interface XcmV0JunctionBodyId extends Enum {
     readonly isUnit: boolean;
     readonly isNamed: boolean;
@@ -476,7 +528,7 @@
     readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial';
   }
 
-  /** @name XcmV0JunctionBodyPart (54) */
+  /** @name XcmV0JunctionBodyPart (56) */
   interface XcmV0JunctionBodyPart extends Enum {
     readonly isVoice: boolean;
     readonly isMembers: boolean;
@@ -501,7 +553,7 @@
     readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
   }
 
-  /** @name XcmV1MultiassetFungibility (55) */
+  /** @name XcmV1MultiassetFungibility (57) */
   interface XcmV1MultiassetFungibility extends Enum {
     readonly isFungible: boolean;
     readonly asFungible: Compact<u128>;
@@ -510,7 +562,7 @@
     readonly type: 'Fungible' | 'NonFungible';
   }
 
-  /** @name XcmV1MultiassetAssetInstance (56) */
+  /** @name XcmV1MultiassetAssetInstance (58) */
   interface XcmV1MultiassetAssetInstance extends Enum {
     readonly isUndefined: boolean;
     readonly isIndex: boolean;
@@ -528,7 +580,7 @@
     readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob';
   }
 
-  /** @name OrmlTokensModuleEvent (59) */
+  /** @name OrmlTokensModuleEvent (61) */
   interface OrmlTokensModuleEvent extends Enum {
     readonly isEndowed: boolean;
     readonly asEndowed: {
@@ -616,7 +668,7 @@
     readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved';
   }
 
-  /** @name PalletForeignAssetsAssetIds (60) */
+  /** @name PalletForeignAssetsAssetIds (62) */
   interface PalletForeignAssetsAssetIds extends Enum {
     readonly isForeignAssetId: boolean;
     readonly asForeignAssetId: u32;
@@ -625,14 +677,14 @@
     readonly type: 'ForeignAssetId' | 'NativeAssetId';
   }
 
-  /** @name PalletForeignAssetsNativeCurrency (61) */
+  /** @name PalletForeignAssetsNativeCurrency (63) */
   interface PalletForeignAssetsNativeCurrency extends Enum {
     readonly isHere: boolean;
     readonly isParent: boolean;
     readonly type: 'Here' | 'Parent';
   }
 
-  /** @name CumulusPalletXcmpQueueEvent (62) */
+  /** @name CumulusPalletXcmpQueueEvent (64) */
   interface CumulusPalletXcmpQueueEvent extends Enum {
     readonly isSuccess: boolean;
     readonly asSuccess: {
@@ -676,7 +728,7 @@
     readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'UpwardMessageSent' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced';
   }
 
-  /** @name XcmV2TraitsError (64) */
+  /** @name XcmV2TraitsError (66) */
   interface XcmV2TraitsError extends Enum {
     readonly isOverflow: boolean;
     readonly isUnimplemented: boolean;
@@ -709,7 +761,7 @@
     readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable';
   }
 
-  /** @name PalletXcmEvent (66) */
+  /** @name PalletXcmEvent (68) */
   interface PalletXcmEvent extends Enum {
     readonly isAttempted: boolean;
     readonly asAttempted: XcmV2TraitsOutcome;
@@ -748,7 +800,7 @@
     readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'AssetsClaimed';
   }
 
-  /** @name XcmV2TraitsOutcome (67) */
+  /** @name XcmV2TraitsOutcome (69) */
   interface XcmV2TraitsOutcome extends Enum {
     readonly isComplete: boolean;
     readonly asComplete: u64;
@@ -759,10 +811,10 @@
     readonly type: 'Complete' | 'Incomplete' | 'Error';
   }
 
-  /** @name XcmV2Xcm (68) */
+  /** @name XcmV2Xcm (70) */
   interface XcmV2Xcm extends Vec<XcmV2Instruction> {}
 
-  /** @name XcmV2Instruction (70) */
+  /** @name XcmV2Instruction (72) */
   interface XcmV2Instruction extends Enum {
     readonly isWithdrawAsset: boolean;
     readonly asWithdrawAsset: XcmV1MultiassetMultiAssets;
@@ -882,7 +934,7 @@
     readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion';
   }
 
-  /** @name XcmV2Response (71) */
+  /** @name XcmV2Response (73) */
   interface XcmV2Response extends Enum {
     readonly isNull: boolean;
     readonly isAssets: boolean;
@@ -894,7 +946,7 @@
     readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version';
   }
 
-  /** @name XcmV0OriginKind (74) */
+  /** @name XcmV0OriginKind (76) */
   interface XcmV0OriginKind extends Enum {
     readonly isNative: boolean;
     readonly isSovereignAccount: boolean;
@@ -903,12 +955,12 @@
     readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm';
   }
 
-  /** @name XcmDoubleEncoded (75) */
+  /** @name XcmDoubleEncoded (77) */
   interface XcmDoubleEncoded extends Struct {
     readonly encoded: Bytes;
   }
 
-  /** @name XcmV1MultiassetMultiAssetFilter (76) */
+  /** @name XcmV1MultiassetMultiAssetFilter (78) */
   interface XcmV1MultiassetMultiAssetFilter extends Enum {
     readonly isDefinite: boolean;
     readonly asDefinite: XcmV1MultiassetMultiAssets;
@@ -917,7 +969,7 @@
     readonly type: 'Definite' | 'Wild';
   }
 
-  /** @name XcmV1MultiassetWildMultiAsset (77) */
+  /** @name XcmV1MultiassetWildMultiAsset (79) */
   interface XcmV1MultiassetWildMultiAsset extends Enum {
     readonly isAll: boolean;
     readonly isAllOf: boolean;
@@ -928,14 +980,14 @@
     readonly type: 'All' | 'AllOf';
   }
 
-  /** @name XcmV1MultiassetWildFungibility (78) */
+  /** @name XcmV1MultiassetWildFungibility (80) */
   interface XcmV1MultiassetWildFungibility extends Enum {
     readonly isFungible: boolean;
     readonly isNonFungible: boolean;
     readonly type: 'Fungible' | 'NonFungible';
   }
 
-  /** @name XcmV2WeightLimit (79) */
+  /** @name XcmV2WeightLimit (81) */
   interface XcmV2WeightLimit extends Enum {
     readonly isUnlimited: boolean;
     readonly isLimited: boolean;
@@ -943,7 +995,7 @@
     readonly type: 'Unlimited' | 'Limited';
   }
 
-  /** @name XcmVersionedMultiAssets (81) */
+  /** @name XcmVersionedMultiAssets (83) */
   interface XcmVersionedMultiAssets extends Enum {
     readonly isV0: boolean;
     readonly asV0: Vec<XcmV0MultiAsset>;
@@ -952,7 +1004,7 @@
     readonly type: 'V0' | 'V1';
   }
 
-  /** @name XcmV0MultiAsset (83) */
+  /** @name XcmV0MultiAsset (85) */
   interface XcmV0MultiAsset extends Enum {
     readonly isNone: boolean;
     readonly isAll: boolean;
@@ -997,7 +1049,7 @@
     readonly type: 'None' | 'All' | 'AllFungible' | 'AllNonFungible' | 'AllAbstractFungible' | 'AllAbstractNonFungible' | 'AllConcreteFungible' | 'AllConcreteNonFungible' | 'AbstractFungible' | 'AbstractNonFungible' | 'ConcreteFungible' | 'ConcreteNonFungible';
   }
 
-  /** @name XcmV0MultiLocation (84) */
+  /** @name XcmV0MultiLocation (86) */
   interface XcmV0MultiLocation extends Enum {
     readonly isNull: boolean;
     readonly isX1: boolean;
@@ -1019,7 +1071,7 @@
     readonly type: 'Null' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
   }
 
-  /** @name XcmV0Junction (85) */
+  /** @name XcmV0Junction (87) */
   interface XcmV0Junction extends Enum {
     readonly isParent: boolean;
     readonly isParachain: boolean;
@@ -1054,7 +1106,7 @@
     readonly type: 'Parent' | 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality';
   }
 
-  /** @name XcmVersionedMultiLocation (86) */
+  /** @name XcmVersionedMultiLocation (88) */
   interface XcmVersionedMultiLocation extends Enum {
     readonly isV0: boolean;
     readonly asV0: XcmV0MultiLocation;
@@ -1063,7 +1115,7 @@
     readonly type: 'V0' | 'V1';
   }
 
-  /** @name CumulusPalletXcmEvent (87) */
+  /** @name CumulusPalletXcmEvent (89) */
   interface CumulusPalletXcmEvent extends Enum {
     readonly isInvalidFormat: boolean;
     readonly asInvalidFormat: U8aFixed;
@@ -1074,7 +1126,7 @@
     readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward';
   }
 
-  /** @name CumulusPalletDmpQueueEvent (88) */
+  /** @name CumulusPalletDmpQueueEvent (90) */
   interface CumulusPalletDmpQueueEvent extends Enum {
     readonly isInvalidFormat: boolean;
     readonly asInvalidFormat: {
@@ -1109,7 +1161,7 @@
     readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced';
   }
 
-  /** @name PalletCommonEvent (89) */
+  /** @name PalletCommonEvent (91) */
   interface PalletCommonEvent extends Enum {
     readonly isCollectionCreated: boolean;
     readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
@@ -1158,7 +1210,7 @@
     readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved';
   }
 
-  /** @name PalletEvmAccountBasicCrossAccountIdRepr (92) */
+  /** @name PalletEvmAccountBasicCrossAccountIdRepr (94) */
   interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum {
     readonly isSubstrate: boolean;
     readonly asSubstrate: AccountId32;
@@ -1167,14 +1219,14 @@
     readonly type: 'Substrate' | 'Ethereum';
   }
 
-  /** @name PalletStructureEvent (96) */
+  /** @name PalletStructureEvent (98) */
   interface PalletStructureEvent extends Enum {
     readonly isExecuted: boolean;
     readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
     readonly type: 'Executed';
   }
 
-  /** @name PalletRmrkCoreEvent (97) */
+  /** @name PalletRmrkCoreEvent (99) */
   interface PalletRmrkCoreEvent extends Enum {
     readonly isCollectionCreated: boolean;
     readonly asCollectionCreated: {
@@ -1264,7 +1316,7 @@
     readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'IssuerChanged' | 'CollectionLocked' | 'NftMinted' | 'NftBurned' | 'NftSent' | 'NftAccepted' | 'NftRejected' | 'PropertySet' | 'ResourceAdded' | 'ResourceRemoval' | 'ResourceAccepted' | 'ResourceRemovalAccepted' | 'PrioritySet';
   }
 
-  /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (98) */
+  /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (100) */
   interface RmrkTraitsNftAccountIdOrCollectionNftTuple extends Enum {
     readonly isAccountId: boolean;
     readonly asAccountId: AccountId32;
@@ -1273,7 +1325,7 @@
     readonly type: 'AccountId' | 'CollectionAndNftTuple';
   }
 
-  /** @name PalletRmrkEquipEvent (102) */
+  /** @name PalletRmrkEquipEvent (104) */
   interface PalletRmrkEquipEvent extends Enum {
     readonly isBaseCreated: boolean;
     readonly asBaseCreated: {
@@ -1288,7 +1340,7 @@
     readonly type: 'BaseCreated' | 'EquippablesUpdated';
   }
 
-  /** @name PalletAppPromotionEvent (103) */
+  /** @name PalletAppPromotionEvent (105) */
   interface PalletAppPromotionEvent extends Enum {
     readonly isStakingRecalculation: boolean;
     readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
@@ -1301,7 +1353,7 @@
     readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
   }
 
-  /** @name PalletForeignAssetsModuleEvent (104) */
+  /** @name PalletForeignAssetsModuleEvent (106) */
   interface PalletForeignAssetsModuleEvent extends Enum {
     readonly isForeignAssetRegistered: boolean;
     readonly asForeignAssetRegistered: {
@@ -1328,7 +1380,7 @@
     readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated';
   }
 
-  /** @name PalletForeignAssetsModuleAssetMetadata (105) */
+  /** @name PalletForeignAssetsModuleAssetMetadata (107) */
   interface PalletForeignAssetsModuleAssetMetadata extends Struct {
     readonly name: Bytes;
     readonly symbol: Bytes;
@@ -1336,7 +1388,7 @@
     readonly minimalBalance: u128;
   }
 
-  /** @name PalletEvmEvent (106) */
+  /** @name PalletEvmEvent (108) */
   interface PalletEvmEvent extends Enum {
     readonly isLog: boolean;
     readonly asLog: {
@@ -1361,14 +1413,14 @@
     readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed';
   }
 
-  /** @name EthereumLog (107) */
+  /** @name EthereumLog (109) */
   interface EthereumLog extends Struct {
     readonly address: H160;
     readonly topics: Vec<H256>;
     readonly data: Bytes;
   }
 
-  /** @name PalletEthereumEvent (109) */
+  /** @name PalletEthereumEvent (111) */
   interface PalletEthereumEvent extends Enum {
     readonly isExecuted: boolean;
     readonly asExecuted: {
@@ -1380,7 +1432,7 @@
     readonly type: 'Executed';
   }
 
-  /** @name EvmCoreErrorExitReason (110) */
+  /** @name EvmCoreErrorExitReason (112) */
   interface EvmCoreErrorExitReason extends Enum {
     readonly isSucceed: boolean;
     readonly asSucceed: EvmCoreErrorExitSucceed;
@@ -1393,7 +1445,7 @@
     readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
   }
 
-  /** @name EvmCoreErrorExitSucceed (111) */
+  /** @name EvmCoreErrorExitSucceed (113) */
   interface EvmCoreErrorExitSucceed extends Enum {
     readonly isStopped: boolean;
     readonly isReturned: boolean;
@@ -1401,7 +1453,7 @@
     readonly type: 'Stopped' | 'Returned' | 'Suicided';
   }
 
-  /** @name EvmCoreErrorExitError (112) */
+  /** @name EvmCoreErrorExitError (114) */
   interface EvmCoreErrorExitError extends Enum {
     readonly isStackUnderflow: boolean;
     readonly isStackOverflow: boolean;
@@ -1422,13 +1474,13 @@
     readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'InvalidCode';
   }
 
-  /** @name EvmCoreErrorExitRevert (115) */
+  /** @name EvmCoreErrorExitRevert (117) */
   interface EvmCoreErrorExitRevert extends Enum {
     readonly isReverted: boolean;
     readonly type: 'Reverted';
   }
 
-  /** @name EvmCoreErrorExitFatal (116) */
+  /** @name EvmCoreErrorExitFatal (118) */
   interface EvmCoreErrorExitFatal extends Enum {
     readonly isNotSupported: boolean;
     readonly isUnhandledInterrupt: boolean;
@@ -1439,7 +1491,7 @@
     readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
   }
 
-  /** @name PalletEvmContractHelpersEvent (117) */
+  /** @name PalletEvmContractHelpersEvent (119) */
   interface PalletEvmContractHelpersEvent extends Enum {
     readonly isContractSponsorSet: boolean;
     readonly asContractSponsorSet: ITuple<[H160, AccountId32]>;
@@ -1450,20 +1502,20 @@
     readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved';
   }
 
-  /** @name PalletEvmMigrationEvent (118) */
+  /** @name PalletEvmMigrationEvent (120) */
   interface PalletEvmMigrationEvent extends Enum {
     readonly isTestEvent: boolean;
     readonly type: 'TestEvent';
   }
 
-  /** @name PalletMaintenanceEvent (119) */
+  /** @name PalletMaintenanceEvent (121) */
   interface PalletMaintenanceEvent extends Enum {
     readonly isMaintenanceEnabled: boolean;
     readonly isMaintenanceDisabled: boolean;
     readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled';
   }
 
-  /** @name PalletTestUtilsEvent (120) */
+  /** @name PalletTestUtilsEvent (122) */
   interface PalletTestUtilsEvent extends Enum {
     readonly isValueIsSet: boolean;
     readonly isShouldRollback: boolean;
@@ -1471,7 +1523,7 @@
     readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted';
   }
 
-  /** @name FrameSystemPhase (121) */
+  /** @name FrameSystemPhase (123) */
   interface FrameSystemPhase extends Enum {
     readonly isApplyExtrinsic: boolean;
     readonly asApplyExtrinsic: u32;
@@ -1480,13 +1532,13 @@
     readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
   }
 
-  /** @name FrameSystemLastRuntimeUpgradeInfo (124) */
+  /** @name FrameSystemLastRuntimeUpgradeInfo (126) */
   interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
     readonly specVersion: Compact<u32>;
     readonly specName: Text;
   }
 
-  /** @name FrameSystemCall (125) */
+  /** @name FrameSystemCall (127) */
   interface FrameSystemCall extends Enum {
     readonly isFillBlock: boolean;
     readonly asFillBlock: {
@@ -1528,21 +1580,21 @@
     readonly type: 'FillBlock' | 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent';
   }
 
-  /** @name FrameSystemLimitsBlockWeights (130) */
+  /** @name FrameSystemLimitsBlockWeights (132) */
   interface FrameSystemLimitsBlockWeights extends Struct {
     readonly baseBlock: SpWeightsWeightV2Weight;
     readonly maxBlock: SpWeightsWeightV2Weight;
     readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
   }
 
-  /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (131) */
+  /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (133) */
   interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct {
     readonly normal: FrameSystemLimitsWeightsPerClass;
     readonly operational: FrameSystemLimitsWeightsPerClass;
     readonly mandatory: FrameSystemLimitsWeightsPerClass;
   }
 
-  /** @name FrameSystemLimitsWeightsPerClass (132) */
+  /** @name FrameSystemLimitsWeightsPerClass (134) */
   interface FrameSystemLimitsWeightsPerClass extends Struct {
     readonly baseExtrinsic: SpWeightsWeightV2Weight;
     readonly maxExtrinsic: Option<SpWeightsWeightV2Weight>;
@@ -1550,25 +1602,25 @@
     readonly reserved: Option<SpWeightsWeightV2Weight>;
   }
 
-  /** @name FrameSystemLimitsBlockLength (134) */
+  /** @name FrameSystemLimitsBlockLength (136) */
   interface FrameSystemLimitsBlockLength extends Struct {
     readonly max: FrameSupportDispatchPerDispatchClassU32;
   }
 
-  /** @name FrameSupportDispatchPerDispatchClassU32 (135) */
+  /** @name FrameSupportDispatchPerDispatchClassU32 (137) */
   interface FrameSupportDispatchPerDispatchClassU32 extends Struct {
     readonly normal: u32;
     readonly operational: u32;
     readonly mandatory: u32;
   }
 
-  /** @name SpWeightsRuntimeDbWeight (136) */
+  /** @name SpWeightsRuntimeDbWeight (138) */
   interface SpWeightsRuntimeDbWeight extends Struct {
     readonly read: u64;
     readonly write: u64;
   }
 
-  /** @name SpVersionRuntimeVersion (137) */
+  /** @name SpVersionRuntimeVersion (139) */
   interface SpVersionRuntimeVersion extends Struct {
     readonly specName: Text;
     readonly implName: Text;
@@ -1580,7 +1632,7 @@
     readonly stateVersion: u8;
   }
 
-  /** @name FrameSystemError (142) */
+  /** @name FrameSystemError (144) */
   interface FrameSystemError extends Enum {
     readonly isInvalidSpecName: boolean;
     readonly isSpecVersionNeedsToIncrease: boolean;
@@ -1591,7 +1643,7 @@
     readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
   }
 
-  /** @name PolkadotPrimitivesV2PersistedValidationData (143) */
+  /** @name PolkadotPrimitivesV2PersistedValidationData (145) */
   interface PolkadotPrimitivesV2PersistedValidationData extends Struct {
     readonly parentHead: Bytes;
     readonly relayParentNumber: u32;
@@ -1599,18 +1651,18 @@
     readonly maxPovSize: u32;
   }
 
-  /** @name PolkadotPrimitivesV2UpgradeRestriction (146) */
+  /** @name PolkadotPrimitivesV2UpgradeRestriction (148) */
   interface PolkadotPrimitivesV2UpgradeRestriction extends Enum {
     readonly isPresent: boolean;
     readonly type: 'Present';
   }
 
-  /** @name SpTrieStorageProof (147) */
+  /** @name SpTrieStorageProof (149) */
   interface SpTrieStorageProof extends Struct {
     readonly trieNodes: BTreeSet<Bytes>;
   }
 
-  /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (149) */
+  /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (151) */
   interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct {
     readonly dmqMqcHead: H256;
     readonly relayDispatchQueueSize: ITuple<[u32, u32]>;
@@ -1618,7 +1670,7 @@
     readonly egressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV2AbridgedHrmpChannel]>>;
   }
 
-  /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (152) */
+  /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (154) */
   interface PolkadotPrimitivesV2AbridgedHrmpChannel extends Struct {
     readonly maxCapacity: u32;
     readonly maxTotalSize: u32;
@@ -1628,7 +1680,7 @@
     readonly mqcHead: Option<H256>;
   }
 
-  /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (153) */
+  /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (155) */
   interface PolkadotPrimitivesV2AbridgedHostConfiguration extends Struct {
     readonly maxCodeSize: u32;
     readonly maxHeadDataSize: u32;
@@ -1641,13 +1693,13 @@
     readonly validationUpgradeDelay: u32;
   }
 
-  /** @name PolkadotCorePrimitivesOutboundHrmpMessage (159) */
+  /** @name PolkadotCorePrimitivesOutboundHrmpMessage (161) */
   interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct {
     readonly recipient: u32;
     readonly data: Bytes;
   }
 
-  /** @name CumulusPalletParachainSystemCall (160) */
+  /** @name CumulusPalletParachainSystemCall (162) */
   interface CumulusPalletParachainSystemCall extends Enum {
     readonly isSetValidationData: boolean;
     readonly asSetValidationData: {
@@ -1668,7 +1720,7 @@
     readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade';
   }
 
-  /** @name CumulusPrimitivesParachainInherentParachainInherentData (161) */
+  /** @name CumulusPrimitivesParachainInherentParachainInherentData (163) */
   interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct {
     readonly validationData: PolkadotPrimitivesV2PersistedValidationData;
     readonly relayChainState: SpTrieStorageProof;
@@ -1676,19 +1728,19 @@
     readonly horizontalMessages: BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>;
   }
 
-  /** @name PolkadotCorePrimitivesInboundDownwardMessage (163) */
+  /** @name PolkadotCorePrimitivesInboundDownwardMessage (165) */
   interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct {
     readonly sentAt: u32;
     readonly msg: Bytes;
   }
 
-  /** @name PolkadotCorePrimitivesInboundHrmpMessage (166) */
+  /** @name PolkadotCorePrimitivesInboundHrmpMessage (168) */
   interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct {
     readonly sentAt: u32;
     readonly data: Bytes;
   }
 
-  /** @name CumulusPalletParachainSystemError (169) */
+  /** @name CumulusPalletParachainSystemError (171) */
   interface CumulusPalletParachainSystemError extends Enum {
     readonly isOverlappingUpgrades: boolean;
     readonly isProhibitedByPolkadot: boolean;
@@ -1701,14 +1753,142 @@
     readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized';
   }
 
-  /** @name PalletBalancesBalanceLock (171) */
+  /** @name PalletAuthorshipUncleEntryItem (173) */
+  interface PalletAuthorshipUncleEntryItem extends Enum {
+    readonly isInclusionHeight: boolean;
+    readonly asInclusionHeight: u32;
+    readonly isUncle: boolean;
+    readonly asUncle: ITuple<[H256, Option<AccountId32>]>;
+    readonly type: 'InclusionHeight' | 'Uncle';
+  }
+
+  /** @name PalletAuthorshipCall (175) */
+  interface PalletAuthorshipCall extends Enum {
+    readonly isSetUncles: boolean;
+    readonly asSetUncles: {
+      readonly newUncles: Vec<SpRuntimeHeader>;
+    } & Struct;
+    readonly type: 'SetUncles';
+  }
+
+  /** @name SpRuntimeHeader (177) */
+  interface SpRuntimeHeader extends Struct {
+    readonly parentHash: H256;
+    readonly number: Compact<u32>;
+    readonly stateRoot: H256;
+    readonly extrinsicsRoot: H256;
+    readonly digest: SpRuntimeDigest;
+  }
+
+  /** @name SpRuntimeBlakeTwo256 (178) */
+  type SpRuntimeBlakeTwo256 = Null;
+
+  /** @name PalletAuthorshipError (179) */
+  interface PalletAuthorshipError extends Enum {
+    readonly isInvalidUncleParent: boolean;
+    readonly isUnclesAlreadySet: boolean;
+    readonly isTooManyUncles: boolean;
+    readonly isGenesisUncle: boolean;
+    readonly isTooHighUncle: boolean;
+    readonly isUncleAlreadyIncluded: boolean;
+    readonly isOldUncle: boolean;
+    readonly type: 'InvalidUncleParent' | 'UnclesAlreadySet' | 'TooManyUncles' | 'GenesisUncle' | 'TooHighUncle' | 'UncleAlreadyIncluded' | 'OldUncle';
+  }
+
+  /** @name PalletCollatorSelectionCall (182) */
+  interface PalletCollatorSelectionCall extends Enum {
+    readonly isAddInvulnerable: boolean;
+    readonly asAddInvulnerable: {
+      readonly new_: AccountId32;
+    } & Struct;
+    readonly isRemoveInvulnerable: boolean;
+    readonly asRemoveInvulnerable: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly isSetDesiredCollators: boolean;
+    readonly asSetDesiredCollators: {
+      readonly max: u32;
+    } & Struct;
+    readonly isSetLicenseBond: boolean;
+    readonly asSetLicenseBond: {
+      readonly bond: u128;
+    } & Struct;
+    readonly isSetKickThreshold: boolean;
+    readonly asSetKickThreshold: {
+      readonly kickThreshold: u32;
+    } & Struct;
+    readonly isGetLicense: boolean;
+    readonly isOnboard: boolean;
+    readonly isOffboard: boolean;
+    readonly isReleaseLicense: boolean;
+    readonly isForceRevokeLicense: boolean;
+    readonly asForceRevokeLicense: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'SetDesiredCollators' | 'SetLicenseBond' | 'SetKickThreshold' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceRevokeLicense';
+  }
+
+  /** @name PalletCollatorSelectionError (183) */
+  interface PalletCollatorSelectionError extends Enum {
+    readonly isTooManyCandidates: boolean;
+    readonly isUnknown: boolean;
+    readonly isPermission: boolean;
+    readonly isAlreadyHoldingLicense: boolean;
+    readonly isNoLicense: boolean;
+    readonly isAlreadyCandidate: boolean;
+    readonly isNotCandidate: boolean;
+    readonly isTooManyInvulnerables: boolean;
+    readonly isTooFewInvulnerables: boolean;
+    readonly isAlreadyInvulnerable: boolean;
+    readonly isNotInvulnerable: boolean;
+    readonly isNoAssociatedValidatorId: boolean;
+    readonly isValidatorNotRegistered: boolean;
+    readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered';
+  }
+
+  /** @name OpalRuntimeRuntimeCommonSessionKeys (186) */
+  interface OpalRuntimeRuntimeCommonSessionKeys extends Struct {
+    readonly aura: SpConsensusAuraSr25519AppSr25519Public;
+  }
+
+  /** @name SpConsensusAuraSr25519AppSr25519Public (187) */
+  interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {}
+
+  /** @name SpCoreSr25519Public (188) */
+  interface SpCoreSr25519Public extends U8aFixed {}
+
+  /** @name SpCoreCryptoKeyTypeId (191) */
+  interface SpCoreCryptoKeyTypeId extends U8aFixed {}
+
+  /** @name PalletSessionCall (192) */
+  interface PalletSessionCall extends Enum {
+    readonly isSetKeys: boolean;
+    readonly asSetKeys: {
+      readonly keys_: OpalRuntimeRuntimeCommonSessionKeys;
+      readonly proof: Bytes;
+    } & Struct;
+    readonly isPurgeKeys: boolean;
+    readonly type: 'SetKeys' | 'PurgeKeys';
+  }
+
+  /** @name PalletSessionError (193) */
+  interface PalletSessionError extends Enum {
+    readonly isInvalidProof: boolean;
+    readonly isNoAssociatedValidatorId: boolean;
+    readonly isDuplicatedKey: boolean;
+    readonly isNoKeys: boolean;
+    readonly isNoAccount: boolean;
+    readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount';
+  }
+
+  /** @name PalletBalancesBalanceLock (195) */
   interface PalletBalancesBalanceLock extends Struct {
     readonly id: U8aFixed;
     readonly amount: u128;
     readonly reasons: PalletBalancesReasons;
   }
 
-  /** @name PalletBalancesReasons (172) */
+  /** @name PalletBalancesReasons (196) */
   interface PalletBalancesReasons extends Enum {
     readonly isFee: boolean;
     readonly isMisc: boolean;
@@ -1716,20 +1896,20 @@
     readonly type: 'Fee' | 'Misc' | 'All';
   }
 
-  /** @name PalletBalancesReserveData (175) */
+  /** @name PalletBalancesReserveData (199) */
   interface PalletBalancesReserveData extends Struct {
     readonly id: U8aFixed;
     readonly amount: u128;
   }
 
-  /** @name PalletBalancesReleases (177) */
+  /** @name PalletBalancesReleases (201) */
   interface PalletBalancesReleases extends Enum {
     readonly isV100: boolean;
     readonly isV200: boolean;
     readonly type: 'V100' | 'V200';
   }
 
-  /** @name PalletBalancesCall (178) */
+  /** @name PalletBalancesCall (202) */
   interface PalletBalancesCall extends Enum {
     readonly isTransfer: boolean;
     readonly asTransfer: {
@@ -1766,7 +1946,7 @@
     readonly type: 'Transfer' | 'SetBalance' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve';
   }
 
-  /** @name PalletBalancesError (181) */
+  /** @name PalletBalancesError (205) */
   interface PalletBalancesError extends Enum {
     readonly isVestingBalance: boolean;
     readonly isLiquidityRestrictions: boolean;
@@ -1779,7 +1959,7 @@
     readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'KeepAlive' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves';
   }
 
-  /** @name PalletTimestampCall (183) */
+  /** @name PalletTimestampCall (207) */
   interface PalletTimestampCall extends Enum {
     readonly isSet: boolean;
     readonly asSet: {
@@ -1788,14 +1968,14 @@
     readonly type: 'Set';
   }
 
-  /** @name PalletTransactionPaymentReleases (185) */
+  /** @name PalletTransactionPaymentReleases (209) */
   interface PalletTransactionPaymentReleases extends Enum {
     readonly isV1Ancient: boolean;
     readonly isV2: boolean;
     readonly type: 'V1Ancient' | 'V2';
   }
 
-  /** @name PalletTreasuryProposal (186) */
+  /** @name PalletTreasuryProposal (210) */
   interface PalletTreasuryProposal extends Struct {
     readonly proposer: AccountId32;
     readonly value: u128;
@@ -1803,7 +1983,7 @@
     readonly bond: u128;
   }
 
-  /** @name PalletTreasuryCall (189) */
+  /** @name PalletTreasuryCall (212) */
   interface PalletTreasuryCall extends Enum {
     readonly isProposeSpend: boolean;
     readonly asProposeSpend: {
@@ -1830,10 +2010,10 @@
     readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval';
   }
 
-  /** @name FrameSupportPalletId (192) */
+  /** @name FrameSupportPalletId (215) */
   interface FrameSupportPalletId extends U8aFixed {}
 
-  /** @name PalletTreasuryError (193) */
+  /** @name PalletTreasuryError (216) */
   interface PalletTreasuryError extends Enum {
     readonly isInsufficientProposersBalance: boolean;
     readonly isInvalidIndex: boolean;
@@ -1843,7 +2023,7 @@
     readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved';
   }
 
-  /** @name PalletSudoCall (194) */
+  /** @name PalletSudoCall (217) */
   interface PalletSudoCall extends Enum {
     readonly isSudo: boolean;
     readonly asSudo: {
@@ -1866,7 +2046,7 @@
     readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs';
   }
 
-  /** @name OrmlVestingModuleCall (196) */
+  /** @name OrmlVestingModuleCall (219) */
   interface OrmlVestingModuleCall extends Enum {
     readonly isClaim: boolean;
     readonly isVestedTransfer: boolean;
@@ -1886,7 +2066,7 @@
     readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor';
   }
 
-  /** @name OrmlXtokensModuleCall (198) */
+  /** @name OrmlXtokensModuleCall (221) */
   interface OrmlXtokensModuleCall extends Enum {
     readonly isTransfer: boolean;
     readonly asTransfer: {
@@ -1933,7 +2113,7 @@
     readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets';
   }
 
-  /** @name XcmVersionedMultiAsset (199) */
+  /** @name XcmVersionedMultiAsset (222) */
   interface XcmVersionedMultiAsset extends Enum {
     readonly isV0: boolean;
     readonly asV0: XcmV0MultiAsset;
@@ -1942,7 +2122,7 @@
     readonly type: 'V0' | 'V1';
   }
 
-  /** @name OrmlTokensModuleCall (202) */
+  /** @name OrmlTokensModuleCall (225) */
   interface OrmlTokensModuleCall extends Enum {
     readonly isTransfer: boolean;
     readonly asTransfer: {
@@ -1979,7 +2159,7 @@
     readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance';
   }
 
-  /** @name CumulusPalletXcmpQueueCall (203) */
+  /** @name CumulusPalletXcmpQueueCall (226) */
   interface CumulusPalletXcmpQueueCall extends Enum {
     readonly isServiceOverweight: boolean;
     readonly asServiceOverweight: {
@@ -2015,7 +2195,7 @@
     readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight';
   }
 
-  /** @name PalletXcmCall (204) */
+  /** @name PalletXcmCall (227) */
   interface PalletXcmCall extends Enum {
     readonly isSend: boolean;
     readonly asSend: {
@@ -2077,7 +2257,7 @@
     readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets';
   }
 
-  /** @name XcmVersionedXcm (205) */
+  /** @name XcmVersionedXcm (228) */
   interface XcmVersionedXcm extends Enum {
     readonly isV0: boolean;
     readonly asV0: XcmV0Xcm;
@@ -2088,7 +2268,7 @@
     readonly type: 'V0' | 'V1' | 'V2';
   }
 
-  /** @name XcmV0Xcm (206) */
+  /** @name XcmV0Xcm (229) */
   interface XcmV0Xcm extends Enum {
     readonly isWithdrawAsset: boolean;
     readonly asWithdrawAsset: {
@@ -2151,7 +2331,7 @@
     readonly type: 'WithdrawAsset' | 'ReserveAssetDeposit' | 'TeleportAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom';
   }
 
-  /** @name XcmV0Order (208) */
+  /** @name XcmV0Order (231) */
   interface XcmV0Order extends Enum {
     readonly isNull: boolean;
     readonly isDepositAsset: boolean;
@@ -2199,14 +2379,14 @@
     readonly type: 'Null' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution';
   }
 
-  /** @name XcmV0Response (210) */
+  /** @name XcmV0Response (233) */
   interface XcmV0Response extends Enum {
     readonly isAssets: boolean;
     readonly asAssets: Vec<XcmV0MultiAsset>;
     readonly type: 'Assets';
   }
 
-  /** @name XcmV1Xcm (211) */
+  /** @name XcmV1Xcm (234) */
   interface XcmV1Xcm extends Enum {
     readonly isWithdrawAsset: boolean;
     readonly asWithdrawAsset: {
@@ -2275,7 +2455,7 @@
     readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom' | 'SubscribeVersion' | 'UnsubscribeVersion';
   }
 
-  /** @name XcmV1Order (213) */
+  /** @name XcmV1Order (236) */
   interface XcmV1Order extends Enum {
     readonly isNoop: boolean;
     readonly isDepositAsset: boolean;
@@ -2325,7 +2505,7 @@
     readonly type: 'Noop' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution';
   }
 
-  /** @name XcmV1Response (215) */
+  /** @name XcmV1Response (238) */
   interface XcmV1Response extends Enum {
     readonly isAssets: boolean;
     readonly asAssets: XcmV1MultiassetMultiAssets;
@@ -2334,10 +2514,10 @@
     readonly type: 'Assets' | 'Version';
   }
 
-  /** @name CumulusPalletXcmCall (229) */
+  /** @name CumulusPalletXcmCall (252) */
   type CumulusPalletXcmCall = Null;
 
-  /** @name CumulusPalletDmpQueueCall (230) */
+  /** @name CumulusPalletDmpQueueCall (253) */
   interface CumulusPalletDmpQueueCall extends Enum {
     readonly isServiceOverweight: boolean;
     readonly asServiceOverweight: {
@@ -2347,7 +2527,7 @@
     readonly type: 'ServiceOverweight';
   }
 
-  /** @name PalletInflationCall (231) */
+  /** @name PalletInflationCall (254) */
   interface PalletInflationCall extends Enum {
     readonly isStartInflation: boolean;
     readonly asStartInflation: {
@@ -2356,7 +2536,7 @@
     readonly type: 'StartInflation';
   }
 
-  /** @name PalletUniqueCall (232) */
+  /** @name PalletUniqueCall (255) */
   interface PalletUniqueCall extends Enum {
     readonly isCreateCollection: boolean;
     readonly asCreateCollection: {
@@ -2517,15 +2697,19 @@
       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) */
+  /** @name UpDataStructsCollectionMode (260) */
   interface UpDataStructsCollectionMode extends Enum {
     readonly isNft: boolean;
     readonly isFungible: boolean;
@@ -2534,7 +2718,7 @@
     readonly type: 'Nft' | 'Fungible' | 'ReFungible';
   }
 
-  /** @name UpDataStructsCreateCollectionData (238) */
+  /** @name UpDataStructsCreateCollectionData (261) */
   interface UpDataStructsCreateCollectionData extends Struct {
     readonly mode: UpDataStructsCollectionMode;
     readonly access: Option<UpDataStructsAccessMode>;
@@ -2548,14 +2732,14 @@
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsAccessMode (240) */
+  /** @name UpDataStructsAccessMode (263) */
   interface UpDataStructsAccessMode extends Enum {
     readonly isNormal: boolean;
     readonly isAllowList: boolean;
     readonly type: 'Normal' | 'AllowList';
   }
 
-  /** @name UpDataStructsCollectionLimits (242) */
+  /** @name UpDataStructsCollectionLimits (265) */
   interface UpDataStructsCollectionLimits extends Struct {
     readonly accountTokenOwnershipLimit: Option<u32>;
     readonly sponsoredDataSize: Option<u32>;
@@ -2568,7 +2752,7 @@
     readonly transfersEnabled: Option<bool>;
   }
 
-  /** @name UpDataStructsSponsoringRateLimit (244) */
+  /** @name UpDataStructsSponsoringRateLimit (267) */
   interface UpDataStructsSponsoringRateLimit extends Enum {
     readonly isSponsoringDisabled: boolean;
     readonly isBlocks: boolean;
@@ -2576,43 +2760,43 @@
     readonly type: 'SponsoringDisabled' | 'Blocks';
   }
 
-  /** @name UpDataStructsCollectionPermissions (247) */
+  /** @name UpDataStructsCollectionPermissions (270) */
   interface UpDataStructsCollectionPermissions extends Struct {
     readonly access: Option<UpDataStructsAccessMode>;
     readonly mintMode: Option<bool>;
     readonly nesting: Option<UpDataStructsNestingPermissions>;
   }
 
-  /** @name UpDataStructsNestingPermissions (249) */
+  /** @name UpDataStructsNestingPermissions (272) */
   interface UpDataStructsNestingPermissions extends Struct {
     readonly tokenOwner: bool;
     readonly collectionAdmin: bool;
     readonly restricted: Option<UpDataStructsOwnerRestrictedSet>;
   }
 
-  /** @name UpDataStructsOwnerRestrictedSet (251) */
+  /** @name UpDataStructsOwnerRestrictedSet (274) */
   interface UpDataStructsOwnerRestrictedSet extends BTreeSet<u32> {}
 
-  /** @name UpDataStructsPropertyKeyPermission (256) */
+  /** @name UpDataStructsPropertyKeyPermission (279) */
   interface UpDataStructsPropertyKeyPermission extends Struct {
     readonly key: Bytes;
     readonly permission: UpDataStructsPropertyPermission;
   }
 
-  /** @name UpDataStructsPropertyPermission (257) */
+  /** @name UpDataStructsPropertyPermission (280) */
   interface UpDataStructsPropertyPermission extends Struct {
     readonly mutable: bool;
     readonly collectionAdmin: bool;
     readonly tokenOwner: bool;
   }
 
-  /** @name UpDataStructsProperty (260) */
+  /** @name UpDataStructsProperty (283) */
   interface UpDataStructsProperty extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name UpDataStructsCreateItemData (263) */
+  /** @name UpDataStructsCreateItemData (286) */
   interface UpDataStructsCreateItemData extends Enum {
     readonly isNft: boolean;
     readonly asNft: UpDataStructsCreateNftData;
@@ -2623,23 +2807,23 @@
     readonly type: 'Nft' | 'Fungible' | 'ReFungible';
   }
 
-  /** @name UpDataStructsCreateNftData (264) */
+  /** @name UpDataStructsCreateNftData (287) */
   interface UpDataStructsCreateNftData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsCreateFungibleData (265) */
+  /** @name UpDataStructsCreateFungibleData (288) */
   interface UpDataStructsCreateFungibleData extends Struct {
     readonly value: u128;
   }
 
-  /** @name UpDataStructsCreateReFungibleData (266) */
+  /** @name UpDataStructsCreateReFungibleData (289) */
   interface UpDataStructsCreateReFungibleData extends Struct {
     readonly pieces: u128;
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsCreateItemExData (269) */
+  /** @name UpDataStructsCreateItemExData (292) */
   interface UpDataStructsCreateItemExData extends Enum {
     readonly isNft: boolean;
     readonly asNft: Vec<UpDataStructsCreateNftExData>;
@@ -2652,26 +2836,26 @@
     readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
   }
 
-  /** @name UpDataStructsCreateNftExData (271) */
+  /** @name UpDataStructsCreateNftExData (294) */
   interface UpDataStructsCreateNftExData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
     readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
   }
 
-  /** @name UpDataStructsCreateRefungibleExSingleOwner (278) */
+  /** @name UpDataStructsCreateRefungibleExSingleOwner (301) */
   interface UpDataStructsCreateRefungibleExSingleOwner extends Struct {
     readonly user: PalletEvmAccountBasicCrossAccountIdRepr;
     readonly pieces: u128;
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsCreateRefungibleExMultipleOwners (280) */
+  /** @name UpDataStructsCreateRefungibleExMultipleOwners (303) */
   interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct {
     readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name PalletConfigurationCall (281) */
+  /** @name PalletConfigurationCall (304) */
   interface PalletConfigurationCall extends Enum {
     readonly isSetWeightToFeeCoefficientOverride: boolean;
     readonly asSetWeightToFeeCoefficientOverride: {
@@ -2692,7 +2876,7 @@
     readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetXcmAllowedLocations' | 'SetAppPromotionConfigurationOverride';
   }
 
-  /** @name PalletConfigurationAppPromotionConfiguration (286) */
+  /** @name PalletConfigurationAppPromotionConfiguration (309) */
   interface PalletConfigurationAppPromotionConfiguration extends Struct {
     readonly recalculationInterval: Option<u32>;
     readonly pendingInterval: Option<u32>;
@@ -2700,13 +2884,13 @@
     readonly maxStakersPerCalculation: Option<u8>;
   }
 
-  /** @name PalletTemplateTransactionPaymentCall (289) */
+  /** @name PalletTemplateTransactionPaymentCall (312) */
   type PalletTemplateTransactionPaymentCall = Null;
 
-  /** @name PalletStructureCall (290) */
+  /** @name PalletStructureCall (313) */
   type PalletStructureCall = Null;
 
-  /** @name PalletRmrkCoreCall (291) */
+  /** @name PalletRmrkCoreCall (314) */
   interface PalletRmrkCoreCall extends Enum {
     readonly isCreateCollection: boolean;
     readonly asCreateCollection: {
@@ -2812,7 +2996,7 @@
     readonly type: 'CreateCollection' | 'DestroyCollection' | 'ChangeCollectionIssuer' | 'LockCollection' | 'MintNft' | 'BurnNft' | 'Send' | 'AcceptNft' | 'RejectNft' | 'AcceptResource' | 'AcceptResourceRemoval' | 'SetProperty' | 'SetPriority' | 'AddBasicResource' | 'AddComposableResource' | 'AddSlotResource' | 'RemoveResource';
   }
 
-  /** @name RmrkTraitsResourceResourceTypes (297) */
+  /** @name RmrkTraitsResourceResourceTypes (320) */
   interface RmrkTraitsResourceResourceTypes extends Enum {
     readonly isBasic: boolean;
     readonly asBasic: RmrkTraitsResourceBasicResource;
@@ -2823,7 +3007,7 @@
     readonly type: 'Basic' | 'Composable' | 'Slot';
   }
 
-  /** @name RmrkTraitsResourceBasicResource (299) */
+  /** @name RmrkTraitsResourceBasicResource (322) */
   interface RmrkTraitsResourceBasicResource extends Struct {
     readonly src: Option<Bytes>;
     readonly metadata: Option<Bytes>;
@@ -2831,7 +3015,7 @@
     readonly thumb: Option<Bytes>;
   }
 
-  /** @name RmrkTraitsResourceComposableResource (301) */
+  /** @name RmrkTraitsResourceComposableResource (324) */
   interface RmrkTraitsResourceComposableResource extends Struct {
     readonly parts: Vec<u32>;
     readonly base: u32;
@@ -2841,7 +3025,7 @@
     readonly thumb: Option<Bytes>;
   }
 
-  /** @name RmrkTraitsResourceSlotResource (302) */
+  /** @name RmrkTraitsResourceSlotResource (325) */
   interface RmrkTraitsResourceSlotResource extends Struct {
     readonly base: u32;
     readonly src: Option<Bytes>;
@@ -2851,7 +3035,7 @@
     readonly thumb: Option<Bytes>;
   }
 
-  /** @name PalletRmrkEquipCall (305) */
+  /** @name PalletRmrkEquipCall (328) */
   interface PalletRmrkEquipCall extends Enum {
     readonly isCreateBase: boolean;
     readonly asCreateBase: {
@@ -2873,7 +3057,7 @@
     readonly type: 'CreateBase' | 'ThemeAdd' | 'Equippable';
   }
 
-  /** @name RmrkTraitsPartPartType (308) */
+  /** @name RmrkTraitsPartPartType (331) */
   interface RmrkTraitsPartPartType extends Enum {
     readonly isFixedPart: boolean;
     readonly asFixedPart: RmrkTraitsPartFixedPart;
@@ -2882,14 +3066,14 @@
     readonly type: 'FixedPart' | 'SlotPart';
   }
 
-  /** @name RmrkTraitsPartFixedPart (310) */
+  /** @name RmrkTraitsPartFixedPart (333) */
   interface RmrkTraitsPartFixedPart extends Struct {
     readonly id: u32;
     readonly z: u32;
     readonly src: Bytes;
   }
 
-  /** @name RmrkTraitsPartSlotPart (311) */
+  /** @name RmrkTraitsPartSlotPart (334) */
   interface RmrkTraitsPartSlotPart extends Struct {
     readonly id: u32;
     readonly equippable: RmrkTraitsPartEquippableList;
@@ -2897,7 +3081,7 @@
     readonly z: u32;
   }
 
-  /** @name RmrkTraitsPartEquippableList (312) */
+  /** @name RmrkTraitsPartEquippableList (335) */
   interface RmrkTraitsPartEquippableList extends Enum {
     readonly isAll: boolean;
     readonly isEmpty: boolean;
@@ -2906,20 +3090,20 @@
     readonly type: 'All' | 'Empty' | 'Custom';
   }
 
-  /** @name RmrkTraitsTheme (314) */
+  /** @name RmrkTraitsTheme (337) */
   interface RmrkTraitsTheme extends Struct {
     readonly name: Bytes;
     readonly properties: Vec<RmrkTraitsThemeThemeProperty>;
     readonly inherit: bool;
   }
 
-  /** @name RmrkTraitsThemeThemeProperty (316) */
+  /** @name RmrkTraitsThemeThemeProperty (339) */
   interface RmrkTraitsThemeThemeProperty extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name PalletAppPromotionCall (318) */
+  /** @name PalletAppPromotionCall (341) */
   interface PalletAppPromotionCall extends Enum {
     readonly isSetAdminAddress: boolean;
     readonly asSetAdminAddress: {
@@ -2953,7 +3137,7 @@
     readonly type: 'SetAdminAddress' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers';
   }
 
-  /** @name PalletForeignAssetsModuleCall (319) */
+  /** @name PalletForeignAssetsModuleCall (342) */
   interface PalletForeignAssetsModuleCall extends Enum {
     readonly isRegisterForeignAsset: boolean;
     readonly asRegisterForeignAsset: {
@@ -2970,7 +3154,7 @@
     readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset';
   }
 
-  /** @name PalletEvmCall (320) */
+  /** @name PalletEvmCall (343) */
   interface PalletEvmCall extends Enum {
     readonly isWithdraw: boolean;
     readonly asWithdraw: {
@@ -3015,7 +3199,7 @@
     readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
   }
 
-  /** @name PalletEthereumCall (326) */
+  /** @name PalletEthereumCall (349) */
   interface PalletEthereumCall extends Enum {
     readonly isTransact: boolean;
     readonly asTransact: {
@@ -3024,7 +3208,7 @@
     readonly type: 'Transact';
   }
 
-  /** @name EthereumTransactionTransactionV2 (327) */
+  /** @name EthereumTransactionTransactionV2 (350) */
   interface EthereumTransactionTransactionV2 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumTransactionLegacyTransaction;
@@ -3035,7 +3219,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumTransactionLegacyTransaction (328) */
+  /** @name EthereumTransactionLegacyTransaction (351) */
   interface EthereumTransactionLegacyTransaction extends Struct {
     readonly nonce: U256;
     readonly gasPrice: U256;
@@ -3046,7 +3230,7 @@
     readonly signature: EthereumTransactionTransactionSignature;
   }
 
-  /** @name EthereumTransactionTransactionAction (329) */
+  /** @name EthereumTransactionTransactionAction (352) */
   interface EthereumTransactionTransactionAction extends Enum {
     readonly isCall: boolean;
     readonly asCall: H160;
@@ -3054,14 +3238,14 @@
     readonly type: 'Call' | 'Create';
   }
 
-  /** @name EthereumTransactionTransactionSignature (330) */
+  /** @name EthereumTransactionTransactionSignature (353) */
   interface EthereumTransactionTransactionSignature extends Struct {
     readonly v: u64;
     readonly r: H256;
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionEip2930Transaction (332) */
+  /** @name EthereumTransactionEip2930Transaction (355) */
   interface EthereumTransactionEip2930Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -3076,13 +3260,13 @@
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionAccessListItem (334) */
+  /** @name EthereumTransactionAccessListItem (357) */
   interface EthereumTransactionAccessListItem extends Struct {
     readonly address: H160;
     readonly storageKeys: Vec<H256>;
   }
 
-  /** @name EthereumTransactionEip1559Transaction (335) */
+  /** @name EthereumTransactionEip1559Transaction (358) */
   interface EthereumTransactionEip1559Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -3098,7 +3282,7 @@
     readonly s: H256;
   }
 
-  /** @name PalletEvmMigrationCall (336) */
+  /** @name PalletEvmMigrationCall (359) */
   interface PalletEvmMigrationCall extends Enum {
     readonly isBegin: boolean;
     readonly asBegin: {
@@ -3125,14 +3309,14 @@
     readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents';
   }
 
-  /** @name PalletMaintenanceCall (340) */
+  /** @name PalletMaintenanceCall (363) */
   interface PalletMaintenanceCall extends Enum {
     readonly isEnable: boolean;
     readonly isDisable: boolean;
     readonly type: 'Enable' | 'Disable';
   }
 
-  /** @name PalletTestUtilsCall (341) */
+  /** @name PalletTestUtilsCall (364) */
   interface PalletTestUtilsCall extends Enum {
     readonly isEnable: boolean;
     readonly isSetTestValue: boolean;
@@ -3152,13 +3336,13 @@
     readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll';
   }
 
-  /** @name PalletSudoError (343) */
+  /** @name PalletSudoError (366) */
   interface PalletSudoError extends Enum {
     readonly isRequireSudo: boolean;
     readonly type: 'RequireSudo';
   }
 
-  /** @name OrmlVestingModuleError (345) */
+  /** @name OrmlVestingModuleError (368) */
   interface OrmlVestingModuleError extends Enum {
     readonly isZeroVestingPeriod: boolean;
     readonly isZeroVestingPeriodCount: boolean;
@@ -3169,7 +3353,7 @@
     readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
   }
 
-  /** @name OrmlXtokensModuleError (346) */
+  /** @name OrmlXtokensModuleError (369) */
   interface OrmlXtokensModuleError extends Enum {
     readonly isAssetHasNoReserve: boolean;
     readonly isNotCrossChainTransfer: boolean;
@@ -3193,26 +3377,26 @@
     readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined';
   }
 
-  /** @name OrmlTokensBalanceLock (349) */
+  /** @name OrmlTokensBalanceLock (372) */
   interface OrmlTokensBalanceLock extends Struct {
     readonly id: U8aFixed;
     readonly amount: u128;
   }
 
-  /** @name OrmlTokensAccountData (351) */
+  /** @name OrmlTokensAccountData (374) */
   interface OrmlTokensAccountData extends Struct {
     readonly free: u128;
     readonly reserved: u128;
     readonly frozen: u128;
   }
 
-  /** @name OrmlTokensReserveData (353) */
+  /** @name OrmlTokensReserveData (376) */
   interface OrmlTokensReserveData extends Struct {
     readonly id: Null;
     readonly amount: u128;
   }
 
-  /** @name OrmlTokensModuleError (355) */
+  /** @name OrmlTokensModuleError (378) */
   interface OrmlTokensModuleError extends Enum {
     readonly isBalanceTooLow: boolean;
     readonly isAmountIntoBalanceFailed: boolean;
@@ -3225,21 +3409,21 @@
     readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves';
   }
 
-  /** @name CumulusPalletXcmpQueueInboundChannelDetails (357) */
+  /** @name CumulusPalletXcmpQueueInboundChannelDetails (380) */
   interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
     readonly sender: u32;
     readonly state: CumulusPalletXcmpQueueInboundState;
     readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesXcmpMessageFormat]>>;
   }
 
-  /** @name CumulusPalletXcmpQueueInboundState (358) */
+  /** @name CumulusPalletXcmpQueueInboundState (381) */
   interface CumulusPalletXcmpQueueInboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (361) */
+  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (384) */
   interface PolkadotParachainPrimitivesXcmpMessageFormat extends Enum {
     readonly isConcatenatedVersionedXcm: boolean;
     readonly isConcatenatedEncodedBlob: boolean;
@@ -3247,7 +3431,7 @@
     readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (364) */
+  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (387) */
   interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
     readonly recipient: u32;
     readonly state: CumulusPalletXcmpQueueOutboundState;
@@ -3256,14 +3440,14 @@
     readonly lastIndex: u16;
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundState (365) */
+  /** @name CumulusPalletXcmpQueueOutboundState (388) */
   interface CumulusPalletXcmpQueueOutboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name CumulusPalletXcmpQueueQueueConfigData (367) */
+  /** @name CumulusPalletXcmpQueueQueueConfigData (390) */
   interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
     readonly suspendThreshold: u32;
     readonly dropThreshold: u32;
@@ -3273,7 +3457,7 @@
     readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight;
   }
 
-  /** @name CumulusPalletXcmpQueueError (369) */
+  /** @name CumulusPalletXcmpQueueError (392) */
   interface CumulusPalletXcmpQueueError extends Enum {
     readonly isFailedToSend: boolean;
     readonly isBadXcmOrigin: boolean;
@@ -3283,7 +3467,7 @@
     readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
   }
 
-  /** @name PalletXcmError (370) */
+  /** @name PalletXcmError (393) */
   interface PalletXcmError extends Enum {
     readonly isUnreachable: boolean;
     readonly isSendFailure: boolean;
@@ -3301,29 +3485,29 @@
     readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed';
   }
 
-  /** @name CumulusPalletXcmError (371) */
+  /** @name CumulusPalletXcmError (394) */
   type CumulusPalletXcmError = Null;
 
-  /** @name CumulusPalletDmpQueueConfigData (372) */
+  /** @name CumulusPalletDmpQueueConfigData (395) */
   interface CumulusPalletDmpQueueConfigData extends Struct {
     readonly maxIndividual: SpWeightsWeightV2Weight;
   }
 
-  /** @name CumulusPalletDmpQueuePageIndexData (373) */
+  /** @name CumulusPalletDmpQueuePageIndexData (396) */
   interface CumulusPalletDmpQueuePageIndexData extends Struct {
     readonly beginUsed: u32;
     readonly endUsed: u32;
     readonly overweightCount: u64;
   }
 
-  /** @name CumulusPalletDmpQueueError (376) */
+  /** @name CumulusPalletDmpQueueError (399) */
   interface CumulusPalletDmpQueueError extends Enum {
     readonly isUnknown: boolean;
     readonly isOverLimit: boolean;
     readonly type: 'Unknown' | 'OverLimit';
   }
 
-  /** @name PalletUniqueError (380) */
+  /** @name PalletUniqueError (403) */
   interface PalletUniqueError extends Enum {
     readonly isCollectionDecimalPointLimitExceeded: boolean;
     readonly isEmptyArgument: boolean;
@@ -3331,13 +3515,13 @@
     readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
   }
 
-  /** @name PalletConfigurationError (381) */
+  /** @name PalletConfigurationError (404) */
   interface PalletConfigurationError extends Enum {
     readonly isInconsistentConfiguration: boolean;
     readonly type: 'InconsistentConfiguration';
   }
 
-  /** @name UpDataStructsCollection (382) */
+  /** @name UpDataStructsCollection (405) */
   interface UpDataStructsCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -3350,7 +3534,7 @@
     readonly flags: U8aFixed;
   }
 
-  /** @name UpDataStructsSponsorshipStateAccountId32 (383) */
+  /** @name UpDataStructsSponsorshipStateAccountId32 (406) */
   interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -3360,43 +3544,43 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name UpDataStructsProperties (385) */
+  /** @name UpDataStructsProperties (408) */
   interface UpDataStructsProperties extends Struct {
     readonly map: UpDataStructsPropertiesMapBoundedVec;
     readonly consumedSpace: u32;
     readonly spaceLimit: u32;
   }
 
-  /** @name UpDataStructsPropertiesMapBoundedVec (386) */
+  /** @name UpDataStructsPropertiesMapBoundedVec (409) */
   interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
 
-  /** @name UpDataStructsPropertiesMapPropertyPermission (391) */
+  /** @name UpDataStructsPropertiesMapPropertyPermission (414) */
   interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
 
-  /** @name UpDataStructsCollectionStats (398) */
+  /** @name UpDataStructsCollectionStats (421) */
   interface UpDataStructsCollectionStats extends Struct {
     readonly created: u32;
     readonly destroyed: u32;
     readonly alive: u32;
   }
 
-  /** @name UpDataStructsTokenChild (399) */
+  /** @name UpDataStructsTokenChild (422) */
   interface UpDataStructsTokenChild extends Struct {
     readonly token: u32;
     readonly collection: u32;
   }
 
-  /** @name PhantomTypeUpDataStructs (400) */
+  /** @name PhantomTypeUpDataStructs (423) */
   interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftNftInfo, RmrkTraitsResourceResourceInfo, RmrkTraitsPropertyPropertyInfo, RmrkTraitsBaseBaseInfo, RmrkTraitsPartPartType, RmrkTraitsTheme, RmrkTraitsNftNftChild]>> {}
 
-  /** @name UpDataStructsTokenData (402) */
+  /** @name UpDataStructsTokenData (425) */
   interface UpDataStructsTokenData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
     readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
     readonly pieces: u128;
   }
 
-  /** @name UpDataStructsRpcCollection (404) */
+  /** @name UpDataStructsRpcCollection (427) */
   interface UpDataStructsRpcCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -3412,13 +3596,13 @@
     readonly flags: UpDataStructsRpcCollectionFlags;
   }
 
-  /** @name UpDataStructsRpcCollectionFlags (405) */
+  /** @name UpDataStructsRpcCollectionFlags (428) */
   interface UpDataStructsRpcCollectionFlags extends Struct {
     readonly foreign: bool;
     readonly erc721metadata: bool;
   }
 
-  /** @name RmrkTraitsCollectionCollectionInfo (406) */
+  /** @name RmrkTraitsCollectionCollectionInfo (429) */
   interface RmrkTraitsCollectionCollectionInfo extends Struct {
     readonly issuer: AccountId32;
     readonly metadata: Bytes;
@@ -3427,7 +3611,7 @@
     readonly nftsCount: u32;
   }
 
-  /** @name RmrkTraitsNftNftInfo (407) */
+  /** @name RmrkTraitsNftNftInfo (430) */
   interface RmrkTraitsNftNftInfo extends Struct {
     readonly owner: RmrkTraitsNftAccountIdOrCollectionNftTuple;
     readonly royalty: Option<RmrkTraitsNftRoyaltyInfo>;
@@ -3436,13 +3620,13 @@
     readonly pending: bool;
   }
 
-  /** @name RmrkTraitsNftRoyaltyInfo (409) */
+  /** @name RmrkTraitsNftRoyaltyInfo (432) */
   interface RmrkTraitsNftRoyaltyInfo extends Struct {
     readonly recipient: AccountId32;
     readonly amount: Permill;
   }
 
-  /** @name RmrkTraitsResourceResourceInfo (410) */
+  /** @name RmrkTraitsResourceResourceInfo (433) */
   interface RmrkTraitsResourceResourceInfo extends Struct {
     readonly id: u32;
     readonly resource: RmrkTraitsResourceResourceTypes;
@@ -3450,26 +3634,26 @@
     readonly pendingRemoval: bool;
   }
 
-  /** @name RmrkTraitsPropertyPropertyInfo (411) */
+  /** @name RmrkTraitsPropertyPropertyInfo (434) */
   interface RmrkTraitsPropertyPropertyInfo extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name RmrkTraitsBaseBaseInfo (412) */
+  /** @name RmrkTraitsBaseBaseInfo (435) */
   interface RmrkTraitsBaseBaseInfo extends Struct {
     readonly issuer: AccountId32;
     readonly baseType: Bytes;
     readonly symbol: Bytes;
   }
 
-  /** @name RmrkTraitsNftNftChild (413) */
+  /** @name RmrkTraitsNftNftChild (436) */
   interface RmrkTraitsNftNftChild extends Struct {
     readonly collectionId: u32;
     readonly nftId: u32;
   }
 
-  /** @name PalletCommonError (415) */
+  /** @name PalletCommonError (438) */
   interface PalletCommonError extends Enum {
     readonly isCollectionNotFound: boolean;
     readonly isMustBeTokenOwner: boolean;
@@ -3510,7 +3694,7 @@
     readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin';
   }
 
-  /** @name PalletFungibleError (417) */
+  /** @name PalletFungibleError (440) */
   interface PalletFungibleError extends Enum {
     readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isFungibleItemsHaveNoId: boolean;
@@ -3522,12 +3706,12 @@
     readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid';
   }
 
-  /** @name PalletRefungibleItemData (418) */
+  /** @name PalletRefungibleItemData (441) */
   interface PalletRefungibleItemData extends Struct {
     readonly constData: Bytes;
   }
 
-  /** @name PalletRefungibleError (423) */
+  /** @name PalletRefungibleError (446) */
   interface PalletRefungibleError extends Enum {
     readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isWrongRefungiblePieces: boolean;
@@ -3537,19 +3721,19 @@
     readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
   }
 
-  /** @name PalletNonfungibleItemData (424) */
+  /** @name PalletNonfungibleItemData (447) */
   interface PalletNonfungibleItemData extends Struct {
     readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
   }
 
-  /** @name UpDataStructsPropertyScope (426) */
+  /** @name UpDataStructsPropertyScope (449) */
   interface UpDataStructsPropertyScope extends Enum {
     readonly isNone: boolean;
     readonly isRmrk: boolean;
     readonly type: 'None' | 'Rmrk';
   }
 
-  /** @name PalletNonfungibleError (428) */
+  /** @name PalletNonfungibleError (451) */
   interface PalletNonfungibleError extends Enum {
     readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isNonfungibleItemsHaveNoAmount: boolean;
@@ -3557,7 +3741,7 @@
     readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
   }
 
-  /** @name PalletStructureError (429) */
+  /** @name PalletStructureError (452) */
   interface PalletStructureError extends Enum {
     readonly isOuroborosDetected: boolean;
     readonly isDepthLimit: boolean;
@@ -3566,7 +3750,7 @@
     readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound';
   }
 
-  /** @name PalletRmrkCoreError (430) */
+  /** @name PalletRmrkCoreError (453) */
   interface PalletRmrkCoreError extends Enum {
     readonly isCorruptedCollectionType: boolean;
     readonly isRmrkPropertyKeyIsTooLong: boolean;
@@ -3590,7 +3774,7 @@
     readonly type: 'CorruptedCollectionType' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId';
   }
 
-  /** @name PalletRmrkEquipError (432) */
+  /** @name PalletRmrkEquipError (455) */
   interface PalletRmrkEquipError extends Enum {
     readonly isPermissionError: boolean;
     readonly isNoAvailableBaseId: boolean;
@@ -3602,7 +3786,7 @@
     readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart';
   }
 
-  /** @name PalletAppPromotionError (438) */
+  /** @name PalletAppPromotionError (461) */
   interface PalletAppPromotionError extends Enum {
     readonly isAdminNotSet: boolean;
     readonly isNoPermission: boolean;
@@ -3613,7 +3797,7 @@
     readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'IncorrectLockedBalanceOperation';
   }
 
-  /** @name PalletForeignAssetsModuleError (439) */
+  /** @name PalletForeignAssetsModuleError (462) */
   interface PalletForeignAssetsModuleError extends Enum {
     readonly isBadLocation: boolean;
     readonly isMultiLocationExisted: boolean;
@@ -3622,7 +3806,7 @@
     readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted';
   }
 
-  /** @name PalletEvmError (441) */
+  /** @name PalletEvmError (464) */
   interface PalletEvmError extends Enum {
     readonly isBalanceLow: boolean;
     readonly isFeeOverflow: boolean;
@@ -3637,7 +3821,7 @@
     readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy';
   }
 
-  /** @name FpRpcTransactionStatus (444) */
+  /** @name FpRpcTransactionStatus (467) */
   interface FpRpcTransactionStatus extends Struct {
     readonly transactionHash: H256;
     readonly transactionIndex: u32;
@@ -3648,10 +3832,10 @@
     readonly logsBloom: EthbloomBloom;
   }
 
-  /** @name EthbloomBloom (446) */
+  /** @name EthbloomBloom (469) */
   interface EthbloomBloom extends U8aFixed {}
 
-  /** @name EthereumReceiptReceiptV3 (448) */
+  /** @name EthereumReceiptReceiptV3 (471) */
   interface EthereumReceiptReceiptV3 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumReceiptEip658ReceiptData;
@@ -3662,7 +3846,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumReceiptEip658ReceiptData (449) */
+  /** @name EthereumReceiptEip658ReceiptData (472) */
   interface EthereumReceiptEip658ReceiptData extends Struct {
     readonly statusCode: u8;
     readonly usedGas: U256;
@@ -3670,14 +3854,14 @@
     readonly logs: Vec<EthereumLog>;
   }
 
-  /** @name EthereumBlock (450) */
+  /** @name EthereumBlock (473) */
   interface EthereumBlock extends Struct {
     readonly header: EthereumHeader;
     readonly transactions: Vec<EthereumTransactionTransactionV2>;
     readonly ommers: Vec<EthereumHeader>;
   }
 
-  /** @name EthereumHeader (451) */
+  /** @name EthereumHeader (474) */
   interface EthereumHeader extends Struct {
     readonly parentHash: H256;
     readonly ommersHash: H256;
@@ -3696,24 +3880,24 @@
     readonly nonce: EthereumTypesHashH64;
   }
 
-  /** @name EthereumTypesHashH64 (452) */
+  /** @name EthereumTypesHashH64 (475) */
   interface EthereumTypesHashH64 extends U8aFixed {}
 
-  /** @name PalletEthereumError (457) */
+  /** @name PalletEthereumError (480) */
   interface PalletEthereumError extends Enum {
     readonly isInvalidSignature: boolean;
     readonly isPreLogExists: boolean;
     readonly type: 'InvalidSignature' | 'PreLogExists';
   }
 
-  /** @name PalletEvmCoderSubstrateError (458) */
+  /** @name PalletEvmCoderSubstrateError (481) */
   interface PalletEvmCoderSubstrateError extends Enum {
     readonly isOutOfGas: boolean;
     readonly isOutOfFund: boolean;
     readonly type: 'OutOfGas' | 'OutOfFund';
   }
 
-  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (459) */
+  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (482) */
   interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -3723,7 +3907,7 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name PalletEvmContractHelpersSponsoringModeT (460) */
+  /** @name PalletEvmContractHelpersSponsoringModeT (483) */
   interface PalletEvmContractHelpersSponsoringModeT extends Enum {
     readonly isDisabled: boolean;
     readonly isAllowlisted: boolean;
@@ -3731,7 +3915,7 @@
     readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
   }
 
-  /** @name PalletEvmContractHelpersError (466) */
+  /** @name PalletEvmContractHelpersError (489) */
   interface PalletEvmContractHelpersError extends Enum {
     readonly isNoPermission: boolean;
     readonly isNoPendingSponsor: boolean;
@@ -3739,7 +3923,7 @@
     readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit';
   }
 
-  /** @name PalletEvmMigrationError (467) */
+  /** @name PalletEvmMigrationError (490) */
   interface PalletEvmMigrationError extends Enum {
     readonly isAccountNotEmpty: boolean;
     readonly isAccountIsNotMigrating: boolean;
@@ -3747,17 +3931,17 @@
     readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent';
   }
 
-  /** @name PalletMaintenanceError (468) */
+  /** @name PalletMaintenanceError (491) */
   type PalletMaintenanceError = Null;
 
-  /** @name PalletTestUtilsError (469) */
+  /** @name PalletTestUtilsError (492) */
   interface PalletTestUtilsError extends Enum {
     readonly isTestPalletDisabled: boolean;
     readonly isTriggerRollback: boolean;
     readonly type: 'TestPalletDisabled' | 'TriggerRollback';
   }
 
-  /** @name SpRuntimeMultiSignature (471) */
+  /** @name SpRuntimeMultiSignature (494) */
   interface SpRuntimeMultiSignature extends Enum {
     readonly isEd25519: boolean;
     readonly asEd25519: SpCoreEd25519Signature;
@@ -3768,40 +3952,40 @@
     readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
   }
 
-  /** @name SpCoreEd25519Signature (472) */
+  /** @name SpCoreEd25519Signature (495) */
   interface SpCoreEd25519Signature extends U8aFixed {}
 
-  /** @name SpCoreSr25519Signature (474) */
+  /** @name SpCoreSr25519Signature (497) */
   interface SpCoreSr25519Signature extends U8aFixed {}
 
-  /** @name SpCoreEcdsaSignature (475) */
+  /** @name SpCoreEcdsaSignature (498) */
   interface SpCoreEcdsaSignature extends U8aFixed {}
 
-  /** @name FrameSystemExtensionsCheckSpecVersion (478) */
+  /** @name FrameSystemExtensionsCheckSpecVersion (501) */
   type FrameSystemExtensionsCheckSpecVersion = Null;
 
-  /** @name FrameSystemExtensionsCheckTxVersion (479) */
+  /** @name FrameSystemExtensionsCheckTxVersion (502) */
   type FrameSystemExtensionsCheckTxVersion = Null;
 
-  /** @name FrameSystemExtensionsCheckGenesis (480) */
+  /** @name FrameSystemExtensionsCheckGenesis (503) */
   type FrameSystemExtensionsCheckGenesis = Null;
 
-  /** @name FrameSystemExtensionsCheckNonce (483) */
+  /** @name FrameSystemExtensionsCheckNonce (506) */
   interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
 
-  /** @name FrameSystemExtensionsCheckWeight (484) */
+  /** @name FrameSystemExtensionsCheckWeight (507) */
   type FrameSystemExtensionsCheckWeight = Null;
 
-  /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (485) */
+  /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (508) */
   type OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance = Null;
 
-  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (486) */
+  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (509) */
   interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
 
-  /** @name OpalRuntimeRuntime (487) */
+  /** @name OpalRuntimeRuntime (510) */
   type OpalRuntimeRuntime = Null;
 
-  /** @name PalletEthereumFakeTransactionFinalizer (488) */
+  /** @name PalletEthereumFakeTransactionFinalizer (511) */
   type PalletEthereumFakeTransactionFinalizer = Null;
 
 } // declare module
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -445,6 +445,30 @@
     return promise;
   }
 
+  /**
+   * Wait for the specified number of sessions to pass. 
+   * Only applicable if the Session pallet is turned on.
+   * @param sessionCount number of sessions to wait
+   * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks
+   * @returns 
+   */
+  async newSessions(sessionCount = 1, blockTimeout = 60000): Promise<void> {
+    console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.` 
+      + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
+
+    const expectedSessionIndex = await this.helper.session.getIndex() + sessionCount;
+    let currentSessionIndex = -1;
+
+    while (currentSessionIndex < expectedSessionIndex) {
+      // eslint-disable-next-line no-async-promise-executor
+      currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {
+        await this.newBlocks(1);
+        const res = this.helper.session.getIndex();
+        resolve(res);
+      }), blockTimeout, 'The chain has stopped producing blocks!');
+    }
+  }
+
   async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {
     timeout = timeout ?? 30 * 60 * 1000;
     // eslint-disable-next-line no-async-promise-executor
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
before · tests/src/util/playgrounds/unique.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable @typescript-eslint/no-var-requires */5/* eslint-disable function-call-argument-newline */6/* eslint-disable no-prototype-builtins */78import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {hexToU8a} from '@polkadot/util/hex';13import {u8aConcat} from '@polkadot/util/u8a';14import {BN} from '@polkadot/util/bn';15import {16  IApiListeners,17  IBlock,18  IEvent,19  IChainProperties,20  ICollectionCreationOptions,21  ICollectionLimits,22  ICollectionPermissions,23  ICrossAccountId,24  ICrossAccountIdLower,25  ILogger,26  INestingPermissions,27  IProperty,28  IStakingInfo,29  ISchedulerOptions,30  ISubstrateBalance,31  IToken,32  ITokenPropertyPermission,33  ITransactionResult,34  IUniqueHelperLog,35  TApiAllowedListeners,36  TEthereumAccount,37  TSigner,38  TSubstrateAccount,39  TNetworks,40  IForeignAssetMetadata,41  AcalaAssetMetadata,42  MoonbeamAssetInfo,43  DemocracyStandardAccountVote,44  IEthCrossAccountId,45} from './types';46import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';47import type {Vec} from '@polkadot/types-codec';48import {FrameSystemEventRecord} from '@polkadot/types/lookup';4950export class CrossAccountId implements ICrossAccountId {51  Substrate?: TSubstrateAccount;52  Ethereum?: TEthereumAccount;5354  constructor(account: ICrossAccountId) {55    if (account.Substrate) this.Substrate = account.Substrate;56    if (account.Ethereum) this.Ethereum = account.Ethereum;57  }5859  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {60    switch (domain) {61      case 'Substrate': return new CrossAccountId({Substrate: account.address});62      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();63    }64  }6566  static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {67    return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});68  }6970  static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {71    return encodeAddress(decodeAddress(address), ss58Format);72  }7374  static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {75    return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});76  }7778  withNormalizedSubstrate(ss58Format = 42): CrossAccountId {79    if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);80    return this;81  }8283  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {84    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));85  }8687  toEthereum(): CrossAccountId {88    if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});89    return this;90  }9192  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {93    return evmToAddress(address, ss58Format);94  }9596  toSubstrate(ss58Format?: number): CrossAccountId {97    if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});98    return this;99  }100101  toLowerCase(): CrossAccountId {102    if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();103    if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();104    return this;105  }106}107108const nesting = {109  toChecksumAddress(address: string): string {110    if (typeof address === 'undefined') return '';111112    if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);113114    address = address.toLowerCase().replace(/^0x/i,'');115    const addressHash = keccakAsHex(address).replace(/^0x/i,'');116    const checksumAddress = ['0x'];117118    for (let i = 0; i < address.length; i++) {119      // If ith character is 8 to f then make it uppercase120      if (parseInt(addressHash[i], 16) > 7) {121        checksumAddress.push(address[i].toUpperCase());122      } else {123        checksumAddress.push(address[i]);124      }125    }126    return checksumAddress.join('');127  },128  tokenIdToAddress(collectionId: number, tokenId: number) {129    return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);130  },131};132133class UniqueUtil {134  static transactionStatus = {135    NOT_READY: 'NotReady',136    FAIL: 'Fail',137    SUCCESS: 'Success',138  };139140  static chainLogType = {141    EXTRINSIC: 'extrinsic',142    RPC: 'rpc',143  };144145  static getTokenAccount(token: IToken): CrossAccountId {146    return new CrossAccountId({Ethereum: this.getTokenAddress(token)});147  }148149  static getTokenAddress(token: IToken): string {150    return nesting.tokenIdToAddress(token.collectionId, token.tokenId);151  }152153  static getDefaultLogger(): ILogger {154    return {155      log(msg: any, level = 'INFO') {156        console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));157      },158      level: {159        ERROR: 'ERROR',160        WARNING: 'WARNING',161        INFO: 'INFO',162      },163    };164  }165166  static vec2str(arr: string[] | number[]) {167    return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');168  }169170  static str2vec(string: string) {171    if (typeof string !== 'string') return string;172    return Array.from(string).map(x => x.charCodeAt(0));173  }174175  static fromSeed(seed: string, ss58Format = 42) {176    const keyring = new Keyring({type: 'sr25519', ss58Format});177    return keyring.addFromUri(seed);178  }179180  static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {181    if (creationResult.status !== this.transactionStatus.SUCCESS) {182      throw Error('Unable to create collection!');183    }184185    let collectionId = null;186    creationResult.result.events.forEach(({event: {data, method, section}}) => {187      if ((section === 'common') && (method === 'CollectionCreated')) {188        collectionId = parseInt(data[0].toString(), 10);189      }190    });191192    if (collectionId === null) {193      throw Error('No CollectionCreated event was found!');194    }195196    return collectionId;197  }198199  static extractTokensFromCreationResult(creationResult: ITransactionResult): {200    success: boolean,201    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],202  } {203    if (creationResult.status !== this.transactionStatus.SUCCESS) {204      throw Error('Unable to create tokens!');205    }206    let success = false;207    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];208    creationResult.result.events.forEach(({event: {data, method, section}}) => {209      if (method === 'ExtrinsicSuccess') {210        success = true;211      } else if ((section === 'common') && (method === 'ItemCreated')) {212        tokens.push({213          collectionId: parseInt(data[0].toString(), 10),214          tokenId: parseInt(data[1].toString(), 10),215          owner: data[2].toHuman(),216          amount: data[3].toBigInt(),217        });218      }219    });220    return {success, tokens};221  }222223  static extractTokensFromBurnResult(burnResult: ITransactionResult): {224    success: boolean,225    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],226  } {227    if (burnResult.status !== this.transactionStatus.SUCCESS) {228      throw Error('Unable to burn tokens!');229    }230    let success = false;231    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];232    burnResult.result.events.forEach(({event: {data, method, section}}) => {233      if (method === 'ExtrinsicSuccess') {234        success = true;235      } else if ((section === 'common') && (method === 'ItemDestroyed')) {236        tokens.push({237          collectionId: parseInt(data[0].toString(), 10),238          tokenId: parseInt(data[1].toString(), 10),239          owner: data[2].toHuman(),240          amount: data[3].toBigInt(),241        });242      }243    });244    return {success, tokens};245  }246247  static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {248    let eventId = null;249    events.forEach(({event: {data, method, section}}) => {250      if ((section === expectedSection) && (method === expectedMethod)) {251        eventId = parseInt(data[0].toString(), 10);252      }253    });254255    if (eventId === null) {256      throw Error(`No ${expectedMethod} event was found!`);257    }258    return eventId === collectionId;259  }260261  static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {262    const normalizeAddress = (address: string | ICrossAccountId) => {263      if(typeof address === 'string') return address;264      const obj = {} as any;265      Object.keys(address).forEach(k => {266        obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];267      });268      if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);269      if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();270      return address;271    };272    let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;273    events.forEach(({event: {data, method, section}}) => {274      if ((section === 'common') && (method === 'Transfer')) {275        const hData = (data as any).toJSON();276        transfer = {277          collectionId: hData[0],278          tokenId: hData[1],279          from: normalizeAddress(hData[2]),280          to: normalizeAddress(hData[3]),281          amount: BigInt(hData[4]),282        };283      }284    });285    let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;286    isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);287    isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);288    isSuccess = isSuccess && amount === transfer.amount;289    return isSuccess;290  }291292  static bigIntToDecimals(number: bigint, decimals = 18) {293    const numberStr = number.toString();294    const dotPos = numberStr.length - decimals;295296    if (dotPos <= 0) {297      return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;298    } else {299      const intPart = numberStr.substring(0, dotPos);300      const fractPart = numberStr.substring(dotPos);301      return intPart + '.' + fractPart;302    }303  }304}305306class UniqueEventHelper {307  private static extractIndex(index: any): [number, number] | string {308    if(index.toRawType() === '[u8;2]') return [index[0], index[1]];309    return index.toJSON();310  }311312  private static extractSub(data: any, subTypes: any): {[key: string]: any} {313    let obj: any = {};314    let index = 0;315316    if (data.entries) {317      for(const [key, value] of data.entries()) {318        obj[key] = this.extractData(value, subTypes[index]);319        index++;320      }321    } else obj = data.toJSON();322323    return obj;324  }325326  private static toHuman(data: any) {327    return data && data.toHuman ? data.toHuman() : `${data}`;328  }329330  private static extractData(data: any, type: any): any {331    if(!type) return this.toHuman(data);332    if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();333    if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();334    if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);335    return this.toHuman(data);336  }337338  public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {339    const parsedEvents: IEvent[] = [];340341    events.forEach((record) => {342      const {event, phase} = record;343      const types = event.typeDef;344345      const eventData: IEvent = {346        section: event.section.toString(),347        method: event.method.toString(),348        index: this.extractIndex(event.index),349        data: [],350        phase: phase.toJSON(),351      };352353      event.data.forEach((val: any, index: number) => {354        eventData.data.push(this.extractData(val, types[index]));355      });356357      parsedEvents.push(eventData);358    });359360    return parsedEvents;361  }362}363364export class ChainHelperBase {365  helperBase: any;366367  transactionStatus = UniqueUtil.transactionStatus;368  chainLogType = UniqueUtil.chainLogType;369  util: typeof UniqueUtil;370  eventHelper: typeof UniqueEventHelper;371  logger: ILogger;372  api: ApiPromise | null;373  forcedNetwork: TNetworks | null;374  network: TNetworks | null;375  chainLog: IUniqueHelperLog[];376  children: ChainHelperBase[];377  address: AddressGroup;378  chain: ChainGroup;379380  constructor(logger?: ILogger, helperBase?: any) {381    this.helperBase = helperBase;382383    this.util = UniqueUtil;384    this.eventHelper = UniqueEventHelper;385    if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();386    this.logger = logger;387    this.api = null;388    this.forcedNetwork = null;389    this.network = null;390    this.chainLog = [];391    this.children = [];392    this.address = new AddressGroup(this);393    this.chain = new ChainGroup(this);394  }395396  clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {397    Object.setPrototypeOf(helperCls.prototype, this);398    const newHelper = new helperCls(this.logger, options);399400    newHelper.api = this.api;401    newHelper.network = this.network;402    newHelper.forceNetwork = this.forceNetwork;403404    this.children.push(newHelper);405406    return newHelper;407  }408409  getApi(): ApiPromise {410    if(this.api === null) throw Error('API not initialized');411    return this.api;412  }413414  async subscribeEvents(expectedEvents: {section: string, names: string[]}[]) {415    const collectedEvents: IEvent[] = [];416    const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {417      const ievents = this.eventHelper.extractEvents(events);418      ievents.forEach((event) => {419        expectedEvents.forEach((e => {420          if (event.section === e.section && e.names.includes(event.method)) {421            collectedEvents.push(event);422          }423        }));424      });425    });426    return {unsubscribe: unsubscribe as any, collectedEvents};427  }428429  clearChainLog(): void {430    this.chainLog = [];431  }432433  forceNetwork(value: TNetworks): void {434    this.forcedNetwork = value;435  }436437  async connect(wsEndpoint: string, listeners?: IApiListeners) {438    if (this.api !== null) throw Error('Already connected');439    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);440    this.api = api;441    this.network = network;442  }443444  async disconnect() {445    for (const child of this.children) {446      child.clearApi();447    }448449    if (this.api === null) return;450    await this.api.disconnect();451    this.clearApi();452  }453454  clearApi() {455    this.api = null;456    this.network = null;457  }458459  static async detectNetwork(api: ApiPromise): Promise<TNetworks> {460    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;461    const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];462463    if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;464465    if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;466    return 'opal';467  }468469  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {470    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});471    await api.isReady;472473    const network = await this.detectNetwork(api);474475    await api.disconnect();476477    return network;478  }479480  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{481    api: ApiPromise;482    network: TNetworks;483  }> {484    if(typeof network === 'undefined' || network === null) network = 'opal';485    const supportedRPC = {486      opal: {487        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,488      },489      quartz: {490        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,491      },492      unique: {493        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,494      },495      rococo: {},496      westend: {},497      moonbeam: {},498      moonriver: {},499      acala: {},500      karura: {},501      westmint: {},502    };503    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);504    const rpc = supportedRPC[network];505506    // TODO: investigate how to replace rpc in runtime507    // api._rpcCore.addUserInterfaces(rpc);508509    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});510511    await api.isReadyOrError;512513    if (typeof listeners === 'undefined') listeners = {};514    for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {515      if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;516      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);517    }518519    return {api, network};520  }521522  getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {523    const {events, status} = data;524    if (status.isReady) {525      return this.transactionStatus.NOT_READY;526    }527    if (status.isBroadcast) {528      return this.transactionStatus.NOT_READY;529    }530    if (status.isInBlock || status.isFinalized) {531      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');532      if (errors.length > 0) {533        return this.transactionStatus.FAIL;534      }535      if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {536        return this.transactionStatus.SUCCESS;537      }538    }539540    return this.transactionStatus.FAIL;541  }542543  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {544    const sign = (callback: any) => {545      if(options !== null) return transaction.signAndSend(sender, options, callback);546      return transaction.signAndSend(sender, callback);547    };548    // eslint-disable-next-line no-async-promise-executor549    return new Promise(async (resolve, reject) => {550      try {551        const unsub = await sign((result: any) => {552          const status = this.getTransactionStatus(result);553554          if (status === this.transactionStatus.SUCCESS) {555            this.logger.log(`${label} successful`);556            unsub();557            resolve({result, status});558          } else if (status === this.transactionStatus.FAIL) {559            let moduleError = null;560561            if (result.hasOwnProperty('dispatchError')) {562              const dispatchError = result['dispatchError'];563564              if (dispatchError) {565                if (dispatchError.isModule) {566                  const modErr = dispatchError.asModule;567                  const errorMeta = dispatchError.registry.findMetaError(modErr);568569                  moduleError = `${errorMeta.section}.${errorMeta.name}`;570                } else {571                  moduleError = dispatchError.toHuman();572                }573              } else {574                this.logger.log(result, this.logger.level.ERROR);575              }576            }577578            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);579            unsub();580            reject({status, moduleError, result});581          }582        });583      } catch (e) {584        this.logger.log(e, this.logger.level.ERROR);585        reject(e);586      }587    });588  }589590  async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {591    const api = this.getApi();592    const signingInfo = await api.derive.tx.signingInfo(signer.address);593594    // We need to sign the tx because595    // unsigned transactions does not have an inclusion fee596    tx.sign(signer, {597      blockHash: api.genesisHash,598      genesisHash: api.genesisHash,599      runtimeVersion: api.runtimeVersion,600      nonce: signingInfo.nonce,601    });602603    if (len === null) {604      return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;605    } else {606      return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;607    }608  }609610  constructApiCall(apiCall: string, params: any[]) {611    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);612    let call = this.getApi() as any;613    for(const part of apiCall.slice(4).split('.')) {614      call = call[part];615    }616    return call(...params);617  }618619  async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {620    if(this.api === null) throw Error('API not initialized');621    if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);622623    const startTime = (new Date()).getTime();624    let result: ITransactionResult;625    let events: IEvent[] = [];626    try {627      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;628      events = this.eventHelper.extractEvents(result.result.events);629    }630    catch(e) {631      if(!(e as object).hasOwnProperty('status')) throw e;632      result = e as ITransactionResult;633    }634635    const endTime = (new Date()).getTime();636637    const log = {638      executedAt: endTime,639      executionTime: endTime - startTime,640      type: this.chainLogType.EXTRINSIC,641      status: result.status,642      call: extrinsic,643      signer: this.getSignerAddress(sender),644      params,645    } as IUniqueHelperLog;646647    if(result.status !== this.transactionStatus.SUCCESS) {648      if (result.moduleError) log.moduleError = result.moduleError;649      else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;650    }651    if(events.length > 0) log.events = events;652653    this.chainLog.push(log);654655    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {656      if (result.moduleError) throw Error(`${result.moduleError}`);657      else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));658    }659    return result;660  }661662  async callRpc(rpc: string, params?: any[]) {663    if(typeof params === 'undefined') params = [];664    if(this.api === null) throw Error('API not initialized');665    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);666667    const startTime = (new Date()).getTime();668    let result;669    let error = null;670    const log = {671      type: this.chainLogType.RPC,672      call: rpc,673      params,674    } as IUniqueHelperLog;675676    try {677      result = await this.constructApiCall(rpc, params);678    }679    catch(e) {680      error = e;681    }682683    const endTime = (new Date()).getTime();684685    log.executedAt = endTime;686    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';687    log.executionTime = endTime - startTime;688689    this.chainLog.push(log);690691    if(error !== null) throw error;692693    return result;694  }695696  getSignerAddress(signer: IKeyringPair | string): string {697    if(typeof signer === 'string') return signer;698    return signer.address;699  }700701  fetchAllPalletNames(): string[] {702    if(this.api === null) throw Error('API not initialized');703    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());704  }705706  fetchMissingPalletNames(requiredPallets: string[]): string[] {707    const palletNames = this.fetchAllPalletNames();708    return requiredPallets.filter(p => !palletNames.includes(p));709  }710}711712713class HelperGroup<T extends ChainHelperBase> {714  helper: T;715716  constructor(uniqueHelper: T) {717    this.helper = uniqueHelper;718  }719}720721722class CollectionGroup extends HelperGroup<UniqueHelper> {723  /**724 * Get number of blocks when sponsored transaction is available.725 *726 * @param collectionId ID of collection727 * @param tokenId ID of token728 * @param addressObj address for which the sponsorship is checked729 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});730 * @returns number of blocks or null if sponsorship hasn't been set731 */732  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {733    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();734  }735736  /**737   * Get the number of created collections.738   *739   * @returns number of created collections740   */741  async getTotalCount(): Promise<number> {742    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();743  }744745  /**746   * Get information about the collection with additional data,747   * including the number of tokens it contains, its administrators,748   * the normalized address of the collection's owner, and decoded name and description.749   *750   * @param collectionId ID of collection751   * @example await getData(2)752   * @returns collection information object753   */754  async getData(collectionId: number): Promise<{755    id: number;756    name: string;757    description: string;758    tokensCount: number;759    admins: CrossAccountId[];760    normalizedOwner: TSubstrateAccount;761    raw: any762  } | null> {763    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);764    const humanCollection = collection.toHuman(), collectionData = {765      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],766      raw: humanCollection,767    } as any, jsonCollection = collection.toJSON();768    if (humanCollection === null) return null;769    collectionData.raw.limits = jsonCollection.limits;770    collectionData.raw.permissions = jsonCollection.permissions;771    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);772    for (const key of ['name', 'description']) {773      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);774    }775776    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))777      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)778      : 0;779    collectionData.admins = await this.getAdmins(collectionId);780781    return collectionData;782  }783784  /**785   * Get the addresses of the collection's administrators, optionally normalized.786   *787   * @param collectionId ID of collection788   * @param normalize whether to normalize the addresses to the default ss58 format789   * @example await getAdmins(1)790   * @returns array of administrators791   */792  async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {793    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();794795    return normalize796      ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())797      : admins;798  }799800  /**801   * Get the addresses added to the collection allow-list, optionally normalized.802   * @param collectionId ID of collection803   * @param normalize whether to normalize the addresses to the default ss58 format804   * @example await getAllowList(1)805   * @returns array of allow-listed addresses806   */807  async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {808    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();809    return normalize810      ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())811      : allowListed;812  }813814  /**815   * Get the effective limits of the collection instead of null for default values816   *817   * @param collectionId ID of collection818   * @example await getEffectiveLimits(2)819   * @returns object of collection limits820   */821  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {822    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();823  }824825  /**826   * Burns the collection if the signer has sufficient permissions and collection is empty.827   *828   * @param signer keyring of signer829   * @param collectionId ID of collection830   * @example await helper.collection.burn(aliceKeyring, 3);831   * @returns ```true``` if extrinsic success, otherwise ```false```832   */833  async burn(signer: TSigner, collectionId: number): Promise<boolean> {834    const result = await this.helper.executeExtrinsic(835      signer,836      'api.tx.unique.destroyCollection', [collectionId],837      true,838    );839840    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');841  }842843  /**844   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.845   *846   * @param signer keyring of signer847   * @param collectionId ID of collection848   * @param sponsorAddress Sponsor substrate address849   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")850   * @returns ```true``` if extrinsic success, otherwise ```false```851   */852  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {853    const result = await this.helper.executeExtrinsic(854      signer,855      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],856      true,857    );858859    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');860  }861862  /**863   * Confirms consent to sponsor the collection on behalf of the signer.864   *865   * @param signer keyring of signer866   * @param collectionId ID of collection867   * @example confirmSponsorship(aliceKeyring, 10)868   * @returns ```true``` if extrinsic success, otherwise ```false```869   */870  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {871    const result = await this.helper.executeExtrinsic(872      signer,873      'api.tx.unique.confirmSponsorship', [collectionId],874      true,875    );876877    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');878  }879880  /**881   * Removes the sponsor of a collection, regardless if it consented or not.882   *883   * @param signer keyring of signer884   * @param collectionId ID of collection885   * @example removeSponsor(aliceKeyring, 10)886   * @returns ```true``` if extrinsic success, otherwise ```false```887   */888  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {889    const result = await this.helper.executeExtrinsic(890      signer,891      'api.tx.unique.removeCollectionSponsor', [collectionId],892      true,893    );894895    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');896  }897898  /**899   * Sets the limits of the collection. At least one limit must be specified for a correct call.900   *901   * @param signer keyring of signer902   * @param collectionId ID of collection903   * @param limits collection limits object904   * @example905   * await setLimits(906   *   aliceKeyring,907   *   10,908   *   {909   *     sponsorTransferTimeout: 0,910   *     ownerCanDestroy: false911   *   }912   * )913   * @returns ```true``` if extrinsic success, otherwise ```false```914   */915  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {916    const result = await this.helper.executeExtrinsic(917      signer,918      'api.tx.unique.setCollectionLimits', [collectionId, limits],919      true,920    );921922    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');923  }924925  /**926   * Changes the owner of the collection to the new Substrate address.927   *928   * @param signer keyring of signer929   * @param collectionId ID of collection930   * @param ownerAddress substrate address of new owner931   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")932   * @returns ```true``` if extrinsic success, otherwise ```false```933   */934  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {935    const result = await this.helper.executeExtrinsic(936      signer,937      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],938      true,939    );940941    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');942  }943944  /**945   * Adds a collection administrator.946   *947   * @param signer keyring of signer948   * @param collectionId ID of collection949   * @param adminAddressObj Administrator address (substrate or ethereum)950   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})951   * @returns ```true``` if extrinsic success, otherwise ```false```952   */953  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {954    const result = await this.helper.executeExtrinsic(955      signer,956      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],957      true,958    );959960    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');961  }962963  /**964   * Removes a collection administrator.965   *966   * @param signer keyring of signer967   * @param collectionId ID of collection968   * @param adminAddressObj Administrator address (substrate or ethereum)969   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})970   * @returns ```true``` if extrinsic success, otherwise ```false```971   */972  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {973    const result = await this.helper.executeExtrinsic(974      signer,975      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],976      true,977    );978979    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');980  }981982  /**983   * Check if user is in allow list.984   *985   * @param collectionId ID of collection986   * @param user Account to check987   * @example await getAdmins(1)988   * @returns is user in allow list989   */990  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {991    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();992  }993994  /**995   * Adds an address to allow list996   * @param signer keyring of signer997   * @param collectionId ID of collection998   * @param addressObj address to add to the allow list999   * @returns ```true``` if extrinsic success, otherwise ```false```1000   */1001  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1002    const result = await this.helper.executeExtrinsic(1003      signer,1004      'api.tx.unique.addToAllowList', [collectionId, addressObj],1005      true,1006    );10071008    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1009  }10101011  /**1012   * Removes an address from allow list1013   *1014   * @param signer keyring of signer1015   * @param collectionId ID of collection1016   * @param addressObj address to remove from the allow list1017   * @returns ```true``` if extrinsic success, otherwise ```false```1018   */1019  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1020    const result = await this.helper.executeExtrinsic(1021      signer,1022      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1023      true,1024    );10251026    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1027  }10281029  /**1030   * Sets onchain permissions for selected collection.1031   *1032   * @param signer keyring of signer1033   * @param collectionId ID of collection1034   * @param permissions collection permissions object1035   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});1036   * @returns ```true``` if extrinsic success, otherwise ```false```1037   */1038  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1039    const result = await this.helper.executeExtrinsic(1040      signer,1041      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1042      true,1043    );10441045    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1046  }10471048  /**1049   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.1050   *1051   * @param signer keyring of signer1052   * @param collectionId ID of collection1053   * @param permissions nesting permissions object1054   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});1055   * @returns ```true``` if extrinsic success, otherwise ```false```1056   */1057  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1058    return await this.setPermissions(signer, collectionId, {nesting: permissions});1059  }10601061  /**1062   * Disables nesting for selected collection.1063   *1064   * @param signer keyring of signer1065   * @param collectionId ID of collection1066   * @example disableNesting(aliceKeyring, 10);1067   * @returns ```true``` if extrinsic success, otherwise ```false```1068   */1069  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1070    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1071  }10721073  /**1074   * Sets onchain properties to the collection.1075   *1076   * @param signer keyring of signer1077   * @param collectionId ID of collection1078   * @param properties array of property objects1079   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);1080   * @returns ```true``` if extrinsic success, otherwise ```false```1081   */1082  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1083    const result = await this.helper.executeExtrinsic(1084      signer,1085      'api.tx.unique.setCollectionProperties', [collectionId, properties],1086      true,1087    );10881089    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1090  }10911092  /**1093   * Get collection properties.1094   *1095   * @param collectionId ID of collection1096   * @param propertyKeys optionally filter the returned properties to only these keys1097   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);1098   * @returns array of key-value pairs1099   */1100  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1101    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1102  }11031104  async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1105    const api = this.helper.getApi();1106    const props = (await api.query.common.collectionProperties(collectionId)).toJSON();1107        1108    return (props! as any).consumedSpace;1109  }11101111  async getCollectionOptions(collectionId: number) {1112    return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1113  }11141115  /**1116   * Deletes onchain properties from the collection.1117   *1118   * @param signer keyring of signer1119   * @param collectionId ID of collection1120   * @param propertyKeys array of property keys to delete1121   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);1122   * @returns ```true``` if extrinsic success, otherwise ```false```1123   */1124  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1125    const result = await this.helper.executeExtrinsic(1126      signer,1127      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1128      true,1129    );11301131    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1132  }11331134  /**1135   * Changes the owner of the token.1136   *1137   * @param signer keyring of signer1138   * @param collectionId ID of collection1139   * @param tokenId ID of token1140   * @param addressObj address of a new owner1141   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1142   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1143   * @returns true if the token success, otherwise false1144   */1145  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1146    const result = await this.helper.executeExtrinsic(1147      signer,1148      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1149      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1150    );11511152    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1153  }11541155  /**1156   *1157   * Change ownership of a token(s) on behalf of the owner.1158   *1159   * @param signer keyring of signer1160   * @param collectionId ID of collection1161   * @param tokenId ID of token1162   * @param fromAddressObj address on behalf of which the token will be sent1163   * @param toAddressObj new token owner1164   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1165   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1166   * @returns true if the token success, otherwise false1167   */1168  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1169    const result = await this.helper.executeExtrinsic(1170      signer,1171      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1172      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1173    );1174    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1175  }11761177  /**1178   *1179   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1180   *1181   * @param signer keyring of signer1182   * @param collectionId ID of collection1183   * @param tokenId ID of token1184   * @param amount amount of tokens to be burned. For NFT must be set to 1n1185   * @example burnToken(aliceKeyring, 10, 5);1186   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1187   */1188  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1189    const burnResult = await this.helper.executeExtrinsic(1190      signer,1191      'api.tx.unique.burnItem', [collectionId, tokenId, amount],1192      true, // `Unable to burn token for ${label}`,1193    );1194    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1195    if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1196    return burnedTokens.success;1197  }11981199  /**1200   * Destroys a concrete instance of NFT on behalf of the owner1201   *1202   * @param signer keyring of signer1203   * @param collectionId ID of collection1204   * @param tokenId ID of token1205   * @param fromAddressObj address on behalf of which the token will be burnt1206   * @param amount amount of tokens to be burned. For NFT must be set to 1n1207   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1208   * @returns ```true``` if extrinsic success, otherwise ```false```1209   */1210  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1211    const burnResult = await this.helper.executeExtrinsic(1212      signer,1213      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1214      true, // `Unable to burn token from for ${label}`,1215    );1216    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1217    return burnedTokens.success && burnedTokens.tokens.length > 0;1218  }12191220  /**1221   * Set, change, or remove approved address to transfer the ownership of the NFT.1222   *1223   * @param signer keyring of signer1224   * @param collectionId ID of collection1225   * @param tokenId ID of token1226   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1227   * @param amount amount of token to be approved. For NFT must be set to 1n1228   * @returns ```true``` if extrinsic success, otherwise ```false```1229   */1230  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1231    const approveResult = await this.helper.executeExtrinsic(1232      signer,1233      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1234      true, // `Unable to approve token for ${label}`,1235    );12361237    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1238  }12391240  /**1241   * Get the amount of token pieces approved to transfer or burn. Normally 0.1242   *1243   * @param collectionId ID of collection1244   * @param tokenId ID of token1245   * @param toAccountObj address which is approved to use token pieces1246   * @param fromAccountObj address which may have allowed the use of its owned tokens1247   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1248   * @returns number of approved to transfer pieces1249   */1250  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1251    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1252  }12531254  /**1255   * Get the last created token ID in a collection1256   *1257   * @param collectionId ID of collection1258   * @example getLastTokenId(10);1259   * @returns id of the last created token1260   */1261  async getLastTokenId(collectionId: number): Promise<number> {1262    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1263  }12641265  /**1266   * Check if token exists1267   *1268   * @param collectionId ID of collection1269   * @param tokenId ID of token1270   * @example doesTokenExist(10, 20);1271   * @returns true if the token exists, otherwise false1272   */1273  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1274    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1275  }1276}12771278class NFTnRFT extends CollectionGroup {1279  /**1280   * Get tokens owned by account1281   *1282   * @param collectionId ID of collection1283   * @param addressObj tokens owner1284   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1285   * @returns array of token ids owned by account1286   */1287  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1288    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1289  }12901291  /**1292   * Get token data1293   *1294   * @param collectionId ID of collection1295   * @param tokenId ID of token1296   * @param propertyKeys optionally filter the token properties to only these keys1297   * @param blockHashAt optionally query the data at some block with this hash1298   * @example getToken(10, 5);1299   * @returns human readable token data1300   */1301  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1302    properties: IProperty[];1303    owner: CrossAccountId;1304    normalizedOwner: CrossAccountId;1305  }| null> {1306    let tokenData;1307    if(typeof blockHashAt === 'undefined') {1308      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1309    }1310    else {1311      if(propertyKeys.length == 0) {1312        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1313        if(!collection) return null;1314        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1315      }1316      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1317    }1318    tokenData = tokenData.toHuman();1319    if (tokenData === null || tokenData.owner === null) return null;1320    const owner = {} as any;1321    for (const key of Object.keys(tokenData.owner)) {1322      owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1323        ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1324        : tokenData.owner[key];1325    }1326    tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1327    return tokenData;1328  }13291330  /**1331   * Set permissions to change token properties1332   *1333   * @param signer keyring of signer1334   * @param collectionId ID of collection1335   * @param permissions permissions to change a property by the collection admin or token owner1336   * @example setTokenPropertyPermissions(1337   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1338   * )1339   * @returns true if extrinsic success otherwise false1340   */1341  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1342    const result = await this.helper.executeExtrinsic(1343      signer,1344      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1345      true,1346    );13471348    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1349  }13501351  /**1352   * Get token property permissions.1353   *1354   * @param collectionId ID of collection1355   * @param propertyKeys optionally filter the returned property permissions to only these keys1356   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1357   * @returns array of key-permission pairs1358   */1359  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1360    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1361  }13621363  /**1364   * Set token properties1365   *1366   * @param signer keyring of signer1367   * @param collectionId ID of collection1368   * @param tokenId ID of token1369   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1370   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1371   * @returns ```true``` if extrinsic success, otherwise ```false```1372   */1373  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1374    const result = await this.helper.executeExtrinsic(1375      signer,1376      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1377      true,1378    );13791380    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1381  }13821383  /**1384   * Get properties, metadata assigned to a token.1385   *1386   * @param collectionId ID of collection1387   * @param tokenId ID of token1388   * @param propertyKeys optionally filter the returned properties to only these keys1389   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1390   * @returns array of key-value pairs1391   */1392  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1393    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1394  }13951396  /**1397   * Delete the provided properties of a token1398   * @param signer keyring of signer1399   * @param collectionId ID of collection1400   * @param tokenId ID of token1401   * @param propertyKeys property keys to be deleted1402   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1403   * @returns ```true``` if extrinsic success, otherwise ```false```1404   */1405  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1406    const result = await this.helper.executeExtrinsic(1407      signer,1408      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1409      true,1410    );14111412    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1413  }14141415  /**1416   * Mint new collection1417   *1418   * @param signer keyring of signer1419   * @param collectionOptions basic collection options and properties1420   * @param mode NFT or RFT type of a collection1421   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1422   * @returns object of the created collection1423   */1424  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1425    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1426    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1427    for (const key of ['name', 'description', 'tokenPrefix']) {1428      if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1429    }1430    const creationResult = await this.helper.executeExtrinsic(1431      signer,1432      'api.tx.unique.createCollectionEx', [collectionOptions],1433      true, // errorLabel,1434    );1435    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1436  }14371438  getCollectionObject(_collectionId: number): any {1439    return null;1440  }14411442  getTokenObject(_collectionId: number, _tokenId: number): any {1443    return null;1444  }14451446  /**1447   * Tells whether the given `owner` approves the `operator`.1448   * @param collectionId ID of collection1449   * @param owner owner address1450   * @param operator operator addrees1451   * @returns true if operator is enabled1452   */1453  async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1454    return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1455  }14561457  /** Sets or unsets the approval of a given operator.1458   *  The `operator` is allowed to transfer all tokens of the `caller` on their behalf.1459   *  @param operator Operator1460   *  @param approved Should operator status be granted or revoked?1461   *  @returns ```true``` if extrinsic success, otherwise ```false```1462   */1463  async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1464    const result = await this.helper.executeExtrinsic(1465      signer,1466      'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1467      true,1468    );1469    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1470  }1471}147214731474class NFTGroup extends NFTnRFT {1475  /**1476   * Get collection object1477   * @param collectionId ID of collection1478   * @example getCollectionObject(2);1479   * @returns instance of UniqueNFTCollection1480   */1481  getCollectionObject(collectionId: number): UniqueNFTCollection {1482    return new UniqueNFTCollection(collectionId, this.helper);1483  }14841485  /**1486   * Get token object1487   * @param collectionId ID of collection1488   * @param tokenId ID of token1489   * @example getTokenObject(10, 5);1490   * @returns instance of UniqueNFTToken1491   */1492  getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1493    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1494  }14951496  /**1497   * Get token's owner1498   * @param collectionId ID of collection1499   * @param tokenId ID of token1500   * @param blockHashAt optionally query the data at the block with this hash1501   * @example getTokenOwner(10, 5);1502   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1503   */1504  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1505    let owner;1506    if (typeof blockHashAt === 'undefined') {1507      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1508    } else {1509      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1510    }1511    return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1512  }15131514  /**1515   * Is token approved to transfer1516   * @param collectionId ID of collection1517   * @param tokenId ID of token1518   * @param toAccountObj address to be approved1519   * @returns ```true``` if extrinsic success, otherwise ```false```1520   */1521  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1522    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1523  }15241525  /**1526   * Changes the owner of the token.1527   *1528   * @param signer keyring of signer1529   * @param collectionId ID of collection1530   * @param tokenId ID of token1531   * @param addressObj address of a new owner1532   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1533   * @returns ```true``` if extrinsic success, otherwise ```false```1534   */1535  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1536    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1537  }15381539  /**1540   *1541   * Change ownership of a NFT on behalf of the owner.1542   *1543   * @param signer keyring of signer1544   * @param collectionId ID of collection1545   * @param tokenId ID of token1546   * @param fromAddressObj address on behalf of which the token will be sent1547   * @param toAddressObj new token owner1548   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1549   * @returns ```true``` if extrinsic success, otherwise ```false```1550   */1551  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1552    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1553  }15541555  /**1556   * Recursively find the address that owns the token1557   * @param collectionId ID of collection1558   * @param tokenId ID of token1559   * @param blockHashAt1560   * @example getTokenTopmostOwner(10, 5);1561   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1562   */1563  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1564    let owner;1565    if (typeof blockHashAt === 'undefined') {1566      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1567    } else {1568      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1569    }15701571    if (owner === null) return null;15721573    return owner.toHuman();1574  }15751576  /**1577   * Get tokens nested in the provided token1578   * @param collectionId ID of collection1579   * @param tokenId ID of token1580   * @param blockHashAt optionally query the data at the block with this hash1581   * @example getTokenChildren(10, 5);1582   * @returns tokens whose depth of nesting is <= 51583   */1584  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1585    let children;1586    if(typeof blockHashAt === 'undefined') {1587      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1588    } else {1589      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1590    }15911592    return children.toJSON().map((x: any) => {1593      return {collectionId: x.collection, tokenId: x.token};1594    });1595  }15961597  /**1598   * Nest one token into another1599   * @param signer keyring of signer1600   * @param tokenObj token to be nested1601   * @param rootTokenObj token to be parent1602   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1603   * @returns ```true``` if extrinsic success, otherwise ```false```1604   */1605  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1606    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1607    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1608    if(!result) {1609      throw Error('Unable to nest token!');1610    }1611    return result;1612  }16131614  /**1615   * Remove token from nested state1616   * @param signer keyring of signer1617   * @param tokenObj token to unnest1618   * @param rootTokenObj parent of a token1619   * @param toAddressObj address of a new token owner1620   * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1621   * @returns ```true``` if extrinsic success, otherwise ```false```1622   */1623  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1624    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1625    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1626    if(!result) {1627      throw Error('Unable to unnest token!');1628    }1629    return result;1630  }16311632  /**1633   * Mint new collection1634   * @param signer keyring of signer1635   * @param collectionOptions Collection options1636   * @example1637   * mintCollection(aliceKeyring, {1638   *   name: 'New',1639   *   description: 'New collection',1640   *   tokenPrefix: 'NEW',1641   * })1642   * @returns object of the created collection1643   */1644  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1645    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1646  }16471648  /**1649   * Mint new token1650   * @param signer keyring of signer1651   * @param data token data1652   * @returns created token object1653   */1654  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1655    const creationResult = await this.helper.executeExtrinsic(1656      signer,1657      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1658        nft: {1659          properties: data.properties,1660        },1661      }],1662      true,1663    );1664    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1665    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1666    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1667    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1668  }16691670  /**1671   * Mint multiple NFT tokens1672   * @param signer keyring of signer1673   * @param collectionId ID of collection1674   * @param tokens array of tokens with owner and properties1675   * @example1676   * mintMultipleTokens(aliceKeyring, 10, [{1677   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1678   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1679   *   },{1680   *     owner: {Ethereum: "0x9F0583DbB855d..."},1681   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1682   * }]);1683   * @returns ```true``` if extrinsic success, otherwise ```false```1684   */1685  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1686    const creationResult = await this.helper.executeExtrinsic(1687      signer,1688      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1689      true,1690    );1691    const collection = this.getCollectionObject(collectionId);1692    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1693  }16941695  /**1696   * Mint multiple NFT tokens with one owner1697   * @param signer keyring of signer1698   * @param collectionId ID of collection1699   * @param owner tokens owner1700   * @param tokens array of tokens with owner and properties1701   * @example1702   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1703   *   properties: [{1704   *   key: "gender",1705   *   value: "female",1706   *  },{1707   *   key: "age",1708   *   value: "33",1709   *  }],1710   * }]);1711   * @returns array of newly created tokens1712   */1713  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1714    const rawTokens = [];1715    for (const token of tokens) {1716      const raw = {NFT: {properties: token.properties}};1717      rawTokens.push(raw);1718    }1719    const creationResult = await this.helper.executeExtrinsic(1720      signer,1721      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1722      true,1723    );1724    const collection = this.getCollectionObject(collectionId);1725    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1726  }17271728  /**1729   * Set, change, or remove approved address to transfer the ownership of the NFT.1730   *1731   * @param signer keyring of signer1732   * @param collectionId ID of collection1733   * @param tokenId ID of token1734   * @param toAddressObj address to approve1735   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1736   * @returns ```true``` if extrinsic success, otherwise ```false```1737   */1738  approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1739    return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1740  }1741}174217431744class RFTGroup extends NFTnRFT {1745  /**1746   * Get collection object1747   * @param collectionId ID of collection1748   * @example getCollectionObject(2);1749   * @returns instance of UniqueRFTCollection1750   */1751  getCollectionObject(collectionId: number): UniqueRFTCollection {1752    return new UniqueRFTCollection(collectionId, this.helper);1753  }17541755  /**1756   * Get token object1757   * @param collectionId ID of collection1758   * @param tokenId ID of token1759   * @example getTokenObject(10, 5);1760   * @returns instance of UniqueNFTToken1761   */1762  getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1763    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1764  }17651766  /**1767   * Get top 10 token owners with the largest number of pieces1768   * @param collectionId ID of collection1769   * @param tokenId ID of token1770   * @example getTokenTop10Owners(10, 5);1771   * @returns array of top 10 owners1772   */1773  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1774    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1775  }17761777  /**1778   * Get number of pieces owned by address1779   * @param collectionId ID of collection1780   * @param tokenId ID of token1781   * @param addressObj address token owner1782   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1783   * @returns number of pieces ownerd by address1784   */1785  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1786    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1787  }17881789  /**1790   * Transfer pieces of token to another address1791   * @param signer keyring of signer1792   * @param collectionId ID of collection1793   * @param tokenId ID of token1794   * @param addressObj address of a new owner1795   * @param amount number of pieces to be transfered1796   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1797   * @returns ```true``` if extrinsic success, otherwise ```false```1798   */1799  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1800    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1801  }18021803  /**1804   * Change ownership of some pieces of RFT on behalf of the owner.1805   * @param signer keyring of signer1806   * @param collectionId ID of collection1807   * @param tokenId ID of token1808   * @param fromAddressObj address on behalf of which the token will be sent1809   * @param toAddressObj new token owner1810   * @param amount number of pieces to be transfered1811   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1812   * @returns ```true``` if extrinsic success, otherwise ```false```1813   */1814  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1815    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1816  }18171818  /**1819   * Mint new collection1820   * @param signer keyring of signer1821   * @param collectionOptions Collection options1822   * @example1823   * mintCollection(aliceKeyring, {1824   *   name: 'New',1825   *   description: 'New collection',1826   *   tokenPrefix: 'NEW',1827   * })1828   * @returns object of the created collection1829   */1830  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1831    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1832  }18331834  /**1835   * Mint new token1836   * @param signer keyring of signer1837   * @param data token data1838   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1839   * @returns created token object1840   */1841  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1842    const creationResult = await this.helper.executeExtrinsic(1843      signer,1844      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1845        refungible: {1846          pieces: data.pieces,1847          properties: data.properties,1848        },1849      }],1850      true,1851    );1852    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1853    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1854    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1855    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1856  }18571858  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1859    throw Error('Not implemented');1860    const creationResult = await this.helper.executeExtrinsic(1861      signer,1862      'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1863      true, // `Unable to mint RFT tokens for ${label}`,1864    );1865    const collection = this.getCollectionObject(collectionId);1866    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1867  }18681869  /**1870   * Mint multiple RFT tokens with one owner1871   * @param signer keyring of signer1872   * @param collectionId ID of collection1873   * @param owner tokens owner1874   * @param tokens array of tokens with properties and pieces1875   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1876   * @returns array of newly created RFT tokens1877   */1878  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1879    const rawTokens = [];1880    for (const token of tokens) {1881      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1882      rawTokens.push(raw);1883    }1884    const creationResult = await this.helper.executeExtrinsic(1885      signer,1886      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1887      true,1888    );1889    const collection = this.getCollectionObject(collectionId);1890    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1891  }18921893  /**1894   * Destroys a concrete instance of RFT.1895   * @param signer keyring of signer1896   * @param collectionId ID of collection1897   * @param tokenId ID of token1898   * @param amount number of pieces to be burnt1899   * @example burnToken(aliceKeyring, 10, 5);1900   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1901   */1902  async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1903    return await super.burnToken(signer, collectionId, tokenId, amount);1904  }19051906  /**1907   * Destroys a concrete instance of RFT on behalf of the owner.1908   * @param signer keyring of signer1909   * @param collectionId ID of collection1910   * @param tokenId ID of token1911   * @param fromAddressObj address on behalf of which the token will be burnt1912   * @param amount number of pieces to be burnt1913   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1914   * @returns ```true``` if extrinsic success, otherwise ```false```1915   */1916  async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1917    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1918  }19191920  /**1921   * Set, change, or remove approved address to transfer the ownership of the RFT.1922   *1923   * @param signer keyring of signer1924   * @param collectionId ID of collection1925   * @param tokenId ID of token1926   * @param toAddressObj address to approve1927   * @param amount number of pieces to be approved1928   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1929   * @returns true if the token success, otherwise false1930   */1931  approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1932    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1933  }19341935  /**1936   * Get total number of pieces1937   * @param collectionId ID of collection1938   * @param tokenId ID of token1939   * @example getTokenTotalPieces(10, 5);1940   * @returns number of pieces1941   */1942  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1943    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1944  }19451946  /**1947   * Change number of token pieces. Signer must be the owner of all token pieces.1948   * @param signer keyring of signer1949   * @param collectionId ID of collection1950   * @param tokenId ID of token1951   * @param amount new number of pieces1952   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1953   * @returns true if the repartion was success, otherwise false1954   */1955  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1956    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1957    const repartitionResult = await this.helper.executeExtrinsic(1958      signer,1959      'api.tx.unique.repartition', [collectionId, tokenId, amount],1960      true,1961    );1962    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1963    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1964  }1965}196619671968class FTGroup extends CollectionGroup {1969  /**1970   * Get collection object1971   * @param collectionId ID of collection1972   * @example getCollectionObject(2);1973   * @returns instance of UniqueFTCollection1974   */1975  getCollectionObject(collectionId: number): UniqueFTCollection {1976    return new UniqueFTCollection(collectionId, this.helper);1977  }19781979  /**1980   * Mint new fungible collection1981   * @param signer keyring of signer1982   * @param collectionOptions Collection options1983   * @param decimalPoints number of token decimals1984   * @example1985   * mintCollection(aliceKeyring, {1986   *   name: 'New',1987   *   description: 'New collection',1988   *   tokenPrefix: 'NEW',1989   * }, 18)1990   * @returns newly created fungible collection1991   */1992  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1993    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1994    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1995    collectionOptions.mode = {fungible: decimalPoints};1996    for (const key of ['name', 'description', 'tokenPrefix']) {1997      if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1998    }1999    const creationResult = await this.helper.executeExtrinsic(2000      signer,2001      'api.tx.unique.createCollectionEx', [collectionOptions],2002      true,2003    );2004    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2005  }20062007  /**2008   * Mint tokens2009   * @param signer keyring of signer2010   * @param collectionId ID of collection2011   * @param owner address owner of new tokens2012   * @param amount amount of tokens to be meanted2013   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);2014   * @returns ```true``` if extrinsic success, otherwise ```false```2015   */2016  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2017    const creationResult = await this.helper.executeExtrinsic(2018      signer,2019      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2020        fungible: {2021          value: amount,2022        },2023      }],2024      true, // `Unable to mint fungible tokens for ${label}`,2025    );2026    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2027  }20282029  /**2030   * Mint multiple Fungible tokens with one owner2031   * @param signer keyring of signer2032   * @param collectionId ID of collection2033   * @param owner tokens owner2034   * @param tokens array of tokens with properties and pieces2035   * @returns ```true``` if extrinsic success, otherwise ```false```2036   */2037  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {2038    const rawTokens = [];2039    for (const token of tokens) {2040      const raw = {Fungible: {Value: token.value}};2041      rawTokens.push(raw);2042    }2043    const creationResult = await this.helper.executeExtrinsic(2044      signer,2045      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2046      true,2047    );2048    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2049  }20502051  /**2052   * Get the top 10 owners with the largest balance for the Fungible collection2053   * @param collectionId ID of collection2054   * @example getTop10Owners(10);2055   * @returns array of ```ICrossAccountId```2056   */2057  async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2058    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2059  }20602061  /**2062   * Get account balance2063   * @param collectionId ID of collection2064   * @param addressObj address of owner2065   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})2066   * @returns amount of fungible tokens owned by address2067   */2068  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2069    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2070  }20712072  /**2073   * Transfer tokens to address2074   * @param signer keyring of signer2075   * @param collectionId ID of collection2076   * @param toAddressObj address recipient2077   * @param amount amount of tokens to be sent2078   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2079   * @returns ```true``` if extrinsic success, otherwise ```false```2080   */2081  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2082    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2083  }20842085  /**2086   * Transfer some tokens on behalf of the owner.2087   * @param signer keyring of signer2088   * @param collectionId ID of collection2089   * @param fromAddressObj address on behalf of which tokens will be sent2090   * @param toAddressObj address where token to be sent2091   * @param amount number of tokens to be sent2092   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);2093   * @returns ```true``` if extrinsic success, otherwise ```false```2094   */2095  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2096    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2097  }20982099  /**2100   * Destroy some amount of tokens2101   * @param signer keyring of signer2102   * @param collectionId ID of collection2103   * @param amount amount of tokens to be destroyed2104   * @example burnTokens(aliceKeyring, 10, 1000n);2105   * @returns ```true``` if extrinsic success, otherwise ```false```2106   */2107  async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2108    return await super.burnToken(signer, collectionId, 0, amount);2109  }21102111  /**2112   * Burn some tokens on behalf of the owner.2113   * @param signer keyring of signer2114   * @param collectionId ID of collection2115   * @param fromAddressObj address on behalf of which tokens will be burnt2116   * @param amount amount of tokens to be burnt2117   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2118   * @returns ```true``` if extrinsic success, otherwise ```false```2119   */2120  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2121    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2122  }21232124  /**2125   * Get total collection supply2126   * @param collectionId2127   * @returns2128   */2129  async getTotalPieces(collectionId: number): Promise<bigint> {2130    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2131  }21322133  /**2134   * Set, change, or remove approved address to transfer tokens.2135   *2136   * @param signer keyring of signer2137   * @param collectionId ID of collection2138   * @param toAddressObj address to be approved2139   * @param amount amount of tokens to be approved2140   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)2141   * @returns ```true``` if extrinsic success, otherwise ```false```2142   */2143  approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2144    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2145  }21462147  /**2148   * Get amount of fungible tokens approved to transfer2149   * @param collectionId ID of collection2150   * @param fromAddressObj owner of tokens2151   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner2152   * @returns number of tokens approved for the transfer2153   */2154  getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2155    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2156  }2157}215821592160class ChainGroup extends HelperGroup<ChainHelperBase> {2161  /**2162   * Get system properties of a chain2163   * @example getChainProperties();2164   * @returns ss58Format, token decimals, and token symbol2165   */2166  getChainProperties(): IChainProperties {2167    const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2168    return {2169      ss58Format: properties.ss58Format.toJSON(),2170      tokenDecimals: properties.tokenDecimals.toJSON(),2171      tokenSymbol: properties.tokenSymbol.toJSON(),2172    };2173  }21742175  /**2176   * Get chain header2177   * @example getLatestBlockNumber();2178   * @returns the number of the last block2179   */2180  async getLatestBlockNumber(): Promise<number> {2181    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2182  }21832184  /**2185   * Get block hash by block number2186   * @param blockNumber number of block2187   * @example getBlockHashByNumber(12345);2188   * @returns hash of a block2189   */2190  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2191    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2192    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2193    return blockHash;2194  }21952196  // TODO add docs2197  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2198    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2199    if (!blockHash) return null;2200    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2201  }22022203  /**2204   * Get account nonce2205   * @param address substrate address2206   * @example getNonce("5GrwvaEF5zXb26Fz...");2207   * @returns number, account's nonce2208   */2209  async getNonce(address: TSubstrateAccount): Promise<number> {2210    return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2211  }2212}22132214class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2215  /**2216 * Get substrate address balance2217 * @param address substrate address2218 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2219 * @returns amount of tokens on address2220 */2221  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2222    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2223  }22242225  /**2226   * Transfer tokens to substrate address2227   * @param signer keyring of signer2228   * @param address substrate address of a recipient2229   * @param amount amount of tokens to be transfered2230   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2231   * @returns ```true``` if extrinsic success, otherwise ```false```2232   */2233  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2234    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);22352236    let transfer = {from: null, to: null, amount: 0n} as any;2237    result.result.events.forEach(({event: {data, method, section}}) => {2238      if ((section === 'balances') && (method === 'Transfer')) {2239        transfer = {2240          from: this.helper.address.normalizeSubstrate(data[0]),2241          to: this.helper.address.normalizeSubstrate(data[1]),2242          amount: BigInt(data[2]),2243        };2244      }2245    });2246    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2247      && this.helper.address.normalizeSubstrate(address) === transfer.to2248      && BigInt(amount) === transfer.amount;2249    return isSuccess;2250  }22512252  /**2253   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2254   * @param address substrate address2255   * @returns2256   */2257  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2258    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2259    return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2260  }2261}22622263class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2264  /**2265   * Get ethereum address balance2266   * @param address ethereum address2267   * @example getEthereum("0x9F0583DbB855d...")2268   * @returns amount of tokens on address2269   */2270  async getEthereum(address: TEthereumAccount): Promise<bigint> {2271    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2272  }22732274  /**2275   * Transfer tokens to address2276   * @param signer keyring of signer2277   * @param address Ethereum address of a recipient2278   * @param amount amount of tokens to be transfered2279   * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2280   * @returns ```true``` if extrinsic success, otherwise ```false```2281   */2282  async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2283    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22842285    let transfer = {from: null, to: null, amount: 0n} as any;2286    result.result.events.forEach(({event: {data, method, section}}) => {2287      if ((section === 'balances') && (method === 'Transfer')) {2288        transfer = {2289          from: data[0].toString(),2290          to: data[1].toString(),2291          amount: BigInt(data[2]),2292        };2293      }2294    });2295    const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2296      && address === transfer.to2297      && BigInt(amount) === transfer.amount;2298    return isSuccess;2299  }2300}23012302class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2303  subBalanceGroup: SubstrateBalanceGroup<T>;2304  ethBalanceGroup: EthereumBalanceGroup<T>;23052306  constructor(helper: T) {2307    super(helper);2308    this.subBalanceGroup = new SubstrateBalanceGroup(helper);2309    this.ethBalanceGroup = new EthereumBalanceGroup(helper);2310  }23112312  getCollectionCreationPrice(): bigint {2313    return 2n * this.getOneTokenNominal();2314  }2315  /**2316   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2317   * @example getOneTokenNominal()2318   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2319   */2320  getOneTokenNominal(): bigint {2321    const chainProperties = this.helper.chain.getChainProperties();2322    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2323  }23242325  /**2326   * Get substrate address balance2327   * @param address substrate address2328   * @example getSubstrate("5GrwvaEF5zXb26Fz...")2329   * @returns amount of tokens on address2330   */2331  getSubstrate(address: TSubstrateAccount): Promise<bigint> {2332    return this.subBalanceGroup.getSubstrate(address);2333  }23342335  /**2336   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2337   * @param address substrate address2338   * @returns2339   */2340  getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2341    return this.subBalanceGroup.getSubstrateFull(address);2342  }23432344  /**2345   * Get ethereum address balance2346   * @param address ethereum address2347   * @example getEthereum("0x9F0583DbB855d...")2348   * @returns amount of tokens on address2349   */2350  getEthereum(address: TEthereumAccount): Promise<bigint> {2351    return this.ethBalanceGroup.getEthereum(address);2352  }23532354  /**2355   * Transfer tokens to substrate address2356   * @param signer keyring of signer2357   * @param address substrate address of a recipient2358   * @param amount amount of tokens to be transfered2359   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2360   * @returns ```true``` if extrinsic success, otherwise ```false```2361   */2362  transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2363    return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2364  }23652366  async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2367    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23682369    let transfer = {from: null, to: null, amount: 0n} as any;2370    result.result.events.forEach(({event: {data, method, section}}) => {2371      if ((section === 'balances') && (method === 'Transfer')) {2372        transfer = {2373          from: this.helper.address.normalizeSubstrate(data[0]),2374          to: this.helper.address.normalizeSubstrate(data[1]),2375          amount: BigInt(data[2]),2376        };2377      }2378    });2379    let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2380    isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2381    isSuccess = isSuccess && BigInt(amount) === transfer.amount;2382    return isSuccess;2383  }2384}23852386class AddressGroup extends HelperGroup<ChainHelperBase> {2387  /**2388   * Normalizes the address to the specified ss58 format, by default ```42```.2389   * @param address substrate address2390   * @param ss58Format format for address conversion, by default ```42```2391   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2392   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2393   */2394  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2395    return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2396  }23972398  /**2399   * Get address in the connected chain format2400   * @param address substrate address2401   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2402   * @returns address in chain format2403   */2404  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2405    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2406  }24072408  /**2409   * Get substrate mirror of an ethereum address2410   * @param ethAddress ethereum address2411   * @param toChainFormat false for normalized account2412   * @example ethToSubstrate('0x9F0583DbB855d...')2413   * @returns substrate mirror of a provided ethereum address2414   */2415  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2416    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2417  }24182419  /**2420   * Get ethereum mirror of a substrate address2421   * @param subAddress substrate account2422   * @example substrateToEth("5DnSF6RRjwteE3BrC...")2423   * @returns ethereum mirror of a provided substrate address2424   */2425  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2426    return CrossAccountId.translateSubToEth(subAddress);2427  }24282429  /**2430   * Encode key to substrate address2431   * @param key key for encoding address2432   * @param ss58Format prefix for encoding to the address of the corresponding network2433   * @returns encoded substrate address2434   */2435  encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2436    const u8a :Uint8Array = typeof key === 'string'2437      ? hexToU8a(key)2438      : typeof key === 'bigint'2439        ? hexToU8a(key.toString(16))2440        : key;2441  2442    if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2443      throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2444    }2445  2446    const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2447    if (!allowedDecodedLengths.includes(u8a.length)) {2448      throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2449    }2450  2451    const u8aPrefix = ss58Format < 642452      ? new Uint8Array([ss58Format])2453      : new Uint8Array([2454        ((ss58Format & 0xfc) >> 2) | 0x40,2455        (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2456      ]);24572458    const input = u8aConcat(u8aPrefix, u8a);2459  2460    return base58Encode(u8aConcat(2461      input,2462      blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2463    ));2464  }24652466  /**2467   * Restore substrate address from bigint representation2468   * @param number decimal representation of substrate address2469   * @returns substrate address2470   */2471  restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2472    if (this.helper.api === null) {2473      throw 'Not connected';2474    }2475    const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2476    if (res === undefined || res === null) {2477      throw 'Restore address error';2478    }2479    return res.toString();2480  }24812482  /**2483   * Convert etherium cross account id to substrate cross account id2484   * @param ethCrossAccount etherium cross account2485   * @returns substrate cross account id2486   */2487  convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2488    if (ethCrossAccount.sub === '0') {2489      return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2490    }2491    2492    const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2493    return {Substrate: ss58};2494  }24952496  paraSiblingSovereignAccount(paraid: number) {2497    // We are getting a *sibling* parachain sovereign account,2498    // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c2499    const siblingPrefix = '0x7369626c';25002501    const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2502    const suffix = '000000000000000000000000000000000000000000000000';25032504    return siblingPrefix + encodedParaId + suffix;2505  }2506}25072508class StakingGroup extends HelperGroup<UniqueHelper> {2509  /**2510   * Stake tokens for App Promotion2511   * @param signer keyring of signer2512   * @param amountToStake amount of tokens to stake2513   * @param label extra label for log2514   * @returns2515   */2516  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2517    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2518    const _stakeResult = await this.helper.executeExtrinsic(2519      signer, 'api.tx.appPromotion.stake',2520      [amountToStake], true,2521    );2522    // TODO extract info from stakeResult2523    return true;2524  }25252526  /**2527   * Unstake tokens for App Promotion2528   * @param signer keyring of signer2529   * @param amountToUnstake amount of tokens to unstake2530   * @param label extra label for log2531   * @returns block number where balances will be unlocked2532   */2533  async unstake(signer: TSigner, label?: string): Promise<number> {2534    if(typeof label === 'undefined') label = `${signer.address}`;2535    const _unstakeResult = await this.helper.executeExtrinsic(2536      signer, 'api.tx.appPromotion.unstake',2537      [], true,2538    );2539    // TODO extract block number fron events2540    return 1;2541  }25422543  /**2544   * Get total staked amount for address2545   * @param address substrate or ethereum address2546   * @returns total staked amount2547   */2548  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2549    if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2550    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2551  }25522553  /**2554   * Get total staked per block2555   * @param address substrate or ethereum address2556   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2557   */2558  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2559    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2560    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2561      return {2562        block: block.toBigInt(),2563        amount: amount.toBigInt(),2564      };2565    });2566  }25672568  /**2569   * Get total pending unstake amount for address2570   * @param address substrate or ethereum address2571   * @returns total pending unstake amount2572   */2573  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2574    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2575  }25762577  /**2578   * Get pending unstake amount per block for address2579   * @param address substrate or ethereum address2580   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block2581   */2582  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2583    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2584    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2585      return {2586        block: block.toBigInt(),2587        amount: amount.toBigInt(),2588      };2589    });2590    return result;2591  }2592}25932594class SchedulerGroup extends HelperGroup<UniqueHelper> {2595  constructor(helper: UniqueHelper) {2596    super(helper);2597  }25982599  cancelScheduled(signer: TSigner, scheduledId: string) {2600    return this.helper.executeExtrinsic(2601      signer,2602      'api.tx.scheduler.cancelNamed',2603      [scheduledId],2604      true,2605    );2606  }26072608  changePriority(signer: TSigner, scheduledId: string, priority: number) {2609    return this.helper.executeExtrinsic(2610      signer,2611      'api.tx.scheduler.changeNamedPriority',2612      [scheduledId, priority],2613      true,2614    );2615  }26162617  scheduleAt<T extends UniqueHelper>(2618    executionBlockNumber: number,2619    options: ISchedulerOptions = {},2620  ) {2621    return this.schedule<T>('schedule', executionBlockNumber, options);2622  }26232624  scheduleAfter<T extends UniqueHelper>(2625    blocksBeforeExecution: number,2626    options: ISchedulerOptions = {},2627  ) {2628    return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2629  }26302631  schedule<T extends UniqueHelper>(2632    scheduleFn: 'schedule' | 'scheduleAfter',2633    blocksNum: number,2634    options: ISchedulerOptions = {},2635  ) {2636    // eslint-disable-next-line @typescript-eslint/naming-convention2637    const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2638    return this.helper.clone(ScheduledHelperType, {2639      scheduleFn,2640      blocksNum,2641      options,2642    }) as T;2643  }2644}26452646class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2647  //todo:collator documentation2648  setKeys(signer: TSigner, key: string) {2649    return this.helper.executeExtrinsic(2650      signer,2651      'api.tx.session.setKeys', 2652      [2653        key,2654        '0x0',2655      ],2656      true,2657    );2658  }26592660  setOwnKeys(signer: TSigner) {2661    return this.setKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));2662  }26632664  addInvulnerable(signer: TSigner, address: string) {2665    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);2666  }26672668  removeInvulnerable(signer: TSigner, address: string) {2669    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);2670  }26712672  async getInvulnerables() {2673    return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2674  }2675}26762677class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2678  async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2679    await this.helper.executeExtrinsic(2680      signer,2681      'api.tx.foreignAssets.registerForeignAsset',2682      [ownerAddress, location, metadata],2683      true,2684    );2685  }26862687  async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2688    await this.helper.executeExtrinsic(2689      signer,2690      'api.tx.foreignAssets.updateForeignAsset',2691      [foreignAssetId, location, metadata],2692      true,2693    );2694  }2695}26962697class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2698  palletName: string;26992700  constructor(helper: T, palletName: string) {2701    super(helper);27022703    this.palletName = palletName;2704  }27052706  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2707    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2708  }2709}27102711class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2712  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2713    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2714  }27152716  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2717    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2718  }27192720  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2721    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2722  }2723}27242725class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2726  async accounts(address: string, currencyId: any) {2727    const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2728    return BigInt(free);2729  }2730}27312732class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2733  async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2734    await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2735  }27362737  async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2738    await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2739  }27402741  async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2742    await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2743  }27442745  async account(assetId: string | number, address: string) {2746    const accountAsset = (2747      await this.helper.callRpc('api.query.assets.account', [assetId, address])2748    ).toJSON()! as any;27492750    if (accountAsset !== null) {2751      return BigInt(accountAsset['balance']);2752    } else {2753      return null;2754    }2755  }2756}27572758class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2759  async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2760    await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2761  }2762}27632764class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2765  makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2766    const apiPrefix = 'api.tx.assetManager.';27672768    const registerTx = this.helper.constructApiCall(2769      apiPrefix + 'registerForeignAsset',2770      [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2771    );27722773    const setUnitsTx = this.helper.constructApiCall(2774      apiPrefix + 'setAssetUnitsPerSecond',2775      [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2776    );27772778    const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2779    const encodedProposal = batchCall?.method.toHex() || '';2780    return encodedProposal;2781  }27822783  async assetTypeId(location: any) {2784    return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2785  }2786}27872788class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2789  async notePreimage(signer: TSigner, encodedProposal: string) {2790    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2791  }27922793  externalProposeMajority(proposalHash: string) {2794    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2795  }27962797  fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2798    return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2799  }28002801  async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2802    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2803  }2804}28052806class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2807  collective: string;28082809  constructor(helper: MoonbeamHelper, collective: string) {2810    super(helper);28112812    this.collective = collective;2813  }28142815  async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2816    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2817  }28182819  async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2820    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2821  }28222823  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2824    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2825  }28262827  async proposalCount() {2828    return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2829  }2830}28312832export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2833export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;28342835export class UniqueHelper extends ChainHelperBase {2836  balance: BalanceGroup<UniqueHelper>;2837  collection: CollectionGroup;2838  nft: NFTGroup;2839  rft: RFTGroup;2840  ft: FTGroup;2841  staking: StakingGroup;2842  scheduler: SchedulerGroup;2843  collatorSelection: CollatorSelectionGroup;2844  foreignAssets: ForeignAssetsGroup;2845  xcm: XcmGroup<UniqueHelper>;2846  xTokens: XTokensGroup<UniqueHelper>;2847  tokens: TokensGroup<UniqueHelper>;28482849  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2850    super(logger, options.helperBase ?? UniqueHelper);28512852    this.balance = new BalanceGroup(this);2853    this.collection = new CollectionGroup(this);2854    this.nft = new NFTGroup(this);2855    this.rft = new RFTGroup(this);2856    this.ft = new FTGroup(this);2857    this.staking = new StakingGroup(this);2858    this.scheduler = new SchedulerGroup(this);2859    this.collatorSelection = new CollatorSelectionGroup(this);2860    this.foreignAssets = new ForeignAssetsGroup(this);2861    this.xcm = new XcmGroup(this, 'polkadotXcm');2862    this.xTokens = new XTokensGroup(this);2863    this.tokens = new TokensGroup(this);2864  }28652866  getSudo<T extends UniqueHelper>() {2867    // eslint-disable-next-line @typescript-eslint/naming-convention2868    const SudoHelperType = SudoHelper(this.helperBase);2869    return this.clone(SudoHelperType) as T;2870  }2871}28722873export class XcmChainHelper extends ChainHelperBase {2874  async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2875    const wsProvider = new WsProvider(wsEndpoint);2876    this.api = new ApiPromise({2877      provider: wsProvider,2878    });2879    await this.api.isReadyOrError;2880    this.network = await UniqueHelper.detectNetwork(this.api);2881  }2882}28832884export class RelayHelper extends XcmChainHelper {2885  xcm: XcmGroup<RelayHelper>;28862887  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2888    super(logger, options.helperBase ?? RelayHelper);28892890    this.xcm = new XcmGroup(this, 'xcmPallet');2891  }2892}28932894export class WestmintHelper extends XcmChainHelper {2895  balance: SubstrateBalanceGroup<WestmintHelper>;2896  xcm: XcmGroup<WestmintHelper>;2897  assets: AssetsGroup<WestmintHelper>;2898  xTokens: XTokensGroup<WestmintHelper>;28992900  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2901    super(logger, options.helperBase ?? WestmintHelper);29022903    this.balance = new SubstrateBalanceGroup(this);2904    this.xcm = new XcmGroup(this, 'polkadotXcm');2905    this.assets = new AssetsGroup(this);2906    this.xTokens = new XTokensGroup(this);2907  }2908}29092910export class MoonbeamHelper extends XcmChainHelper {2911  balance: EthereumBalanceGroup<MoonbeamHelper>;2912  assetManager: MoonbeamAssetManagerGroup;2913  assets: AssetsGroup<MoonbeamHelper>;2914  xTokens: XTokensGroup<MoonbeamHelper>;2915  democracy: MoonbeamDemocracyGroup;2916  collective: {2917    council: MoonbeamCollectiveGroup,2918    techCommittee: MoonbeamCollectiveGroup,2919  };29202921  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2922    super(logger, options.helperBase ?? MoonbeamHelper);29232924    this.balance = new EthereumBalanceGroup(this);2925    this.assetManager = new MoonbeamAssetManagerGroup(this);2926    this.assets = new AssetsGroup(this);2927    this.xTokens = new XTokensGroup(this);2928    this.democracy = new MoonbeamDemocracyGroup(this);2929    this.collective = {2930      council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2931      techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2932    };2933  }2934}29352936export class AcalaHelper extends XcmChainHelper {2937  balance: SubstrateBalanceGroup<AcalaHelper>;2938  assetRegistry: AcalaAssetRegistryGroup;2939  xTokens: XTokensGroup<AcalaHelper>;2940  tokens: TokensGroup<AcalaHelper>;29412942  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2943    super(logger, options.helperBase ?? AcalaHelper);29442945    this.balance = new SubstrateBalanceGroup(this);2946    this.assetRegistry = new AcalaAssetRegistryGroup(this);2947    this.xTokens = new XTokensGroup(this);2948    this.tokens = new TokensGroup(this);2949  }29502951  getSudo<T extends AcalaHelper>() {2952    // eslint-disable-next-line @typescript-eslint/naming-convention2953    const SudoHelperType = SudoHelper(this.helperBase);2954    return this.clone(SudoHelperType) as T;2955  }2956}29572958// eslint-disable-next-line @typescript-eslint/naming-convention2959function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2960  return class extends Base {2961    scheduleFn: 'schedule' | 'scheduleAfter';2962    blocksNum: number;2963    options: ISchedulerOptions;29642965    constructor(...args: any[]) {2966      const logger = args[0] as ILogger;2967      const options = args[1] as {2968        scheduleFn: 'schedule' | 'scheduleAfter',2969        blocksNum: number,2970        options: ISchedulerOptions2971      };29722973      super(logger);29742975      this.scheduleFn = options.scheduleFn;2976      this.blocksNum = options.blocksNum;2977      this.options = options.options;2978    }29792980    executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2981      const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2982      2983      const mandatorySchedArgs = [2984        this.blocksNum,2985        this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2986        this.options.priority ?? null,2987        scheduledTx,2988      ];2989      2990      let schedArgs;2991      let scheduleFn;29922993      if (this.options.scheduledId) {2994        schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];29952996        if (this.scheduleFn == 'schedule') {2997          scheduleFn = 'scheduleNamed';2998        } else if (this.scheduleFn == 'scheduleAfter') {2999          scheduleFn = 'scheduleNamedAfter';3000        }3001      } else {3002        schedArgs = mandatorySchedArgs;3003        scheduleFn = this.scheduleFn;3004      }30053006      const extrinsic = 'api.tx.scheduler.' +  scheduleFn;30073008      return super.executeExtrinsic(3009        sender,3010        extrinsic,3011        schedArgs,3012        expectSuccess,3013      );3014    }3015  };3016}30173018// eslint-disable-next-line @typescript-eslint/naming-convention3019function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {3020  return class extends Base {3021    constructor(...args: any[]) {3022      super(...args);3023    }30243025    async executeExtrinsic(3026      sender: IKeyringPair,3027      extrinsic: string,3028      params: any[],3029      expectSuccess?: boolean,3030      options: Partial<SignerOptions>|null = null,3031    ): Promise<ITransactionResult> {3032      const call = this.constructApiCall(extrinsic, params);3033      const result = await super.executeExtrinsic(3034        sender,3035        'api.tx.sudo.sudo',3036        [call],3037        expectSuccess,3038        options,3039      );30403041      if (result.status === 'Fail') return result;30423043      const data = this.eventHelper.extractEvents(result.result.events).find(x => x.section == 'sudo')?.data[0];3044      if (data.err) {3045        const error = data.err.module;3046        // todo:collator3047        const metaError = super.getApi()?.registry.findMetaError({index: new BN(error.index), error: new BN(9)});3048        throw new Error(`${data.err.module.error} ${metaError.section}.${metaError.name}`);3049      }3050      return result;3051    }3052  };3053}30543055export class UniqueBaseCollection {3056  helper: UniqueHelper;3057  collectionId: number;30583059  constructor(collectionId: number, uniqueHelper: UniqueHelper) {3060    this.collectionId = collectionId;3061    this.helper = uniqueHelper;3062  }30633064  async getData() {3065    return await this.helper.collection.getData(this.collectionId);3066  }30673068  async getLastTokenId() {3069    return await this.helper.collection.getLastTokenId(this.collectionId);3070  }30713072  async doesTokenExist(tokenId: number) {3073    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3074  }30753076  async getAdmins() {3077    return await this.helper.collection.getAdmins(this.collectionId);3078  }30793080  async getAllowList() {3081    return await this.helper.collection.getAllowList(this.collectionId);3082  }30833084  async getEffectiveLimits() {3085    return await this.helper.collection.getEffectiveLimits(this.collectionId);3086  }30873088  async getProperties(propertyKeys?: string[] | null) {3089    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3090  }30913092  async getPropertiesConsumedSpace() {3093    return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3094  }30953096  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3097    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3098  }30993100  async getOptions() {3101    return await this.helper.collection.getCollectionOptions(this.collectionId);3102  }31033104  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3105    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3106  }31073108  async confirmSponsorship(signer: TSigner) {3109    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3110  }31113112  async removeSponsor(signer: TSigner) {3113    return await this.helper.collection.removeSponsor(signer, this.collectionId);3114  }31153116  async setLimits(signer: TSigner, limits: ICollectionLimits) {3117    return await this.helper.collection.setLimits(signer, this.collectionId, limits);3118  }31193120  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3121    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3122  }31233124  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3125    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3126  }31273128  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3129    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3130  }31313132  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3133    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3134  }31353136  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3137    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3138  }31393140  async setProperties(signer: TSigner, properties: IProperty[]) {3141    return await this.helper.collection.setProperties(signer, this.collectionId, properties);3142  }31433144  async deleteProperties(signer: TSigner, propertyKeys: string[]) {3145    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3146  }31473148  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3149    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3150  }31513152  async enableNesting(signer: TSigner, permissions: INestingPermissions) {3153    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3154  }31553156  async disableNesting(signer: TSigner) {3157    return await this.helper.collection.disableNesting(signer, this.collectionId);3158  }31593160  async burn(signer: TSigner) {3161    return await this.helper.collection.burn(signer, this.collectionId);3162  }31633164  scheduleAt<T extends UniqueHelper>(3165    executionBlockNumber: number,3166    options: ISchedulerOptions = {},3167  ) {3168    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3169    return new UniqueBaseCollection(this.collectionId, scheduledHelper);3170  }31713172  scheduleAfter<T extends UniqueHelper>(3173    blocksBeforeExecution: number,3174    options: ISchedulerOptions = {},3175  ) {3176    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3177    return new UniqueBaseCollection(this.collectionId, scheduledHelper);3178  }31793180  getSudo<T extends UniqueHelper>() {3181    return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3182  }3183}318431853186export class UniqueNFTCollection extends UniqueBaseCollection {3187  getTokenObject(tokenId: number) {3188    return new UniqueNFToken(tokenId, this);3189  }31903191  async getTokensByAddress(addressObj: ICrossAccountId) {3192    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3193  }31943195  async getToken(tokenId: number, blockHashAt?: string) {3196    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3197  }31983199  async getTokenOwner(tokenId: number, blockHashAt?: string) {3200    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3201  }32023203  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3204    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3205  }32063207  async getTokenChildren(tokenId: number, blockHashAt?: string) {3208    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3209  }32103211  async getPropertyPermissions(propertyKeys: string[] | null = null) {3212    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3213  }32143215  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3216    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3217  }32183219  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3220    const api = this.helper.getApi();3221    const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();3222        3223    return (props! as any).consumedSpace;3224  }32253226  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3227    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3228  }32293230  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3231    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3232  }32333234  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3235    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3236  }32373238  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3239    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3240  }32413242  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3243    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3244  }32453246  async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3247    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3248  }32493250  async burnToken(signer: TSigner, tokenId: number) {3251    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3252  }32533254  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3255    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3256  }32573258  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3259    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3260  }32613262  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3263    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3264  }32653266  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3267    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3268  }32693270  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3271    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3272  }32733274  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3275    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3276  }32773278  scheduleAt<T extends UniqueHelper>(3279    executionBlockNumber: number,3280    options: ISchedulerOptions = {},3281  ) {3282    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3283    return new UniqueNFTCollection(this.collectionId, scheduledHelper);3284  }32853286  scheduleAfter<T extends UniqueHelper>(3287    blocksBeforeExecution: number,3288    options: ISchedulerOptions = {},3289  ) {3290    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3291    return new UniqueNFTCollection(this.collectionId, scheduledHelper);3292  }32933294  getSudo<T extends UniqueHelper>() {3295    return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3296  }3297}329832993300export class UniqueRFTCollection extends UniqueBaseCollection {3301  getTokenObject(tokenId: number) {3302    return new UniqueRFToken(tokenId, this);3303  }33043305  async getToken(tokenId: number, blockHashAt?: string) {3306    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3307  }33083309  async getTokensByAddress(addressObj: ICrossAccountId) {3310    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3311  }33123313  async getTop10TokenOwners(tokenId: number) {3314    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3315  }33163317  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3318    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3319  }33203321  async getTokenTotalPieces(tokenId: number) {3322    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3323  }33243325  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3326    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3327  }33283329  async getPropertyPermissions(propertyKeys: string[] | null = null) {3330    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3331  }33323333  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3334    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3335  }33363337  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3338    const api = this.helper.getApi();3339    const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();3340        3341    return (props! as any).consumedSpace;3342  }33433344  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3345    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3346  }33473348  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3349    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3350  }33513352  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3353    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3354  }33553356  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3357    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3358  }33593360  async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3361    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3362  }33633364  async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3365    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3366  }33673368  async burnToken(signer: TSigner, tokenId: number, amount=1n) {3369    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3370  }33713372  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId,  amount=1n) {3373    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3374  }33753376  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3377    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3378  }33793380  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3381    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3382  }33833384  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3385    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3386  }33873388  scheduleAt<T extends UniqueHelper>(3389    executionBlockNumber: number,3390    options: ISchedulerOptions = {},3391  ) {3392    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3393    return new UniqueRFTCollection(this.collectionId, scheduledHelper);3394  }33953396  scheduleAfter<T extends UniqueHelper>(3397    blocksBeforeExecution: number,3398    options: ISchedulerOptions = {},3399  ) {3400    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3401    return new UniqueRFTCollection(this.collectionId, scheduledHelper);3402  }34033404  getSudo<T extends UniqueHelper>() {3405    return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3406  }3407}340834093410export class UniqueFTCollection extends UniqueBaseCollection {3411  async getBalance(addressObj: ICrossAccountId) {3412    return await this.helper.ft.getBalance(this.collectionId, addressObj);3413  }34143415  async getTotalPieces() {3416    return await this.helper.ft.getTotalPieces(this.collectionId);3417  }34183419  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3420    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3421  }34223423  async getTop10Owners() {3424    return await this.helper.ft.getTop10Owners(this.collectionId);3425  }34263427  async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3428    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3429  }34303431  async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3432    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3433  }34343435  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3436    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3437  }34383439  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3440    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3441  }34423443  async burnTokens(signer: TSigner, amount=1n) {3444    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3445  }34463447  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3448    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3449  }34503451  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3452    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3453  }34543455  scheduleAt<T extends UniqueHelper>(3456    executionBlockNumber: number,3457    options: ISchedulerOptions = {},3458  ) {3459    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3460    return new UniqueFTCollection(this.collectionId, scheduledHelper);3461  }34623463  scheduleAfter<T extends UniqueHelper>(3464    blocksBeforeExecution: number,3465    options: ISchedulerOptions = {},3466  ) {3467    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3468    return new UniqueFTCollection(this.collectionId, scheduledHelper);3469  }34703471  getSudo<T extends UniqueHelper>() {3472    return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3473  }3474}347534763477export class UniqueBaseToken {3478  collection: UniqueNFTCollection | UniqueRFTCollection;3479  collectionId: number;3480  tokenId: number;34813482  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3483    this.collection = collection;3484    this.collectionId = collection.collectionId;3485    this.tokenId = tokenId;3486  }34873488  async getNextSponsored(addressObj: ICrossAccountId) {3489    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3490  }34913492  async getProperties(propertyKeys?: string[] | null) {3493    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3494  }34953496  async getTokenPropertiesConsumedSpace() {3497    return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3498  }34993500  async setProperties(signer: TSigner, properties: IProperty[]) {3501    return await this.collection.setTokenProperties(signer, this.tokenId, properties);3502  }35033504  async deleteProperties(signer: TSigner, propertyKeys: string[]) {3505    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3506  }35073508  async doesExist() {3509    return await this.collection.doesTokenExist(this.tokenId);3510  }35113512  nestingAccount() {3513    return this.collection.helper.util.getTokenAccount(this);3514  }35153516  scheduleAt<T extends UniqueHelper>(3517    executionBlockNumber: number,3518    options: ISchedulerOptions = {},3519  ) {3520    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3521    return new UniqueBaseToken(this.tokenId, scheduledCollection);3522  }35233524  scheduleAfter<T extends UniqueHelper>(3525    blocksBeforeExecution: number,3526    options: ISchedulerOptions = {},3527  ) {3528    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3529    return new UniqueBaseToken(this.tokenId, scheduledCollection);3530  }35313532  getSudo<T extends UniqueHelper>() {3533    return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3534  }3535}353635373538export class UniqueNFToken extends UniqueBaseToken {3539  collection: UniqueNFTCollection;35403541  constructor(tokenId: number, collection: UniqueNFTCollection) {3542    super(tokenId, collection);3543    this.collection = collection;3544  }35453546  async getData(blockHashAt?: string) {3547    return await this.collection.getToken(this.tokenId, blockHashAt);3548  }35493550  async getOwner(blockHashAt?: string) {3551    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3552  }35533554  async getTopmostOwner(blockHashAt?: string) {3555    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3556  }35573558  async getChildren(blockHashAt?: string) {3559    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3560  }35613562  async nest(signer: TSigner, toTokenObj: IToken) {3563    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3564  }35653566  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3567    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3568  }35693570  async transfer(signer: TSigner, addressObj: ICrossAccountId) {3571    return await this.collection.transferToken(signer, this.tokenId, addressObj);3572  }35733574  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3575    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3576  }35773578  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3579    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3580  }35813582  async isApproved(toAddressObj: ICrossAccountId) {3583    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3584  }35853586  async burn(signer: TSigner) {3587    return await this.collection.burnToken(signer, this.tokenId);3588  }35893590  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3591    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3592  }35933594  scheduleAt<T extends UniqueHelper>(3595    executionBlockNumber: number,3596    options: ISchedulerOptions = {},3597  ) {3598    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3599    return new UniqueNFToken(this.tokenId, scheduledCollection);3600  }36013602  scheduleAfter<T extends UniqueHelper>(3603    blocksBeforeExecution: number,3604    options: ISchedulerOptions = {},3605  ) {3606    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3607    return new UniqueNFToken(this.tokenId, scheduledCollection);3608  }36093610  getSudo<T extends UniqueHelper>() {3611    return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3612  }3613}36143615export class UniqueRFToken extends UniqueBaseToken {3616  collection: UniqueRFTCollection;36173618  constructor(tokenId: number, collection: UniqueRFTCollection) {3619    super(tokenId, collection);3620    this.collection = collection;3621  }36223623  async getData(blockHashAt?: string) {3624    return await this.collection.getToken(this.tokenId, blockHashAt);3625  }36263627  async getTop10Owners() {3628    return await this.collection.getTop10TokenOwners(this.tokenId);3629  }36303631  async getBalance(addressObj: ICrossAccountId) {3632    return await this.collection.getTokenBalance(this.tokenId, addressObj);3633  }36343635  async getTotalPieces() {3636    return await this.collection.getTokenTotalPieces(this.tokenId);3637  }36383639  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3640    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3641  }36423643  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3644    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3645  }36463647  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3648    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3649  }36503651  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3652    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3653  }36543655  async repartition(signer: TSigner, amount: bigint) {3656    return await this.collection.repartitionToken(signer, this.tokenId, amount);3657  }36583659  async burn(signer: TSigner, amount=1n) {3660    return await this.collection.burnToken(signer, this.tokenId, amount);3661  }36623663  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3664    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3665  }36663667  scheduleAt<T extends UniqueHelper>(3668    executionBlockNumber: number,3669    options: ISchedulerOptions = {},3670  ) {3671    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3672    return new UniqueRFToken(this.tokenId, scheduledCollection);3673  }36743675  scheduleAfter<T extends UniqueHelper>(3676    blocksBeforeExecution: number,3677    options: ISchedulerOptions = {},3678  ) {3679    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3680    return new UniqueRFToken(this.tokenId, scheduledCollection);3681  }36823683  getSudo<T extends UniqueHelper>() {3684    return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3685  }3686}
after · tests/src/util/playgrounds/unique.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable @typescript-eslint/no-var-requires */5/* eslint-disable function-call-argument-newline */6/* eslint-disable no-prototype-builtins */78import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {hexToU8a} from '@polkadot/util/hex';13import {u8aConcat} from '@polkadot/util/u8a';14import {15  IApiListeners,16  IBlock,17  IEvent,18  IChainProperties,19  ICollectionCreationOptions,20  ICollectionLimits,21  ICollectionPermissions,22  ICrossAccountId,23  ICrossAccountIdLower,24  ILogger,25  INestingPermissions,26  IProperty,27  IStakingInfo,28  ISchedulerOptions,29  ISubstrateBalance,30  IToken,31  ITokenPropertyPermission,32  ITransactionResult,33  IUniqueHelperLog,34  TApiAllowedListeners,35  TEthereumAccount,36  TSigner,37  TSubstrateAccount,38  TNetworks,39  IForeignAssetMetadata,40  AcalaAssetMetadata,41  MoonbeamAssetInfo,42  DemocracyStandardAccountVote,43  IEthCrossAccountId,44} from './types';45import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';46import type {Vec} from '@polkadot/types-codec';47import {FrameSystemEventRecord} from '@polkadot/types/lookup';48import {DevUniqueHelper} from './unique.dev';4950export class CrossAccountId implements ICrossAccountId {51  Substrate?: TSubstrateAccount;52  Ethereum?: TEthereumAccount;5354  constructor(account: ICrossAccountId) {55    if (account.Substrate) this.Substrate = account.Substrate;56    if (account.Ethereum) this.Ethereum = account.Ethereum;57  }5859  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {60    switch (domain) {61      case 'Substrate': return new CrossAccountId({Substrate: account.address});62      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();63    }64  }6566  static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {67    return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});68  }6970  static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {71    return encodeAddress(decodeAddress(address), ss58Format);72  }7374  static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {75    return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});76  }7778  withNormalizedSubstrate(ss58Format = 42): CrossAccountId {79    if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);80    return this;81  }8283  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {84    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));85  }8687  toEthereum(): CrossAccountId {88    if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});89    return this;90  }9192  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {93    return evmToAddress(address, ss58Format);94  }9596  toSubstrate(ss58Format?: number): CrossAccountId {97    if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});98    return this;99  }100101  toLowerCase(): CrossAccountId {102    if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();103    if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();104    return this;105  }106}107108const nesting = {109  toChecksumAddress(address: string): string {110    if (typeof address === 'undefined') return '';111112    if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);113114    address = address.toLowerCase().replace(/^0x/i,'');115    const addressHash = keccakAsHex(address).replace(/^0x/i,'');116    const checksumAddress = ['0x'];117118    for (let i = 0; i < address.length; i++) {119      // If ith character is 8 to f then make it uppercase120      if (parseInt(addressHash[i], 16) > 7) {121        checksumAddress.push(address[i].toUpperCase());122      } else {123        checksumAddress.push(address[i]);124      }125    }126    return checksumAddress.join('');127  },128  tokenIdToAddress(collectionId: number, tokenId: number) {129    return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);130  },131};132133class UniqueUtil {134  static transactionStatus = {135    NOT_READY: 'NotReady',136    FAIL: 'Fail',137    SUCCESS: 'Success',138  };139140  static chainLogType = {141    EXTRINSIC: 'extrinsic',142    RPC: 'rpc',143  };144145  static getTokenAccount(token: IToken): CrossAccountId {146    return new CrossAccountId({Ethereum: this.getTokenAddress(token)});147  }148149  static getTokenAddress(token: IToken): string {150    return nesting.tokenIdToAddress(token.collectionId, token.tokenId);151  }152153  static getDefaultLogger(): ILogger {154    return {155      log(msg: any, level = 'INFO') {156        console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));157      },158      level: {159        ERROR: 'ERROR',160        WARNING: 'WARNING',161        INFO: 'INFO',162      },163    };164  }165166  static vec2str(arr: string[] | number[]) {167    return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');168  }169170  static str2vec(string: string) {171    if (typeof string !== 'string') return string;172    return Array.from(string).map(x => x.charCodeAt(0));173  }174175  static fromSeed(seed: string, ss58Format = 42) {176    const keyring = new Keyring({type: 'sr25519', ss58Format});177    return keyring.addFromUri(seed);178  }179180  static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {181    if (creationResult.status !== this.transactionStatus.SUCCESS) {182      throw Error('Unable to create collection!');183    }184185    let collectionId = null;186    creationResult.result.events.forEach(({event: {data, method, section}}) => {187      if ((section === 'common') && (method === 'CollectionCreated')) {188        collectionId = parseInt(data[0].toString(), 10);189      }190    });191192    if (collectionId === null) {193      throw Error('No CollectionCreated event was found!');194    }195196    return collectionId;197  }198199  static extractTokensFromCreationResult(creationResult: ITransactionResult): {200    success: boolean,201    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],202  } {203    if (creationResult.status !== this.transactionStatus.SUCCESS) {204      throw Error('Unable to create tokens!');205    }206    let success = false;207    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];208    creationResult.result.events.forEach(({event: {data, method, section}}) => {209      if (method === 'ExtrinsicSuccess') {210        success = true;211      } else if ((section === 'common') && (method === 'ItemCreated')) {212        tokens.push({213          collectionId: parseInt(data[0].toString(), 10),214          tokenId: parseInt(data[1].toString(), 10),215          owner: data[2].toHuman(),216          amount: data[3].toBigInt(),217        });218      }219    });220    return {success, tokens};221  }222223  static extractTokensFromBurnResult(burnResult: ITransactionResult): {224    success: boolean,225    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],226  } {227    if (burnResult.status !== this.transactionStatus.SUCCESS) {228      throw Error('Unable to burn tokens!');229    }230    let success = false;231    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];232    burnResult.result.events.forEach(({event: {data, method, section}}) => {233      if (method === 'ExtrinsicSuccess') {234        success = true;235      } else if ((section === 'common') && (method === 'ItemDestroyed')) {236        tokens.push({237          collectionId: parseInt(data[0].toString(), 10),238          tokenId: parseInt(data[1].toString(), 10),239          owner: data[2].toHuman(),240          amount: data[3].toBigInt(),241        });242      }243    });244    return {success, tokens};245  }246247  static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {248    let eventId = null;249    events.forEach(({event: {data, method, section}}) => {250      if ((section === expectedSection) && (method === expectedMethod)) {251        eventId = parseInt(data[0].toString(), 10);252      }253    });254255    if (eventId === null) {256      throw Error(`No ${expectedMethod} event was found!`);257    }258    return eventId === collectionId;259  }260261  static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {262    const normalizeAddress = (address: string | ICrossAccountId) => {263      if(typeof address === 'string') return address;264      const obj = {} as any;265      Object.keys(address).forEach(k => {266        obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];267      });268      if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);269      if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();270      return address;271    };272    let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;273    events.forEach(({event: {data, method, section}}) => {274      if ((section === 'common') && (method === 'Transfer')) {275        const hData = (data as any).toJSON();276        transfer = {277          collectionId: hData[0],278          tokenId: hData[1],279          from: normalizeAddress(hData[2]),280          to: normalizeAddress(hData[3]),281          amount: BigInt(hData[4]),282        };283      }284    });285    let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;286    isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);287    isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);288    isSuccess = isSuccess && amount === transfer.amount;289    return isSuccess;290  }291292  static bigIntToDecimals(number: bigint, decimals = 18) {293    const numberStr = number.toString();294    const dotPos = numberStr.length - decimals;295296    if (dotPos <= 0) {297      return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;298    } else {299      const intPart = numberStr.substring(0, dotPos);300      const fractPart = numberStr.substring(dotPos);301      return intPart + '.' + fractPart;302    }303  }304}305306class UniqueEventHelper {307  private static extractIndex(index: any): [number, number] | string {308    if(index.toRawType() === '[u8;2]') return [index[0], index[1]];309    return index.toJSON();310  }311312  private static extractSub(data: any, subTypes: any): {[key: string]: any} {313    let obj: any = {};314    let index = 0;315316    if (data.entries) {317      for(const [key, value] of data.entries()) {318        obj[key] = this.extractData(value, subTypes[index]);319        index++;320      }321    } else obj = data.toJSON();322323    return obj;324  }325326  private static toHuman(data: any) {327    return data && data.toHuman ? data.toHuman() : `${data}`;328  }329330  private static extractData(data: any, type: any): any {331    if(!type) return this.toHuman(data);332    if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();333    if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();334    if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);335    return this.toHuman(data);336  }337338  public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {339    const parsedEvents: IEvent[] = [];340341    events.forEach((record) => {342      const {event, phase} = record;343      const types = event.typeDef;344345      const eventData: IEvent = {346        section: event.section.toString(),347        method: event.method.toString(),348        index: this.extractIndex(event.index),349        data: [],350        phase: phase.toJSON(),351      };352353      event.data.forEach((val: any, index: number) => {354        eventData.data.push(this.extractData(val, types[index]));355      });356357      parsedEvents.push(eventData);358    });359360    return parsedEvents;361  }362}363364export class ChainHelperBase {365  helperBase: any;366367  transactionStatus = UniqueUtil.transactionStatus;368  chainLogType = UniqueUtil.chainLogType;369  util: typeof UniqueUtil;370  eventHelper: typeof UniqueEventHelper;371  logger: ILogger;372  api: ApiPromise | null;373  forcedNetwork: TNetworks | null;374  network: TNetworks | null;375  chainLog: IUniqueHelperLog[];376  children: ChainHelperBase[];377  address: AddressGroup;378  chain: ChainGroup;379  session: SessionGroup;380381  constructor(logger?: ILogger, helperBase?: any) {382    this.helperBase = helperBase;383384    this.util = UniqueUtil;385    this.eventHelper = UniqueEventHelper;386    if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();387    this.logger = logger;388    this.api = null;389    this.forcedNetwork = null;390    this.network = null;391    this.chainLog = [];392    this.children = [];393    this.address = new AddressGroup(this);394    this.chain = new ChainGroup(this);395    this.session = new SessionGroup(this);396  }397398  clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {399    Object.setPrototypeOf(helperCls.prototype, this);400    const newHelper = new helperCls(this.logger, options);401402    newHelper.api = this.api;403    newHelper.network = this.network;404    newHelper.forceNetwork = this.forceNetwork;405406    this.children.push(newHelper);407408    return newHelper;409  }410411  getApi(): ApiPromise {412    if(this.api === null) throw Error('API not initialized');413    return this.api;414  }415416  async subscribeEvents(expectedEvents: {section: string, names: string[]}[]) {417    const collectedEvents: IEvent[] = [];418    const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {419      const ievents = this.eventHelper.extractEvents(events);420      ievents.forEach((event) => {421        expectedEvents.forEach((e => {422          if (event.section === e.section && e.names.includes(event.method)) {423            collectedEvents.push(event);424          }425        }));426      });427    });428    return {unsubscribe: unsubscribe as any, collectedEvents};429  }430431  clearChainLog(): void {432    this.chainLog = [];433  }434435  forceNetwork(value: TNetworks): void {436    this.forcedNetwork = value;437  }438439  async connect(wsEndpoint: string, listeners?: IApiListeners) {440    if (this.api !== null) throw Error('Already connected');441    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);442    this.api = api;443    this.network = network;444  }445446  async disconnect() {447    for (const child of this.children) {448      child.clearApi();449    }450451    if (this.api === null) return;452    await this.api.disconnect();453    this.clearApi();454  }455456  clearApi() {457    this.api = null;458    this.network = null;459  }460461  static async detectNetwork(api: ApiPromise): Promise<TNetworks> {462    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;463    const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];464465    if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;466467    if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;468    return 'opal';469  }470471  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {472    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});473    await api.isReady;474475    const network = await this.detectNetwork(api);476477    await api.disconnect();478479    return network;480  }481482  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{483    api: ApiPromise;484    network: TNetworks;485  }> {486    if(typeof network === 'undefined' || network === null) network = 'opal';487    const supportedRPC = {488      opal: {489        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,490      },491      quartz: {492        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,493      },494      unique: {495        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,496      },497      rococo: {},498      westend: {},499      moonbeam: {},500      moonriver: {},501      acala: {},502      karura: {},503      westmint: {},504    };505    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);506    const rpc = supportedRPC[network];507508    // TODO: investigate how to replace rpc in runtime509    // api._rpcCore.addUserInterfaces(rpc);510511    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});512513    await api.isReadyOrError;514515    if (typeof listeners === 'undefined') listeners = {};516    for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {517      if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;518      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);519    }520521    return {api, network};522  }523524  getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {525    const {events, status} = data;526    if (status.isReady) {527      return this.transactionStatus.NOT_READY;528    }529    if (status.isBroadcast) {530      return this.transactionStatus.NOT_READY;531    }532    if (status.isInBlock || status.isFinalized) {533      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');534      if (errors.length > 0) {535        return this.transactionStatus.FAIL;536      }537      if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {538        return this.transactionStatus.SUCCESS;539      }540    }541542    return this.transactionStatus.FAIL;543  }544545  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {546    const sign = (callback: any) => {547      if(options !== null) return transaction.signAndSend(sender, options, callback);548      return transaction.signAndSend(sender, callback);549    };550    // eslint-disable-next-line no-async-promise-executor551    return new Promise(async (resolve, reject) => {552      try {553        const unsub = await sign((result: any) => {554          const status = this.getTransactionStatus(result);555556          if (status === this.transactionStatus.SUCCESS) {557            this.logger.log(`${label} successful`);558            unsub();559            resolve({result, status});560          } else if (status === this.transactionStatus.FAIL) {561            let moduleError = null;562563            if (result.hasOwnProperty('dispatchError')) {564              const dispatchError = result['dispatchError'];565566              if (dispatchError) {567                if (dispatchError.isModule) {568                  const modErr = dispatchError.asModule;569                  const errorMeta = dispatchError.registry.findMetaError(modErr);570571                  moduleError = `${errorMeta.section}.${errorMeta.name}`;572                } else {573                  moduleError = dispatchError.toHuman();574                }575              } else {576                this.logger.log(result, this.logger.level.ERROR);577              }578            }579580            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);581            unsub();582            reject({status, moduleError, result});583          }584        });585      } catch (e) {586        this.logger.log(e, this.logger.level.ERROR);587        reject(e);588      }589    });590  }591592  async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {593    const api = this.getApi();594    const signingInfo = await api.derive.tx.signingInfo(signer.address);595596    // We need to sign the tx because597    // unsigned transactions does not have an inclusion fee598    tx.sign(signer, {599      blockHash: api.genesisHash,600      genesisHash: api.genesisHash,601      runtimeVersion: api.runtimeVersion,602      nonce: signingInfo.nonce,603    });604605    if (len === null) {606      return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;607    } else {608      return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;609    }610  }611612  constructApiCall(apiCall: string, params: any[]) {613    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);614    let call = this.getApi() as any;615    for(const part of apiCall.slice(4).split('.')) {616      call = call[part];617    }618    return call(...params);619  }620621  async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {622    if(this.api === null) throw Error('API not initialized');623    if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);624625    const startTime = (new Date()).getTime();626    let result: ITransactionResult;627    let events: IEvent[] = [];628    try {629      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;630      events = this.eventHelper.extractEvents(result.result.events);631    }632    catch(e) {633      if(!(e as object).hasOwnProperty('status')) throw e;634      result = e as ITransactionResult;635    }636637    const endTime = (new Date()).getTime();638639    const log = {640      executedAt: endTime,641      executionTime: endTime - startTime,642      type: this.chainLogType.EXTRINSIC,643      status: result.status,644      call: extrinsic,645      signer: this.getSignerAddress(sender),646      params,647    } as IUniqueHelperLog;648649    if(result.status !== this.transactionStatus.SUCCESS) {650      if (result.moduleError) log.moduleError = result.moduleError;651      else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;652    }653    if(events.length > 0) log.events = events;654655    this.chainLog.push(log);656657    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {658      if (result.moduleError) throw Error(`${result.moduleError}`);659      else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));660    }661    return result;662  }663664  async callRpc(rpc: string, params?: any[]) {665    if(typeof params === 'undefined') params = [];666    if(this.api === null) throw Error('API not initialized');667    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);668669    const startTime = (new Date()).getTime();670    let result;671    let error = null;672    const log = {673      type: this.chainLogType.RPC,674      call: rpc,675      params,676    } as IUniqueHelperLog;677678    try {679      result = await this.constructApiCall(rpc, params);680    }681    catch(e) {682      error = e;683    }684685    const endTime = (new Date()).getTime();686687    log.executedAt = endTime;688    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';689    log.executionTime = endTime - startTime;690691    this.chainLog.push(log);692693    if(error !== null) throw error;694695    return result;696  }697698  getSignerAddress(signer: IKeyringPair | string): string {699    if(typeof signer === 'string') return signer;700    return signer.address;701  }702703  fetchAllPalletNames(): string[] {704    if(this.api === null) throw Error('API not initialized');705    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());706  }707708  fetchMissingPalletNames(requiredPallets: string[]): string[] {709    const palletNames = this.fetchAllPalletNames();710    return requiredPallets.filter(p => !palletNames.includes(p));711  }712}713714715class HelperGroup<T extends ChainHelperBase> {716  helper: T;717718  constructor(uniqueHelper: T) {719    this.helper = uniqueHelper;720  }721}722723724class CollectionGroup extends HelperGroup<UniqueHelper> {725  /**726 * Get number of blocks when sponsored transaction is available.727 *728 * @param collectionId ID of collection729 * @param tokenId ID of token730 * @param addressObj address for which the sponsorship is checked731 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});732 * @returns number of blocks or null if sponsorship hasn't been set733 */734  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {735    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();736  }737738  /**739   * Get the number of created collections.740   *741   * @returns number of created collections742   */743  async getTotalCount(): Promise<number> {744    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();745  }746747  /**748   * Get information about the collection with additional data,749   * including the number of tokens it contains, its administrators,750   * the normalized address of the collection's owner, and decoded name and description.751   *752   * @param collectionId ID of collection753   * @example await getData(2)754   * @returns collection information object755   */756  async getData(collectionId: number): Promise<{757    id: number;758    name: string;759    description: string;760    tokensCount: number;761    admins: CrossAccountId[];762    normalizedOwner: TSubstrateAccount;763    raw: any764  } | null> {765    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);766    const humanCollection = collection.toHuman(), collectionData = {767      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],768      raw: humanCollection,769    } as any, jsonCollection = collection.toJSON();770    if (humanCollection === null) return null;771    collectionData.raw.limits = jsonCollection.limits;772    collectionData.raw.permissions = jsonCollection.permissions;773    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);774    for (const key of ['name', 'description']) {775      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);776    }777778    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))779      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)780      : 0;781    collectionData.admins = await this.getAdmins(collectionId);782783    return collectionData;784  }785786  /**787   * Get the addresses of the collection's administrators, optionally normalized.788   *789   * @param collectionId ID of collection790   * @param normalize whether to normalize the addresses to the default ss58 format791   * @example await getAdmins(1)792   * @returns array of administrators793   */794  async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {795    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();796797    return normalize798      ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())799      : admins;800  }801802  /**803   * Get the addresses added to the collection allow-list, optionally normalized.804   * @param collectionId ID of collection805   * @param normalize whether to normalize the addresses to the default ss58 format806   * @example await getAllowList(1)807   * @returns array of allow-listed addresses808   */809  async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {810    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();811    return normalize812      ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())813      : allowListed;814  }815816  /**817   * Get the effective limits of the collection instead of null for default values818   *819   * @param collectionId ID of collection820   * @example await getEffectiveLimits(2)821   * @returns object of collection limits822   */823  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {824    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();825  }826827  /**828   * Burns the collection if the signer has sufficient permissions and collection is empty.829   *830   * @param signer keyring of signer831   * @param collectionId ID of collection832   * @example await helper.collection.burn(aliceKeyring, 3);833   * @returns ```true``` if extrinsic success, otherwise ```false```834   */835  async burn(signer: TSigner, collectionId: number): Promise<boolean> {836    const result = await this.helper.executeExtrinsic(837      signer,838      'api.tx.unique.destroyCollection', [collectionId],839      true,840    );841842    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');843  }844845  /**846   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.847   *848   * @param signer keyring of signer849   * @param collectionId ID of collection850   * @param sponsorAddress Sponsor substrate address851   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")852   * @returns ```true``` if extrinsic success, otherwise ```false```853   */854  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {855    const result = await this.helper.executeExtrinsic(856      signer,857      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],858      true,859    );860861    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');862  }863864  /**865   * Confirms consent to sponsor the collection on behalf of the signer.866   *867   * @param signer keyring of signer868   * @param collectionId ID of collection869   * @example confirmSponsorship(aliceKeyring, 10)870   * @returns ```true``` if extrinsic success, otherwise ```false```871   */872  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {873    const result = await this.helper.executeExtrinsic(874      signer,875      'api.tx.unique.confirmSponsorship', [collectionId],876      true,877    );878879    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');880  }881882  /**883   * Removes the sponsor of a collection, regardless if it consented or not.884   *885   * @param signer keyring of signer886   * @param collectionId ID of collection887   * @example removeSponsor(aliceKeyring, 10)888   * @returns ```true``` if extrinsic success, otherwise ```false```889   */890  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {891    const result = await this.helper.executeExtrinsic(892      signer,893      'api.tx.unique.removeCollectionSponsor', [collectionId],894      true,895    );896897    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');898  }899900  /**901   * Sets the limits of the collection. At least one limit must be specified for a correct call.902   *903   * @param signer keyring of signer904   * @param collectionId ID of collection905   * @param limits collection limits object906   * @example907   * await setLimits(908   *   aliceKeyring,909   *   10,910   *   {911   *     sponsorTransferTimeout: 0,912   *     ownerCanDestroy: false913   *   }914   * )915   * @returns ```true``` if extrinsic success, otherwise ```false```916   */917  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {918    const result = await this.helper.executeExtrinsic(919      signer,920      'api.tx.unique.setCollectionLimits', [collectionId, limits],921      true,922    );923924    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');925  }926927  /**928   * Changes the owner of the collection to the new Substrate address.929   *930   * @param signer keyring of signer931   * @param collectionId ID of collection932   * @param ownerAddress substrate address of new owner933   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")934   * @returns ```true``` if extrinsic success, otherwise ```false```935   */936  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {937    const result = await this.helper.executeExtrinsic(938      signer,939      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],940      true,941    );942943    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');944  }945946  /**947   * Adds a collection administrator.948   *949   * @param signer keyring of signer950   * @param collectionId ID of collection951   * @param adminAddressObj Administrator address (substrate or ethereum)952   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})953   * @returns ```true``` if extrinsic success, otherwise ```false```954   */955  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {956    const result = await this.helper.executeExtrinsic(957      signer,958      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],959      true,960    );961962    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');963  }964965  /**966   * Removes a collection administrator.967   *968   * @param signer keyring of signer969   * @param collectionId ID of collection970   * @param adminAddressObj Administrator address (substrate or ethereum)971   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})972   * @returns ```true``` if extrinsic success, otherwise ```false```973   */974  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {975    const result = await this.helper.executeExtrinsic(976      signer,977      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],978      true,979    );980981    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');982  }983984  /**985   * Check if user is in allow list.986   *987   * @param collectionId ID of collection988   * @param user Account to check989   * @example await getAdmins(1)990   * @returns is user in allow list991   */992  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {993    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();994  }995996  /**997   * Adds an address to allow list998   * @param signer keyring of signer999   * @param collectionId ID of collection1000   * @param addressObj address to add to the allow list1001   * @returns ```true``` if extrinsic success, otherwise ```false```1002   */1003  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1004    const result = await this.helper.executeExtrinsic(1005      signer,1006      'api.tx.unique.addToAllowList', [collectionId, addressObj],1007      true,1008    );10091010    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1011  }10121013  /**1014   * Removes an address from allow list1015   *1016   * @param signer keyring of signer1017   * @param collectionId ID of collection1018   * @param addressObj address to remove from the allow list1019   * @returns ```true``` if extrinsic success, otherwise ```false```1020   */1021  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1022    const result = await this.helper.executeExtrinsic(1023      signer,1024      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1025      true,1026    );10271028    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1029  }10301031  /**1032   * Sets onchain permissions for selected collection.1033   *1034   * @param signer keyring of signer1035   * @param collectionId ID of collection1036   * @param permissions collection permissions object1037   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});1038   * @returns ```true``` if extrinsic success, otherwise ```false```1039   */1040  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1041    const result = await this.helper.executeExtrinsic(1042      signer,1043      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1044      true,1045    );10461047    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1048  }10491050  /**1051   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.1052   *1053   * @param signer keyring of signer1054   * @param collectionId ID of collection1055   * @param permissions nesting permissions object1056   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});1057   * @returns ```true``` if extrinsic success, otherwise ```false```1058   */1059  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1060    return await this.setPermissions(signer, collectionId, {nesting: permissions});1061  }10621063  /**1064   * Disables nesting for selected collection.1065   *1066   * @param signer keyring of signer1067   * @param collectionId ID of collection1068   * @example disableNesting(aliceKeyring, 10);1069   * @returns ```true``` if extrinsic success, otherwise ```false```1070   */1071  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1072    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1073  }10741075  /**1076   * Sets onchain properties to the collection.1077   *1078   * @param signer keyring of signer1079   * @param collectionId ID of collection1080   * @param properties array of property objects1081   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);1082   * @returns ```true``` if extrinsic success, otherwise ```false```1083   */1084  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1085    const result = await this.helper.executeExtrinsic(1086      signer,1087      'api.tx.unique.setCollectionProperties', [collectionId, properties],1088      true,1089    );10901091    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1092  }10931094  /**1095   * Get collection properties.1096   *1097   * @param collectionId ID of collection1098   * @param propertyKeys optionally filter the returned properties to only these keys1099   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);1100   * @returns array of key-value pairs1101   */1102  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1103    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1104  }11051106  async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1107    const api = this.helper.getApi();1108    const props = (await api.query.common.collectionProperties(collectionId)).toJSON();1109        1110    return (props! as any).consumedSpace;1111  }11121113  async getCollectionOptions(collectionId: number) {1114    return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1115  }11161117  /**1118   * Deletes onchain properties from the collection.1119   *1120   * @param signer keyring of signer1121   * @param collectionId ID of collection1122   * @param propertyKeys array of property keys to delete1123   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);1124   * @returns ```true``` if extrinsic success, otherwise ```false```1125   */1126  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1127    const result = await this.helper.executeExtrinsic(1128      signer,1129      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1130      true,1131    );11321133    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1134  }11351136  /**1137   * Changes the owner of the token.1138   *1139   * @param signer keyring of signer1140   * @param collectionId ID of collection1141   * @param tokenId ID of token1142   * @param addressObj address of a new owner1143   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1144   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1145   * @returns true if the token success, otherwise false1146   */1147  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1148    const result = await this.helper.executeExtrinsic(1149      signer,1150      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1151      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1152    );11531154    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1155  }11561157  /**1158   *1159   * Change ownership of a token(s) on behalf of the owner.1160   *1161   * @param signer keyring of signer1162   * @param collectionId ID of collection1163   * @param tokenId ID of token1164   * @param fromAddressObj address on behalf of which the token will be sent1165   * @param toAddressObj new token owner1166   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1167   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1168   * @returns true if the token success, otherwise false1169   */1170  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1171    const result = await this.helper.executeExtrinsic(1172      signer,1173      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1174      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1175    );1176    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1177  }11781179  /**1180   *1181   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1182   *1183   * @param signer keyring of signer1184   * @param collectionId ID of collection1185   * @param tokenId ID of token1186   * @param amount amount of tokens to be burned. For NFT must be set to 1n1187   * @example burnToken(aliceKeyring, 10, 5);1188   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1189   */1190  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1191    const burnResult = await this.helper.executeExtrinsic(1192      signer,1193      'api.tx.unique.burnItem', [collectionId, tokenId, amount],1194      true, // `Unable to burn token for ${label}`,1195    );1196    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1197    if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1198    return burnedTokens.success;1199  }12001201  /**1202   * Destroys a concrete instance of NFT on behalf of the owner1203   *1204   * @param signer keyring of signer1205   * @param collectionId ID of collection1206   * @param tokenId ID of token1207   * @param fromAddressObj address on behalf of which the token will be burnt1208   * @param amount amount of tokens to be burned. For NFT must be set to 1n1209   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1210   * @returns ```true``` if extrinsic success, otherwise ```false```1211   */1212  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1213    const burnResult = await this.helper.executeExtrinsic(1214      signer,1215      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1216      true, // `Unable to burn token from for ${label}`,1217    );1218    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1219    return burnedTokens.success && burnedTokens.tokens.length > 0;1220  }12211222  /**1223   * Set, change, or remove approved address to transfer the ownership of the NFT.1224   *1225   * @param signer keyring of signer1226   * @param collectionId ID of collection1227   * @param tokenId ID of token1228   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1229   * @param amount amount of token to be approved. For NFT must be set to 1n1230   * @returns ```true``` if extrinsic success, otherwise ```false```1231   */1232  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1233    const approveResult = await this.helper.executeExtrinsic(1234      signer,1235      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1236      true, // `Unable to approve token for ${label}`,1237    );12381239    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1240  }12411242  /**1243   * Get the amount of token pieces approved to transfer or burn. Normally 0.1244   *1245   * @param collectionId ID of collection1246   * @param tokenId ID of token1247   * @param toAccountObj address which is approved to use token pieces1248   * @param fromAccountObj address which may have allowed the use of its owned tokens1249   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1250   * @returns number of approved to transfer pieces1251   */1252  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1253    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1254  }12551256  /**1257   * Get the last created token ID in a collection1258   *1259   * @param collectionId ID of collection1260   * @example getLastTokenId(10);1261   * @returns id of the last created token1262   */1263  async getLastTokenId(collectionId: number): Promise<number> {1264    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1265  }12661267  /**1268   * Check if token exists1269   *1270   * @param collectionId ID of collection1271   * @param tokenId ID of token1272   * @example doesTokenExist(10, 20);1273   * @returns true if the token exists, otherwise false1274   */1275  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1276    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1277  }1278}12791280class NFTnRFT extends CollectionGroup {1281  /**1282   * Get tokens owned by account1283   *1284   * @param collectionId ID of collection1285   * @param addressObj tokens owner1286   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1287   * @returns array of token ids owned by account1288   */1289  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1290    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1291  }12921293  /**1294   * Get token data1295   *1296   * @param collectionId ID of collection1297   * @param tokenId ID of token1298   * @param propertyKeys optionally filter the token properties to only these keys1299   * @param blockHashAt optionally query the data at some block with this hash1300   * @example getToken(10, 5);1301   * @returns human readable token data1302   */1303  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1304    properties: IProperty[];1305    owner: CrossAccountId;1306    normalizedOwner: CrossAccountId;1307  }| null> {1308    let tokenData;1309    if(typeof blockHashAt === 'undefined') {1310      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1311    }1312    else {1313      if(propertyKeys.length == 0) {1314        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1315        if(!collection) return null;1316        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1317      }1318      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1319    }1320    tokenData = tokenData.toHuman();1321    if (tokenData === null || tokenData.owner === null) return null;1322    const owner = {} as any;1323    for (const key of Object.keys(tokenData.owner)) {1324      owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1325        ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1326        : tokenData.owner[key];1327    }1328    tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1329    return tokenData;1330  }13311332  /**1333   * Set permissions to change token properties1334   *1335   * @param signer keyring of signer1336   * @param collectionId ID of collection1337   * @param permissions permissions to change a property by the collection admin or token owner1338   * @example setTokenPropertyPermissions(1339   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1340   * )1341   * @returns true if extrinsic success otherwise false1342   */1343  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1344    const result = await this.helper.executeExtrinsic(1345      signer,1346      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1347      true,1348    );13491350    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1351  }13521353  /**1354   * Get token property permissions.1355   *1356   * @param collectionId ID of collection1357   * @param propertyKeys optionally filter the returned property permissions to only these keys1358   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1359   * @returns array of key-permission pairs1360   */1361  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1362    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1363  }13641365  /**1366   * Set token properties1367   *1368   * @param signer keyring of signer1369   * @param collectionId ID of collection1370   * @param tokenId ID of token1371   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1372   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1373   * @returns ```true``` if extrinsic success, otherwise ```false```1374   */1375  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1376    const result = await this.helper.executeExtrinsic(1377      signer,1378      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1379      true,1380    );13811382    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1383  }13841385  /**1386   * Get properties, metadata assigned to a token.1387   *1388   * @param collectionId ID of collection1389   * @param tokenId ID of token1390   * @param propertyKeys optionally filter the returned properties to only these keys1391   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1392   * @returns array of key-value pairs1393   */1394  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1395    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1396  }13971398  /**1399   * Delete the provided properties of a token1400   * @param signer keyring of signer1401   * @param collectionId ID of collection1402   * @param tokenId ID of token1403   * @param propertyKeys property keys to be deleted1404   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1405   * @returns ```true``` if extrinsic success, otherwise ```false```1406   */1407  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1408    const result = await this.helper.executeExtrinsic(1409      signer,1410      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1411      true,1412    );14131414    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1415  }14161417  /**1418   * Mint new collection1419   *1420   * @param signer keyring of signer1421   * @param collectionOptions basic collection options and properties1422   * @param mode NFT or RFT type of a collection1423   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1424   * @returns object of the created collection1425   */1426  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1427    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1428    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1429    for (const key of ['name', 'description', 'tokenPrefix']) {1430      if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1431    }1432    const creationResult = await this.helper.executeExtrinsic(1433      signer,1434      'api.tx.unique.createCollectionEx', [collectionOptions],1435      true, // errorLabel,1436    );1437    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1438  }14391440  getCollectionObject(_collectionId: number): any {1441    return null;1442  }14431444  getTokenObject(_collectionId: number, _tokenId: number): any {1445    return null;1446  }14471448  /**1449   * Tells whether the given `owner` approves the `operator`.1450   * @param collectionId ID of collection1451   * @param owner owner address1452   * @param operator operator addrees1453   * @returns true if operator is enabled1454   */1455  async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1456    return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1457  }14581459  /** Sets or unsets the approval of a given operator.1460   *  The `operator` is allowed to transfer all tokens of the `caller` on their behalf.1461   *  @param operator Operator1462   *  @param approved Should operator status be granted or revoked?1463   *  @returns ```true``` if extrinsic success, otherwise ```false```1464   */1465  async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1466    const result = await this.helper.executeExtrinsic(1467      signer,1468      'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1469      true,1470    );1471    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1472  }1473}147414751476class NFTGroup extends NFTnRFT {1477  /**1478   * Get collection object1479   * @param collectionId ID of collection1480   * @example getCollectionObject(2);1481   * @returns instance of UniqueNFTCollection1482   */1483  getCollectionObject(collectionId: number): UniqueNFTCollection {1484    return new UniqueNFTCollection(collectionId, this.helper);1485  }14861487  /**1488   * Get token object1489   * @param collectionId ID of collection1490   * @param tokenId ID of token1491   * @example getTokenObject(10, 5);1492   * @returns instance of UniqueNFTToken1493   */1494  getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1495    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1496  }14971498  /**1499   * Get token's owner1500   * @param collectionId ID of collection1501   * @param tokenId ID of token1502   * @param blockHashAt optionally query the data at the block with this hash1503   * @example getTokenOwner(10, 5);1504   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1505   */1506  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1507    let owner;1508    if (typeof blockHashAt === 'undefined') {1509      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1510    } else {1511      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1512    }1513    return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1514  }15151516  /**1517   * Is token approved to transfer1518   * @param collectionId ID of collection1519   * @param tokenId ID of token1520   * @param toAccountObj address to be approved1521   * @returns ```true``` if extrinsic success, otherwise ```false```1522   */1523  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1524    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1525  }15261527  /**1528   * Changes the owner of the token.1529   *1530   * @param signer keyring of signer1531   * @param collectionId ID of collection1532   * @param tokenId ID of token1533   * @param addressObj address of a new owner1534   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1535   * @returns ```true``` if extrinsic success, otherwise ```false```1536   */1537  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1538    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1539  }15401541  /**1542   *1543   * Change ownership of a NFT on behalf of the owner.1544   *1545   * @param signer keyring of signer1546   * @param collectionId ID of collection1547   * @param tokenId ID of token1548   * @param fromAddressObj address on behalf of which the token will be sent1549   * @param toAddressObj new token owner1550   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1551   * @returns ```true``` if extrinsic success, otherwise ```false```1552   */1553  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1554    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1555  }15561557  /**1558   * Recursively find the address that owns the token1559   * @param collectionId ID of collection1560   * @param tokenId ID of token1561   * @param blockHashAt1562   * @example getTokenTopmostOwner(10, 5);1563   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1564   */1565  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1566    let owner;1567    if (typeof blockHashAt === 'undefined') {1568      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1569    } else {1570      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1571    }15721573    if (owner === null) return null;15741575    return owner.toHuman();1576  }15771578  /**1579   * Get tokens nested in the provided token1580   * @param collectionId ID of collection1581   * @param tokenId ID of token1582   * @param blockHashAt optionally query the data at the block with this hash1583   * @example getTokenChildren(10, 5);1584   * @returns tokens whose depth of nesting is <= 51585   */1586  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1587    let children;1588    if(typeof blockHashAt === 'undefined') {1589      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1590    } else {1591      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1592    }15931594    return children.toJSON().map((x: any) => {1595      return {collectionId: x.collection, tokenId: x.token};1596    });1597  }15981599  /**1600   * Nest one token into another1601   * @param signer keyring of signer1602   * @param tokenObj token to be nested1603   * @param rootTokenObj token to be parent1604   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1605   * @returns ```true``` if extrinsic success, otherwise ```false```1606   */1607  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1608    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1609    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1610    if(!result) {1611      throw Error('Unable to nest token!');1612    }1613    return result;1614  }16151616  /**1617   * Remove token from nested state1618   * @param signer keyring of signer1619   * @param tokenObj token to unnest1620   * @param rootTokenObj parent of a token1621   * @param toAddressObj address of a new token owner1622   * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1623   * @returns ```true``` if extrinsic success, otherwise ```false```1624   */1625  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1626    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1627    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1628    if(!result) {1629      throw Error('Unable to unnest token!');1630    }1631    return result;1632  }16331634  /**1635   * Mint new collection1636   * @param signer keyring of signer1637   * @param collectionOptions Collection options1638   * @example1639   * mintCollection(aliceKeyring, {1640   *   name: 'New',1641   *   description: 'New collection',1642   *   tokenPrefix: 'NEW',1643   * })1644   * @returns object of the created collection1645   */1646  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1647    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1648  }16491650  /**1651   * Mint new token1652   * @param signer keyring of signer1653   * @param data token data1654   * @returns created token object1655   */1656  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1657    const creationResult = await this.helper.executeExtrinsic(1658      signer,1659      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1660        nft: {1661          properties: data.properties,1662        },1663      }],1664      true,1665    );1666    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1667    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1668    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1669    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1670  }16711672  /**1673   * Mint multiple NFT tokens1674   * @param signer keyring of signer1675   * @param collectionId ID of collection1676   * @param tokens array of tokens with owner and properties1677   * @example1678   * mintMultipleTokens(aliceKeyring, 10, [{1679   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1680   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1681   *   },{1682   *     owner: {Ethereum: "0x9F0583DbB855d..."},1683   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1684   * }]);1685   * @returns ```true``` if extrinsic success, otherwise ```false```1686   */1687  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1688    const creationResult = await this.helper.executeExtrinsic(1689      signer,1690      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1691      true,1692    );1693    const collection = this.getCollectionObject(collectionId);1694    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1695  }16961697  /**1698   * Mint multiple NFT tokens with one owner1699   * @param signer keyring of signer1700   * @param collectionId ID of collection1701   * @param owner tokens owner1702   * @param tokens array of tokens with owner and properties1703   * @example1704   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1705   *   properties: [{1706   *   key: "gender",1707   *   value: "female",1708   *  },{1709   *   key: "age",1710   *   value: "33",1711   *  }],1712   * }]);1713   * @returns array of newly created tokens1714   */1715  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1716    const rawTokens = [];1717    for (const token of tokens) {1718      const raw = {NFT: {properties: token.properties}};1719      rawTokens.push(raw);1720    }1721    const creationResult = await this.helper.executeExtrinsic(1722      signer,1723      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1724      true,1725    );1726    const collection = this.getCollectionObject(collectionId);1727    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1728  }17291730  /**1731   * Set, change, or remove approved address to transfer the ownership of the NFT.1732   *1733   * @param signer keyring of signer1734   * @param collectionId ID of collection1735   * @param tokenId ID of token1736   * @param toAddressObj address to approve1737   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1738   * @returns ```true``` if extrinsic success, otherwise ```false```1739   */1740  approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1741    return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1742  }1743}174417451746class RFTGroup extends NFTnRFT {1747  /**1748   * Get collection object1749   * @param collectionId ID of collection1750   * @example getCollectionObject(2);1751   * @returns instance of UniqueRFTCollection1752   */1753  getCollectionObject(collectionId: number): UniqueRFTCollection {1754    return new UniqueRFTCollection(collectionId, this.helper);1755  }17561757  /**1758   * Get token object1759   * @param collectionId ID of collection1760   * @param tokenId ID of token1761   * @example getTokenObject(10, 5);1762   * @returns instance of UniqueNFTToken1763   */1764  getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1765    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1766  }17671768  /**1769   * Get top 10 token owners with the largest number of pieces1770   * @param collectionId ID of collection1771   * @param tokenId ID of token1772   * @example getTokenTop10Owners(10, 5);1773   * @returns array of top 10 owners1774   */1775  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1776    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1777  }17781779  /**1780   * Get number of pieces owned by address1781   * @param collectionId ID of collection1782   * @param tokenId ID of token1783   * @param addressObj address token owner1784   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1785   * @returns number of pieces ownerd by address1786   */1787  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1788    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1789  }17901791  /**1792   * Transfer pieces of token to another address1793   * @param signer keyring of signer1794   * @param collectionId ID of collection1795   * @param tokenId ID of token1796   * @param addressObj address of a new owner1797   * @param amount number of pieces to be transfered1798   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1799   * @returns ```true``` if extrinsic success, otherwise ```false```1800   */1801  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1802    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1803  }18041805  /**1806   * Change ownership of some pieces of RFT on behalf of the owner.1807   * @param signer keyring of signer1808   * @param collectionId ID of collection1809   * @param tokenId ID of token1810   * @param fromAddressObj address on behalf of which the token will be sent1811   * @param toAddressObj new token owner1812   * @param amount number of pieces to be transfered1813   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1814   * @returns ```true``` if extrinsic success, otherwise ```false```1815   */1816  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1817    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1818  }18191820  /**1821   * Mint new collection1822   * @param signer keyring of signer1823   * @param collectionOptions Collection options1824   * @example1825   * mintCollection(aliceKeyring, {1826   *   name: 'New',1827   *   description: 'New collection',1828   *   tokenPrefix: 'NEW',1829   * })1830   * @returns object of the created collection1831   */1832  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1833    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1834  }18351836  /**1837   * Mint new token1838   * @param signer keyring of signer1839   * @param data token data1840   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1841   * @returns created token object1842   */1843  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1844    const creationResult = await this.helper.executeExtrinsic(1845      signer,1846      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1847        refungible: {1848          pieces: data.pieces,1849          properties: data.properties,1850        },1851      }],1852      true,1853    );1854    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1855    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1856    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1857    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1858  }18591860  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1861    throw Error('Not implemented');1862    const creationResult = await this.helper.executeExtrinsic(1863      signer,1864      'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1865      true, // `Unable to mint RFT tokens for ${label}`,1866    );1867    const collection = this.getCollectionObject(collectionId);1868    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1869  }18701871  /**1872   * Mint multiple RFT tokens with one owner1873   * @param signer keyring of signer1874   * @param collectionId ID of collection1875   * @param owner tokens owner1876   * @param tokens array of tokens with properties and pieces1877   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1878   * @returns array of newly created RFT tokens1879   */1880  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1881    const rawTokens = [];1882    for (const token of tokens) {1883      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1884      rawTokens.push(raw);1885    }1886    const creationResult = await this.helper.executeExtrinsic(1887      signer,1888      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1889      true,1890    );1891    const collection = this.getCollectionObject(collectionId);1892    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1893  }18941895  /**1896   * Destroys a concrete instance of RFT.1897   * @param signer keyring of signer1898   * @param collectionId ID of collection1899   * @param tokenId ID of token1900   * @param amount number of pieces to be burnt1901   * @example burnToken(aliceKeyring, 10, 5);1902   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1903   */1904  async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1905    return await super.burnToken(signer, collectionId, tokenId, amount);1906  }19071908  /**1909   * Destroys a concrete instance of RFT on behalf of the owner.1910   * @param signer keyring of signer1911   * @param collectionId ID of collection1912   * @param tokenId ID of token1913   * @param fromAddressObj address on behalf of which the token will be burnt1914   * @param amount number of pieces to be burnt1915   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1916   * @returns ```true``` if extrinsic success, otherwise ```false```1917   */1918  async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1919    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1920  }19211922  /**1923   * Set, change, or remove approved address to transfer the ownership of the RFT.1924   *1925   * @param signer keyring of signer1926   * @param collectionId ID of collection1927   * @param tokenId ID of token1928   * @param toAddressObj address to approve1929   * @param amount number of pieces to be approved1930   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1931   * @returns true if the token success, otherwise false1932   */1933  approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1934    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1935  }19361937  /**1938   * Get total number of pieces1939   * @param collectionId ID of collection1940   * @param tokenId ID of token1941   * @example getTokenTotalPieces(10, 5);1942   * @returns number of pieces1943   */1944  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1945    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1946  }19471948  /**1949   * Change number of token pieces. Signer must be the owner of all token pieces.1950   * @param signer keyring of signer1951   * @param collectionId ID of collection1952   * @param tokenId ID of token1953   * @param amount new number of pieces1954   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1955   * @returns true if the repartion was success, otherwise false1956   */1957  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1958    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1959    const repartitionResult = await this.helper.executeExtrinsic(1960      signer,1961      'api.tx.unique.repartition', [collectionId, tokenId, amount],1962      true,1963    );1964    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1965    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1966  }1967}196819691970class FTGroup extends CollectionGroup {1971  /**1972   * Get collection object1973   * @param collectionId ID of collection1974   * @example getCollectionObject(2);1975   * @returns instance of UniqueFTCollection1976   */1977  getCollectionObject(collectionId: number): UniqueFTCollection {1978    return new UniqueFTCollection(collectionId, this.helper);1979  }19801981  /**1982   * Mint new fungible collection1983   * @param signer keyring of signer1984   * @param collectionOptions Collection options1985   * @param decimalPoints number of token decimals1986   * @example1987   * mintCollection(aliceKeyring, {1988   *   name: 'New',1989   *   description: 'New collection',1990   *   tokenPrefix: 'NEW',1991   * }, 18)1992   * @returns newly created fungible collection1993   */1994  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1995    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1996    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1997    collectionOptions.mode = {fungible: decimalPoints};1998    for (const key of ['name', 'description', 'tokenPrefix']) {1999      if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);2000    }2001    const creationResult = await this.helper.executeExtrinsic(2002      signer,2003      'api.tx.unique.createCollectionEx', [collectionOptions],2004      true,2005    );2006    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2007  }20082009  /**2010   * Mint tokens2011   * @param signer keyring of signer2012   * @param collectionId ID of collection2013   * @param owner address owner of new tokens2014   * @param amount amount of tokens to be meanted2015   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);2016   * @returns ```true``` if extrinsic success, otherwise ```false```2017   */2018  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2019    const creationResult = await this.helper.executeExtrinsic(2020      signer,2021      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2022        fungible: {2023          value: amount,2024        },2025      }],2026      true, // `Unable to mint fungible tokens for ${label}`,2027    );2028    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2029  }20302031  /**2032   * Mint multiple Fungible tokens with one owner2033   * @param signer keyring of signer2034   * @param collectionId ID of collection2035   * @param owner tokens owner2036   * @param tokens array of tokens with properties and pieces2037   * @returns ```true``` if extrinsic success, otherwise ```false```2038   */2039  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {2040    const rawTokens = [];2041    for (const token of tokens) {2042      const raw = {Fungible: {Value: token.value}};2043      rawTokens.push(raw);2044    }2045    const creationResult = await this.helper.executeExtrinsic(2046      signer,2047      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2048      true,2049    );2050    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2051  }20522053  /**2054   * Get the top 10 owners with the largest balance for the Fungible collection2055   * @param collectionId ID of collection2056   * @example getTop10Owners(10);2057   * @returns array of ```ICrossAccountId```2058   */2059  async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2060    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2061  }20622063  /**2064   * Get account balance2065   * @param collectionId ID of collection2066   * @param addressObj address of owner2067   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})2068   * @returns amount of fungible tokens owned by address2069   */2070  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2071    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2072  }20732074  /**2075   * Transfer tokens to address2076   * @param signer keyring of signer2077   * @param collectionId ID of collection2078   * @param toAddressObj address recipient2079   * @param amount amount of tokens to be sent2080   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2081   * @returns ```true``` if extrinsic success, otherwise ```false```2082   */2083  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2084    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2085  }20862087  /**2088   * Transfer some tokens on behalf of the owner.2089   * @param signer keyring of signer2090   * @param collectionId ID of collection2091   * @param fromAddressObj address on behalf of which tokens will be sent2092   * @param toAddressObj address where token to be sent2093   * @param amount number of tokens to be sent2094   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);2095   * @returns ```true``` if extrinsic success, otherwise ```false```2096   */2097  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2098    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2099  }21002101  /**2102   * Destroy some amount of tokens2103   * @param signer keyring of signer2104   * @param collectionId ID of collection2105   * @param amount amount of tokens to be destroyed2106   * @example burnTokens(aliceKeyring, 10, 1000n);2107   * @returns ```true``` if extrinsic success, otherwise ```false```2108   */2109  async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2110    return await super.burnToken(signer, collectionId, 0, amount);2111  }21122113  /**2114   * Burn some tokens on behalf of the owner.2115   * @param signer keyring of signer2116   * @param collectionId ID of collection2117   * @param fromAddressObj address on behalf of which tokens will be burnt2118   * @param amount amount of tokens to be burnt2119   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2120   * @returns ```true``` if extrinsic success, otherwise ```false```2121   */2122  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2123    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2124  }21252126  /**2127   * Get total collection supply2128   * @param collectionId2129   * @returns2130   */2131  async getTotalPieces(collectionId: number): Promise<bigint> {2132    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2133  }21342135  /**2136   * Set, change, or remove approved address to transfer tokens.2137   *2138   * @param signer keyring of signer2139   * @param collectionId ID of collection2140   * @param toAddressObj address to be approved2141   * @param amount amount of tokens to be approved2142   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)2143   * @returns ```true``` if extrinsic success, otherwise ```false```2144   */2145  approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2146    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2147  }21482149  /**2150   * Get amount of fungible tokens approved to transfer2151   * @param collectionId ID of collection2152   * @param fromAddressObj owner of tokens2153   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner2154   * @returns number of tokens approved for the transfer2155   */2156  getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2157    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2158  }2159}216021612162class ChainGroup extends HelperGroup<ChainHelperBase> {2163  /**2164   * Get system properties of a chain2165   * @example getChainProperties();2166   * @returns ss58Format, token decimals, and token symbol2167   */2168  getChainProperties(): IChainProperties {2169    const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2170    return {2171      ss58Format: properties.ss58Format.toJSON(),2172      tokenDecimals: properties.tokenDecimals.toJSON(),2173      tokenSymbol: properties.tokenSymbol.toJSON(),2174    };2175  }21762177  /**2178   * Get chain header2179   * @example getLatestBlockNumber();2180   * @returns the number of the last block2181   */2182  async getLatestBlockNumber(): Promise<number> {2183    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2184  }21852186  /**2187   * Get block hash by block number2188   * @param blockNumber number of block2189   * @example getBlockHashByNumber(12345);2190   * @returns hash of a block2191   */2192  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2193    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2194    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2195    return blockHash;2196  }21972198  // TODO add docs2199  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2200    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2201    if (!blockHash) return null;2202    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2203  }22042205  /**2206   * Get account nonce2207   * @param address substrate address2208   * @example getNonce("5GrwvaEF5zXb26Fz...");2209   * @returns number, account's nonce2210   */2211  async getNonce(address: TSubstrateAccount): Promise<number> {2212    return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2213  }2214}22152216class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2217  /**2218 * Get substrate address balance2219 * @param address substrate address2220 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2221 * @returns amount of tokens on address2222 */2223  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2224    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2225  }22262227  /**2228   * Transfer tokens to substrate address2229   * @param signer keyring of signer2230   * @param address substrate address of a recipient2231   * @param amount amount of tokens to be transfered2232   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2233   * @returns ```true``` if extrinsic success, otherwise ```false```2234   */2235  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2236    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);22372238    let transfer = {from: null, to: null, amount: 0n} as any;2239    result.result.events.forEach(({event: {data, method, section}}) => {2240      if ((section === 'balances') && (method === 'Transfer')) {2241        transfer = {2242          from: this.helper.address.normalizeSubstrate(data[0]),2243          to: this.helper.address.normalizeSubstrate(data[1]),2244          amount: BigInt(data[2]),2245        };2246      }2247    });2248    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2249      && this.helper.address.normalizeSubstrate(address) === transfer.to2250      && BigInt(amount) === transfer.amount;2251    return isSuccess;2252  }22532254  /**2255   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2256   * @param address substrate address2257   * @returns2258   */2259  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2260    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2261    return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2262  }2263}22642265class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2266  /**2267   * Get ethereum address balance2268   * @param address ethereum address2269   * @example getEthereum("0x9F0583DbB855d...")2270   * @returns amount of tokens on address2271   */2272  async getEthereum(address: TEthereumAccount): Promise<bigint> {2273    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2274  }22752276  /**2277   * Transfer tokens to address2278   * @param signer keyring of signer2279   * @param address Ethereum address of a recipient2280   * @param amount amount of tokens to be transfered2281   * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2282   * @returns ```true``` if extrinsic success, otherwise ```false```2283   */2284  async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2285    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22862287    let transfer = {from: null, to: null, amount: 0n} as any;2288    result.result.events.forEach(({event: {data, method, section}}) => {2289      if ((section === 'balances') && (method === 'Transfer')) {2290        transfer = {2291          from: data[0].toString(),2292          to: data[1].toString(),2293          amount: BigInt(data[2]),2294        };2295      }2296    });2297    const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2298      && address === transfer.to2299      && BigInt(amount) === transfer.amount;2300    return isSuccess;2301  }2302}23032304class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2305  subBalanceGroup: SubstrateBalanceGroup<T>;2306  ethBalanceGroup: EthereumBalanceGroup<T>;23072308  constructor(helper: T) {2309    super(helper);2310    this.subBalanceGroup = new SubstrateBalanceGroup(helper);2311    this.ethBalanceGroup = new EthereumBalanceGroup(helper);2312  }23132314  getCollectionCreationPrice(): bigint {2315    return 2n * this.getOneTokenNominal();2316  }2317  /**2318   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2319   * @example getOneTokenNominal()2320   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2321   */2322  getOneTokenNominal(): bigint {2323    const chainProperties = this.helper.chain.getChainProperties();2324    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2325  }23262327  /**2328   * Get substrate address balance2329   * @param address substrate address2330   * @example getSubstrate("5GrwvaEF5zXb26Fz...")2331   * @returns amount of tokens on address2332   */2333  getSubstrate(address: TSubstrateAccount): Promise<bigint> {2334    return this.subBalanceGroup.getSubstrate(address);2335  }23362337  /**2338   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2339   * @param address substrate address2340   * @returns2341   */2342  getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2343    return this.subBalanceGroup.getSubstrateFull(address);2344  }23452346  /**2347   * Get ethereum address balance2348   * @param address ethereum address2349   * @example getEthereum("0x9F0583DbB855d...")2350   * @returns amount of tokens on address2351   */2352  getEthereum(address: TEthereumAccount): Promise<bigint> {2353    return this.ethBalanceGroup.getEthereum(address);2354  }23552356  /**2357   * Transfer tokens to substrate address2358   * @param signer keyring of signer2359   * @param address substrate address of a recipient2360   * @param amount amount of tokens to be transfered2361   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2362   * @returns ```true``` if extrinsic success, otherwise ```false```2363   */2364  transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2365    return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2366  }23672368  async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2369    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23702371    let transfer = {from: null, to: null, amount: 0n} as any;2372    result.result.events.forEach(({event: {data, method, section}}) => {2373      if ((section === 'balances') && (method === 'Transfer')) {2374        transfer = {2375          from: this.helper.address.normalizeSubstrate(data[0]),2376          to: this.helper.address.normalizeSubstrate(data[1]),2377          amount: BigInt(data[2]),2378        };2379      }2380    });2381    let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2382    isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2383    isSuccess = isSuccess && BigInt(amount) === transfer.amount;2384    return isSuccess;2385  }2386}23872388class AddressGroup extends HelperGroup<ChainHelperBase> {2389  /**2390   * Normalizes the address to the specified ss58 format, by default ```42```.2391   * @param address substrate address2392   * @param ss58Format format for address conversion, by default ```42```2393   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2394   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2395   */2396  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2397    return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2398  }23992400  /**2401   * Get address in the connected chain format2402   * @param address substrate address2403   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2404   * @returns address in chain format2405   */2406  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2407    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2408  }24092410  /**2411   * Get substrate mirror of an ethereum address2412   * @param ethAddress ethereum address2413   * @param toChainFormat false for normalized account2414   * @example ethToSubstrate('0x9F0583DbB855d...')2415   * @returns substrate mirror of a provided ethereum address2416   */2417  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2418    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2419  }24202421  /**2422   * Get ethereum mirror of a substrate address2423   * @param subAddress substrate account2424   * @example substrateToEth("5DnSF6RRjwteE3BrC...")2425   * @returns ethereum mirror of a provided substrate address2426   */2427  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2428    return CrossAccountId.translateSubToEth(subAddress);2429  }24302431  /**2432   * Encode key to substrate address2433   * @param key key for encoding address2434   * @param ss58Format prefix for encoding to the address of the corresponding network2435   * @returns encoded substrate address2436   */2437  encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2438    const u8a :Uint8Array = typeof key === 'string'2439      ? hexToU8a(key)2440      : typeof key === 'bigint'2441        ? hexToU8a(key.toString(16))2442        : key;2443  2444    if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2445      throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2446    }2447  2448    const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2449    if (!allowedDecodedLengths.includes(u8a.length)) {2450      throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2451    }2452  2453    const u8aPrefix = ss58Format < 642454      ? new Uint8Array([ss58Format])2455      : new Uint8Array([2456        ((ss58Format & 0xfc) >> 2) | 0x40,2457        (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2458      ]);24592460    const input = u8aConcat(u8aPrefix, u8a);2461  2462    return base58Encode(u8aConcat(2463      input,2464      blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2465    ));2466  }24672468  /**2469   * Restore substrate address from bigint representation2470   * @param number decimal representation of substrate address2471   * @returns substrate address2472   */2473  restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2474    if (this.helper.api === null) {2475      throw 'Not connected';2476    }2477    const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2478    if (res === undefined || res === null) {2479      throw 'Restore address error';2480    }2481    return res.toString();2482  }24832484  /**2485   * Convert etherium cross account id to substrate cross account id2486   * @param ethCrossAccount etherium cross account2487   * @returns substrate cross account id2488   */2489  convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2490    if (ethCrossAccount.sub === '0') {2491      return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2492    }2493    2494    const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2495    return {Substrate: ss58};2496  }24972498  paraSiblingSovereignAccount(paraid: number) {2499    // We are getting a *sibling* parachain sovereign account,2500    // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c2501    const siblingPrefix = '0x7369626c';25022503    const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2504    const suffix = '000000000000000000000000000000000000000000000000';25052506    return siblingPrefix + encodedParaId + suffix;2507  }2508}25092510class StakingGroup extends HelperGroup<UniqueHelper> {2511  /**2512   * Stake tokens for App Promotion2513   * @param signer keyring of signer2514   * @param amountToStake amount of tokens to stake2515   * @param label extra label for log2516   * @returns2517   */2518  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2519    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2520    const _stakeResult = await this.helper.executeExtrinsic(2521      signer, 'api.tx.appPromotion.stake',2522      [amountToStake], true,2523    );2524    // TODO extract info from stakeResult2525    return true;2526  }25272528  /**2529   * Unstake tokens for App Promotion2530   * @param signer keyring of signer2531   * @param amountToUnstake amount of tokens to unstake2532   * @param label extra label for log2533   * @returns block number where balances will be unlocked2534   */2535  async unstake(signer: TSigner, label?: string): Promise<number> {2536    if(typeof label === 'undefined') label = `${signer.address}`;2537    const _unstakeResult = await this.helper.executeExtrinsic(2538      signer, 'api.tx.appPromotion.unstake',2539      [], true,2540    );2541    // TODO extract block number fron events2542    return 1;2543  }25442545  /**2546   * Get total staked amount for address2547   * @param address substrate or ethereum address2548   * @returns total staked amount2549   */2550  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2551    if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2552    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2553  }25542555  /**2556   * Get total staked per block2557   * @param address substrate or ethereum address2558   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2559   */2560  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2561    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2562    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2563      return {2564        block: block.toBigInt(),2565        amount: amount.toBigInt(),2566      };2567    });2568  }25692570  /**2571   * Get total pending unstake amount for address2572   * @param address substrate or ethereum address2573   * @returns total pending unstake amount2574   */2575  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2576    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2577  }25782579  /**2580   * Get pending unstake amount per block for address2581   * @param address substrate or ethereum address2582   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block2583   */2584  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2585    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2586    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2587      return {2588        block: block.toBigInt(),2589        amount: amount.toBigInt(),2590      };2591    });2592    return result;2593  }2594}25952596class SchedulerGroup extends HelperGroup<UniqueHelper> {2597  constructor(helper: UniqueHelper) {2598    super(helper);2599  }26002601  cancelScheduled(signer: TSigner, scheduledId: string) {2602    return this.helper.executeExtrinsic(2603      signer,2604      'api.tx.scheduler.cancelNamed',2605      [scheduledId],2606      true,2607    );2608  }26092610  changePriority(signer: TSigner, scheduledId: string, priority: number) {2611    return this.helper.executeExtrinsic(2612      signer,2613      'api.tx.scheduler.changeNamedPriority',2614      [scheduledId, priority],2615      true,2616    );2617  }26182619  scheduleAt<T extends UniqueHelper>(2620    executionBlockNumber: number,2621    options: ISchedulerOptions = {},2622  ) {2623    return this.schedule<T>('schedule', executionBlockNumber, options);2624  }26252626  scheduleAfter<T extends UniqueHelper>(2627    blocksBeforeExecution: number,2628    options: ISchedulerOptions = {},2629  ) {2630    return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2631  }26322633  schedule<T extends UniqueHelper>(2634    scheduleFn: 'schedule' | 'scheduleAfter',2635    blocksNum: number,2636    options: ISchedulerOptions = {},2637  ) {2638    // eslint-disable-next-line @typescript-eslint/naming-convention2639    const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2640    return this.helper.clone(ScheduledHelperType, {2641      scheduleFn,2642      blocksNum,2643      options,2644    }) as T;2645  }2646}26472648class SessionGroup extends HelperGroup<ChainHelperBase> {2649  //todo:collator documentation2650  async getIndex(): Promise<number> {2651    return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();2652  }26532654  newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {2655    return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);2656  }26572658  setOwnKeys(signer: TSigner, key: string) {2659    return this.helper.executeExtrinsic(2660      signer,2661      'api.tx.session.setKeys', 2662      [key, '0x0'],2663      true,2664    );2665  }26662667  setOwnKeysFromAddress(signer: TSigner) {2668    return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));2669  }2670}26712672class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2673  //todo:collator documentation2674  addInvulnerable(signer: TSigner, address: string) {2675    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);2676  }26772678  removeInvulnerable(signer: TSigner, address: string) {2679    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);2680  }26812682  async getInvulnerables(): Promise<string[]> {2683    return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2684  }26852686  setLicenseBond(signer: TSigner, amount: bigint) {2687    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.setLicenseBond', [amount]);2688  }26892690  async getLicenseBond(): Promise<bigint> {2691    return (await this.helper.callRpc('api.query.collatorSelection.licenseBond')).toBigInt();2692  }26932694  obtainLicense(signer: TSigner) {2695    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);2696  }26972698  releaseLicense(signer: TSigner) {2699    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);2700  }27012702  forceRevokeLicense(signer: TSigner, released: string) {2703    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceRevokeLicense', [released]);2704  }27052706  async hasLicense(address: string): Promise<bigint> {2707    return (await this.helper.callRpc('api.query.collatorSelection.licenses', [address])).toBigInt();2708  }27092710  onboard(signer: TSigner) {2711    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);2712  }27132714  offboard(signer: TSigner) {2715    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);2716  }27172718  async getCandidates(): Promise<string[]> {2719    return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());2720  }2721}27222723class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2724  async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2725    await this.helper.executeExtrinsic(2726      signer,2727      'api.tx.foreignAssets.registerForeignAsset',2728      [ownerAddress, location, metadata],2729      true,2730    );2731  }27322733  async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2734    await this.helper.executeExtrinsic(2735      signer,2736      'api.tx.foreignAssets.updateForeignAsset',2737      [foreignAssetId, location, metadata],2738      true,2739    );2740  }2741}27422743class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2744  palletName: string;27452746  constructor(helper: T, palletName: string) {2747    super(helper);27482749    this.palletName = palletName;2750  }27512752  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2753    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2754  }2755}27562757class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2758  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2759    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2760  }27612762  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2763    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2764  }27652766  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2767    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2768  }2769}27702771class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2772  async accounts(address: string, currencyId: any) {2773    const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2774    return BigInt(free);2775  }2776}27772778class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2779  async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2780    await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2781  }27822783  async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2784    await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2785  }27862787  async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2788    await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2789  }27902791  async account(assetId: string | number, address: string) {2792    const accountAsset = (2793      await this.helper.callRpc('api.query.assets.account', [assetId, address])2794    ).toJSON()! as any;27952796    if (accountAsset !== null) {2797      return BigInt(accountAsset['balance']);2798    } else {2799      return null;2800    }2801  }2802}28032804class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2805  async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2806    await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2807  }2808}28092810class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2811  makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2812    const apiPrefix = 'api.tx.assetManager.';28132814    const registerTx = this.helper.constructApiCall(2815      apiPrefix + 'registerForeignAsset',2816      [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2817    );28182819    const setUnitsTx = this.helper.constructApiCall(2820      apiPrefix + 'setAssetUnitsPerSecond',2821      [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2822    );28232824    const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2825    const encodedProposal = batchCall?.method.toHex() || '';2826    return encodedProposal;2827  }28282829  async assetTypeId(location: any) {2830    return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2831  }2832}28332834class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2835  async notePreimage(signer: TSigner, encodedProposal: string) {2836    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2837  }28382839  externalProposeMajority(proposalHash: string) {2840    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2841  }28422843  fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2844    return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2845  }28462847  async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2848    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2849  }2850}28512852class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2853  collective: string;28542855  constructor(helper: MoonbeamHelper, collective: string) {2856    super(helper);28572858    this.collective = collective;2859  }28602861  async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2862    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2863  }28642865  async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2866    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2867  }28682869  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2870    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2871  }28722873  async proposalCount() {2874    return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2875  }2876}28772878export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2879export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;28802881export class UniqueHelper extends ChainHelperBase {2882  balance: BalanceGroup<UniqueHelper>;2883  collection: CollectionGroup;2884  nft: NFTGroup;2885  rft: RFTGroup;2886  ft: FTGroup;2887  staking: StakingGroup;2888  scheduler: SchedulerGroup;2889  collatorSelection: CollatorSelectionGroup;2890  foreignAssets: ForeignAssetsGroup;2891  xcm: XcmGroup<UniqueHelper>;2892  xTokens: XTokensGroup<UniqueHelper>;2893  tokens: TokensGroup<UniqueHelper>;28942895  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2896    super(logger, options.helperBase ?? UniqueHelper);28972898    this.balance = new BalanceGroup(this);2899    this.collection = new CollectionGroup(this);2900    this.nft = new NFTGroup(this);2901    this.rft = new RFTGroup(this);2902    this.ft = new FTGroup(this);2903    this.staking = new StakingGroup(this);2904    this.scheduler = new SchedulerGroup(this);2905    this.collatorSelection = new CollatorSelectionGroup(this);2906    this.foreignAssets = new ForeignAssetsGroup(this);2907    this.xcm = new XcmGroup(this, 'polkadotXcm');2908    this.xTokens = new XTokensGroup(this);2909    this.tokens = new TokensGroup(this);2910  }29112912  getSudo<T extends UniqueHelper>() {2913    // eslint-disable-next-line @typescript-eslint/naming-convention2914    const SudoHelperType = SudoHelper(this.helperBase);2915    return this.clone(SudoHelperType) as T;2916  }2917}29182919export class XcmChainHelper extends ChainHelperBase {2920  async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2921    const wsProvider = new WsProvider(wsEndpoint);2922    this.api = new ApiPromise({2923      provider: wsProvider,2924    });2925    await this.api.isReadyOrError;2926    this.network = await UniqueHelper.detectNetwork(this.api);2927  }2928}29292930export class RelayHelper extends XcmChainHelper {2931  xcm: XcmGroup<RelayHelper>;29322933  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2934    super(logger, options.helperBase ?? RelayHelper);29352936    this.xcm = new XcmGroup(this, 'xcmPallet');2937  }2938}29392940export class WestmintHelper extends XcmChainHelper {2941  balance: SubstrateBalanceGroup<WestmintHelper>;2942  xcm: XcmGroup<WestmintHelper>;2943  assets: AssetsGroup<WestmintHelper>;2944  xTokens: XTokensGroup<WestmintHelper>;29452946  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2947    super(logger, options.helperBase ?? WestmintHelper);29482949    this.balance = new SubstrateBalanceGroup(this);2950    this.xcm = new XcmGroup(this, 'polkadotXcm');2951    this.assets = new AssetsGroup(this);2952    this.xTokens = new XTokensGroup(this);2953  }2954}29552956export class MoonbeamHelper extends XcmChainHelper {2957  balance: EthereumBalanceGroup<MoonbeamHelper>;2958  assetManager: MoonbeamAssetManagerGroup;2959  assets: AssetsGroup<MoonbeamHelper>;2960  xTokens: XTokensGroup<MoonbeamHelper>;2961  democracy: MoonbeamDemocracyGroup;2962  collective: {2963    council: MoonbeamCollectiveGroup,2964    techCommittee: MoonbeamCollectiveGroup,2965  };29662967  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2968    super(logger, options.helperBase ?? MoonbeamHelper);29692970    this.balance = new EthereumBalanceGroup(this);2971    this.assetManager = new MoonbeamAssetManagerGroup(this);2972    this.assets = new AssetsGroup(this);2973    this.xTokens = new XTokensGroup(this);2974    this.democracy = new MoonbeamDemocracyGroup(this);2975    this.collective = {2976      council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2977      techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2978    };2979  }2980}29812982export class AcalaHelper extends XcmChainHelper {2983  balance: SubstrateBalanceGroup<AcalaHelper>;2984  assetRegistry: AcalaAssetRegistryGroup;2985  xTokens: XTokensGroup<AcalaHelper>;2986  tokens: TokensGroup<AcalaHelper>;29872988  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2989    super(logger, options.helperBase ?? AcalaHelper);29902991    this.balance = new SubstrateBalanceGroup(this);2992    this.assetRegistry = new AcalaAssetRegistryGroup(this);2993    this.xTokens = new XTokensGroup(this);2994    this.tokens = new TokensGroup(this);2995  }29962997  getSudo<T extends AcalaHelper>() {2998    // eslint-disable-next-line @typescript-eslint/naming-convention2999    const SudoHelperType = SudoHelper(this.helperBase);3000    return this.clone(SudoHelperType) as T;3001  }3002}30033004// eslint-disable-next-line @typescript-eslint/naming-convention3005function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {3006  return class extends Base {3007    scheduleFn: 'schedule' | 'scheduleAfter';3008    blocksNum: number;3009    options: ISchedulerOptions;30103011    constructor(...args: any[]) {3012      const logger = args[0] as ILogger;3013      const options = args[1] as {3014        scheduleFn: 'schedule' | 'scheduleAfter',3015        blocksNum: number,3016        options: ISchedulerOptions3017      };30183019      super(logger);30203021      this.scheduleFn = options.scheduleFn;3022      this.blocksNum = options.blocksNum;3023      this.options = options.options;3024    }30253026    executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {3027      const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);3028      3029      const mandatorySchedArgs = [3030        this.blocksNum,3031        this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,3032        this.options.priority ?? null,3033        scheduledTx,3034      ];3035      3036      let schedArgs;3037      let scheduleFn;30383039      if (this.options.scheduledId) {3040        schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];30413042        if (this.scheduleFn == 'schedule') {3043          scheduleFn = 'scheduleNamed';3044        } else if (this.scheduleFn == 'scheduleAfter') {3045          scheduleFn = 'scheduleNamedAfter';3046        }3047      } else {3048        schedArgs = mandatorySchedArgs;3049        scheduleFn = this.scheduleFn;3050      }30513052      const extrinsic = 'api.tx.scheduler.' +  scheduleFn;30533054      return super.executeExtrinsic(3055        sender,3056        extrinsic,3057        schedArgs,3058        expectSuccess,3059      );3060    }3061  };3062}30633064// eslint-disable-next-line @typescript-eslint/naming-convention3065function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {3066  return class extends Base {3067    constructor(...args: any[]) {3068      super(...args);3069    }30703071    async executeExtrinsic(3072      sender: IKeyringPair,3073      extrinsic: string,3074      params: any[],3075      expectSuccess?: boolean,3076      options: Partial<SignerOptions>|null = null,3077    ): Promise<ITransactionResult> {3078      const call = this.constructApiCall(extrinsic, params);3079      const result = await super.executeExtrinsic(3080        sender,3081        'api.tx.sudo.sudo',3082        [call],3083        expectSuccess,3084        options,3085      );30863087      if (result.status === 'Fail') return result;30883089      const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;3090      if (data.isErr) {3091        if (data.asErr.isModule) {3092          const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;3093          const metaError = super.getApi()?.registry.findMetaError(error);3094          throw new Error(`${metaError.section}.${metaError.name}`);3095        } else {3096          throw new Error(data.asErr.toHuman());3097        }3098      }3099      return result;3100    }3101  };3102}31033104export class UniqueBaseCollection {3105  helper: UniqueHelper;3106  collectionId: number;31073108  constructor(collectionId: number, uniqueHelper: UniqueHelper) {3109    this.collectionId = collectionId;3110    this.helper = uniqueHelper;3111  }31123113  async getData() {3114    return await this.helper.collection.getData(this.collectionId);3115  }31163117  async getLastTokenId() {3118    return await this.helper.collection.getLastTokenId(this.collectionId);3119  }31203121  async doesTokenExist(tokenId: number) {3122    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3123  }31243125  async getAdmins() {3126    return await this.helper.collection.getAdmins(this.collectionId);3127  }31283129  async getAllowList() {3130    return await this.helper.collection.getAllowList(this.collectionId);3131  }31323133  async getEffectiveLimits() {3134    return await this.helper.collection.getEffectiveLimits(this.collectionId);3135  }31363137  async getProperties(propertyKeys?: string[] | null) {3138    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3139  }31403141  async getPropertiesConsumedSpace() {3142    return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3143  }31443145  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3146    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3147  }31483149  async getOptions() {3150    return await this.helper.collection.getCollectionOptions(this.collectionId);3151  }31523153  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3154    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3155  }31563157  async confirmSponsorship(signer: TSigner) {3158    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3159  }31603161  async removeSponsor(signer: TSigner) {3162    return await this.helper.collection.removeSponsor(signer, this.collectionId);3163  }31643165  async setLimits(signer: TSigner, limits: ICollectionLimits) {3166    return await this.helper.collection.setLimits(signer, this.collectionId, limits);3167  }31683169  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3170    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3171  }31723173  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3174    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3175  }31763177  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3178    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3179  }31803181  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3182    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3183  }31843185  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3186    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3187  }31883189  async setProperties(signer: TSigner, properties: IProperty[]) {3190    return await this.helper.collection.setProperties(signer, this.collectionId, properties);3191  }31923193  async deleteProperties(signer: TSigner, propertyKeys: string[]) {3194    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3195  }31963197  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3198    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3199  }32003201  async enableNesting(signer: TSigner, permissions: INestingPermissions) {3202    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3203  }32043205  async disableNesting(signer: TSigner) {3206    return await this.helper.collection.disableNesting(signer, this.collectionId);3207  }32083209  async burn(signer: TSigner) {3210    return await this.helper.collection.burn(signer, this.collectionId);3211  }32123213  scheduleAt<T extends UniqueHelper>(3214    executionBlockNumber: number,3215    options: ISchedulerOptions = {},3216  ) {3217    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3218    return new UniqueBaseCollection(this.collectionId, scheduledHelper);3219  }32203221  scheduleAfter<T extends UniqueHelper>(3222    blocksBeforeExecution: number,3223    options: ISchedulerOptions = {},3224  ) {3225    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3226    return new UniqueBaseCollection(this.collectionId, scheduledHelper);3227  }32283229  getSudo<T extends UniqueHelper>() {3230    return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3231  }3232}323332343235export class UniqueNFTCollection extends UniqueBaseCollection {3236  getTokenObject(tokenId: number) {3237    return new UniqueNFToken(tokenId, this);3238  }32393240  async getTokensByAddress(addressObj: ICrossAccountId) {3241    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3242  }32433244  async getToken(tokenId: number, blockHashAt?: string) {3245    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3246  }32473248  async getTokenOwner(tokenId: number, blockHashAt?: string) {3249    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3250  }32513252  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3253    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3254  }32553256  async getTokenChildren(tokenId: number, blockHashAt?: string) {3257    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3258  }32593260  async getPropertyPermissions(propertyKeys: string[] | null = null) {3261    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3262  }32633264  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3265    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3266  }32673268  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3269    const api = this.helper.getApi();3270    const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();3271        3272    return (props! as any).consumedSpace;3273  }32743275  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3276    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3277  }32783279  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3280    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3281  }32823283  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3284    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3285  }32863287  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3288    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3289  }32903291  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3292    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3293  }32943295  async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3296    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3297  }32983299  async burnToken(signer: TSigner, tokenId: number) {3300    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3301  }33023303  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3304    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3305  }33063307  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3308    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3309  }33103311  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3312    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3313  }33143315  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3316    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3317  }33183319  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3320    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3321  }33223323  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3324    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3325  }33263327  scheduleAt<T extends UniqueHelper>(3328    executionBlockNumber: number,3329    options: ISchedulerOptions = {},3330  ) {3331    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3332    return new UniqueNFTCollection(this.collectionId, scheduledHelper);3333  }33343335  scheduleAfter<T extends UniqueHelper>(3336    blocksBeforeExecution: number,3337    options: ISchedulerOptions = {},3338  ) {3339    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3340    return new UniqueNFTCollection(this.collectionId, scheduledHelper);3341  }33423343  getSudo<T extends UniqueHelper>() {3344    return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3345  }3346}334733483349export class UniqueRFTCollection extends UniqueBaseCollection {3350  getTokenObject(tokenId: number) {3351    return new UniqueRFToken(tokenId, this);3352  }33533354  async getToken(tokenId: number, blockHashAt?: string) {3355    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3356  }33573358  async getTokensByAddress(addressObj: ICrossAccountId) {3359    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3360  }33613362  async getTop10TokenOwners(tokenId: number) {3363    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3364  }33653366  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3367    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3368  }33693370  async getTokenTotalPieces(tokenId: number) {3371    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3372  }33733374  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3375    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3376  }33773378  async getPropertyPermissions(propertyKeys: string[] | null = null) {3379    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3380  }33813382  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3383    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3384  }33853386  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3387    const api = this.helper.getApi();3388    const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();3389        3390    return (props! as any).consumedSpace;3391  }33923393  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3394    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3395  }33963397  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3398    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3399  }34003401  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3402    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3403  }34043405  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3406    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3407  }34083409  async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3410    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3411  }34123413  async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3414    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3415  }34163417  async burnToken(signer: TSigner, tokenId: number, amount=1n) {3418    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3419  }34203421  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId,  amount=1n) {3422    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3423  }34243425  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3426    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3427  }34283429  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3430    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3431  }34323433  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3434    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3435  }34363437  scheduleAt<T extends UniqueHelper>(3438    executionBlockNumber: number,3439    options: ISchedulerOptions = {},3440  ) {3441    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3442    return new UniqueRFTCollection(this.collectionId, scheduledHelper);3443  }34443445  scheduleAfter<T extends UniqueHelper>(3446    blocksBeforeExecution: number,3447    options: ISchedulerOptions = {},3448  ) {3449    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3450    return new UniqueRFTCollection(this.collectionId, scheduledHelper);3451  }34523453  getSudo<T extends UniqueHelper>() {3454    return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3455  }3456}345734583459export class UniqueFTCollection extends UniqueBaseCollection {3460  async getBalance(addressObj: ICrossAccountId) {3461    return await this.helper.ft.getBalance(this.collectionId, addressObj);3462  }34633464  async getTotalPieces() {3465    return await this.helper.ft.getTotalPieces(this.collectionId);3466  }34673468  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3469    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3470  }34713472  async getTop10Owners() {3473    return await this.helper.ft.getTop10Owners(this.collectionId);3474  }34753476  async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3477    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3478  }34793480  async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3481    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3482  }34833484  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3485    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3486  }34873488  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3489    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3490  }34913492  async burnTokens(signer: TSigner, amount=1n) {3493    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3494  }34953496  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3497    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3498  }34993500  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3501    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3502  }35033504  scheduleAt<T extends UniqueHelper>(3505    executionBlockNumber: number,3506    options: ISchedulerOptions = {},3507  ) {3508    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3509    return new UniqueFTCollection(this.collectionId, scheduledHelper);3510  }35113512  scheduleAfter<T extends UniqueHelper>(3513    blocksBeforeExecution: number,3514    options: ISchedulerOptions = {},3515  ) {3516    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3517    return new UniqueFTCollection(this.collectionId, scheduledHelper);3518  }35193520  getSudo<T extends UniqueHelper>() {3521    return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3522  }3523}352435253526export class UniqueBaseToken {3527  collection: UniqueNFTCollection | UniqueRFTCollection;3528  collectionId: number;3529  tokenId: number;35303531  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3532    this.collection = collection;3533    this.collectionId = collection.collectionId;3534    this.tokenId = tokenId;3535  }35363537  async getNextSponsored(addressObj: ICrossAccountId) {3538    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3539  }35403541  async getProperties(propertyKeys?: string[] | null) {3542    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3543  }35443545  async getTokenPropertiesConsumedSpace() {3546    return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3547  }35483549  async setProperties(signer: TSigner, properties: IProperty[]) {3550    return await this.collection.setTokenProperties(signer, this.tokenId, properties);3551  }35523553  async deleteProperties(signer: TSigner, propertyKeys: string[]) {3554    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3555  }35563557  async doesExist() {3558    return await this.collection.doesTokenExist(this.tokenId);3559  }35603561  nestingAccount() {3562    return this.collection.helper.util.getTokenAccount(this);3563  }35643565  scheduleAt<T extends UniqueHelper>(3566    executionBlockNumber: number,3567    options: ISchedulerOptions = {},3568  ) {3569    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3570    return new UniqueBaseToken(this.tokenId, scheduledCollection);3571  }35723573  scheduleAfter<T extends UniqueHelper>(3574    blocksBeforeExecution: number,3575    options: ISchedulerOptions = {},3576  ) {3577    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3578    return new UniqueBaseToken(this.tokenId, scheduledCollection);3579  }35803581  getSudo<T extends UniqueHelper>() {3582    return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3583  }3584}358535863587export class UniqueNFToken extends UniqueBaseToken {3588  collection: UniqueNFTCollection;35893590  constructor(tokenId: number, collection: UniqueNFTCollection) {3591    super(tokenId, collection);3592    this.collection = collection;3593  }35943595  async getData(blockHashAt?: string) {3596    return await this.collection.getToken(this.tokenId, blockHashAt);3597  }35983599  async getOwner(blockHashAt?: string) {3600    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3601  }36023603  async getTopmostOwner(blockHashAt?: string) {3604    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3605  }36063607  async getChildren(blockHashAt?: string) {3608    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3609  }36103611  async nest(signer: TSigner, toTokenObj: IToken) {3612    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3613  }36143615  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3616    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3617  }36183619  async transfer(signer: TSigner, addressObj: ICrossAccountId) {3620    return await this.collection.transferToken(signer, this.tokenId, addressObj);3621  }36223623  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3624    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3625  }36263627  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3628    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3629  }36303631  async isApproved(toAddressObj: ICrossAccountId) {3632    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3633  }36343635  async burn(signer: TSigner) {3636    return await this.collection.burnToken(signer, this.tokenId);3637  }36383639  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3640    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3641  }36423643  scheduleAt<T extends UniqueHelper>(3644    executionBlockNumber: number,3645    options: ISchedulerOptions = {},3646  ) {3647    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3648    return new UniqueNFToken(this.tokenId, scheduledCollection);3649  }36503651  scheduleAfter<T extends UniqueHelper>(3652    blocksBeforeExecution: number,3653    options: ISchedulerOptions = {},3654  ) {3655    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3656    return new UniqueNFToken(this.tokenId, scheduledCollection);3657  }36583659  getSudo<T extends UniqueHelper>() {3660    return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3661  }3662}36633664export class UniqueRFToken extends UniqueBaseToken {3665  collection: UniqueRFTCollection;36663667  constructor(tokenId: number, collection: UniqueRFTCollection) {3668    super(tokenId, collection);3669    this.collection = collection;3670  }36713672  async getData(blockHashAt?: string) {3673    return await this.collection.getToken(this.tokenId, blockHashAt);3674  }36753676  async getTop10Owners() {3677    return await this.collection.getTop10TokenOwners(this.tokenId);3678  }36793680  async getBalance(addressObj: ICrossAccountId) {3681    return await this.collection.getTokenBalance(this.tokenId, addressObj);3682  }36833684  async getTotalPieces() {3685    return await this.collection.getTokenTotalPieces(this.tokenId);3686  }36873688  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3689    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3690  }36913692  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3693    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3694  }36953696  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3697    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3698  }36993700  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3701    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3702  }37033704  async repartition(signer: TSigner, amount: bigint) {3705    return await this.collection.repartitionToken(signer, this.tokenId, amount);3706  }37073708  async burn(signer: TSigner, amount=1n) {3709    return await this.collection.burnToken(signer, this.tokenId, amount);3710  }37113712  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3713    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3714  }37153716  scheduleAt<T extends UniqueHelper>(3717    executionBlockNumber: number,3718    options: ISchedulerOptions = {},3719  ) {3720    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3721    return new UniqueRFToken(this.tokenId, scheduledCollection);3722  }37233724  scheduleAfter<T extends UniqueHelper>(3725    blocksBeforeExecution: number,3726    options: ISchedulerOptions = {},3727  ) {3728    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3729    return new UniqueRFToken(this.tokenId, scheduledCollection);3730  }37313732  getSudo<T extends UniqueHelper>() {3733    return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3734  }3735}