git.delta.rocks / unique-network / refs/commits / 7781ab982a4b

difftreelog

style(Scheduler) removed unlikely to be used commented chunks of code

Fahrrader2022-02-21parent: #be56642.patch.diff
in: master

3 files changed

modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -159,15 +159,6 @@
 /// The location of a scheduled task that can be used to remove it.
 pub type TaskAddress<BlockNumber> = (BlockNumber, u32);
 
-/*#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))] todo remove completely?
-#[derive(Clone, RuntimeDebug, Encode, Decode)]
-struct ScheduledV1<Call, BlockNumber> {
-	maybe_id: Option<Vec<u8>>,
-	priority: schedule::Priority,
-	call: Call,
-	maybe_periodic: Option<schedule::Period<BlockNumber>>,
-}*/
-
 /// Information regarding an item to be executed in the future.
 #[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]
 #[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]
@@ -185,27 +176,8 @@
 	/// The information regarding the call's preparation for dispatch, in particular the fee, to be sent to post-dispatch.
 	pre_dispatch: PreDispatch,
 	_phantom: PhantomData<AccountId>,
-}
-
-/// The current version of Scheduled struct.
-/*pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId, PreDispatch> =
-	ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId, PreDispatch>;*/
-
-// A value placed in storage that represents the current version of the Scheduler storage.
-// This value is used by the `on_runtime_upgrade` logic to determine whether we run
-// storage migration logic.
-/*#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] todo remove completely?
-enum Releases {
-	V1,
-	V2,
 }
 
-impl Default for Releases {
-	fn default() -> Self {
-		Releases::V1
-	}
-}*/
-
 #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
 pub struct CallSpec {
 	module: u32,
@@ -223,17 +195,9 @@
 				T::AccountId,
 				<<T as Config>::CallExecutor as DispatchCall<T, H160>>::Pre
 			>>>;
-
-		/*pub SpecAgenda: map hasher(twox_64_concat) T::BlockNumber todo remove completely?
-			=> Vec<Option<CallSpec>>;*/
-
+			
 		/// Lookup from identity to the block number and index of the task.
 		Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;
-
-		// / Storage version of the pallet.
-		// /
-		// / New networks start with last version.
-		//StorageVersion build(|_| Releases::V2): Releases; todo remove completely?
 	}
 }
 
@@ -535,9 +499,9 @@
 			<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin.clone()).into(),
 		)
 		.unwrap_or_default(); // todo sponsoring doesn't work with the line below -- found sponsoring is already checked for in transaction_payment
-					  //let who_will_pay = T::SponsorshipHandler::get_sponsor(&sender, &call).unwrap_or(sender);
-					  //let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay.clone()));
-					  //let r = call.clone().dispatch(sponsor.into());
+		//let who_will_pay = T::SponsorshipHandler::get_sponsor(&sender, &call).unwrap_or(sender);
+		//let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay.clone()));
+		//let r = call.clone().dispatch(sponsor.into());
 
 		let pre_dispatch = match T::CallExecutor::pre_dispatch(sender.clone(), call.clone()) {
 			Ok(pre_dispatch) => pre_dispatch,
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -811,19 +811,6 @@
 
 type ChargeTransactionPayment = pallet_charge_transaction::ChargeTransactionPayment<Runtime>;
 
-/*fn get_signed_extra(from: <Runtime as frame_system::Config>::AccountId) -> SignedExtra {
-	(
-		frame_system::CheckSpecVersion::<Runtime>::new(),
-		frame_system::CheckGenesis::<Runtime>::new(),
-		frame_system::CheckEra::<Runtime>::from(Era::Immortal),
-		frame_system::CheckNonce::<Runtime>::from(frame_system::Pallet::<Runtime>::account_nonce(
-			from,
-		)),
-		frame_system::CheckWeight::<Runtime>::new(),
-		ChargeTransactionPayment::new(0),
-	)
-}*/
-
 #[derive(Default, Encode, Decode, Clone, TypeInfo)]
 pub struct SchedulerPreDispatch {
 	tip: Balance,
@@ -849,29 +836,12 @@
 	type Pre = SchedulerPreDispatch;
 
 	fn dispatch_call(
-		//signer: <T as frame_system::Config>::AccountId,
 		pre_dispatch: Self::Pre,
 		call: <T as pallet_unq_scheduler::Config>::Call,
 	) -> Result<
 		Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
 		TransactionValidityError,
 	> {
-		/*let dispatch_info = call.get_dispatch_info();
-
-		let extrinsic = fp_self_contained::CheckedExtrinsic::<
-			AccountId,
-			Call,
-			SignedExtra,
-			SelfContainedSignedInfo,
-		> {
-			signed: CheckedSignature::<AccountId, SignedExtra, SelfContainedSignedInfo>::Signed(
-				signer.clone().into(),
-				get_signed_extra(signer.clone().into()),
-			),
-			function: call.clone().into(),
-		};
-
-		extrinsic.apply::<Runtime>(&dispatch_info, 0)*/
 		let dispatch_info = call.get_dispatch_info();
 		let pre = (
 			pre_dispatch.tip,
modifiedtests/src/scheduler.test.tsdiffbeforeafterboth
before · tests/src/scheduler.test.ts
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56import chai, { expect } from 'chai';7import chaiAsPromised from 'chai-as-promised';8import privateKey from './substrate/privateKey';9import {10  default as usingApi, 11  submitTransactionAsync,12  submitTransactionExpectFailAsync,13} from './substrate/substrate-api';14import {15  createItemExpectSuccess,16  createCollectionExpectSuccess,17  scheduleTransferExpectSuccess,18  scheduleTransferAndWaitExpectSuccess,19  setCollectionSponsorExpectSuccess,20  confirmSponsorshipExpectSuccess,21  findUnusedAddress,22  UNIQUE,23  enablePublicMintingExpectSuccess,24  addToAllowListExpectSuccess,25  waitNewBlocks,26  normalizeAccountId,27  getTokenOwner,28  getGenericResult,29  scheduleTransferFundsPeriodicExpectSuccess,30  setCollectionLimitsExpectSuccess,31  getDetailedCollectionInfo,32  getFreeBalance,33  confirmSponsorshipByKeyExpectSuccess,34} from './util/helpers';35import {IKeyringPair} from '@polkadot/types/types';36import {getBalanceSingle} from './substrate/get-balance';3738chai.use(chaiAsPromised);3940describe('Scheduling token and balance transfers', () => {41  let alice: IKeyringPair;42  let bob: IKeyringPair;4344  before(async() => {45    await usingApi(async () => {46      alice = privateKey('//Alice');47      bob = privateKey('//Bob');48    });49  });5051  it('Can schedule a transfer of an owned token with delay', async () => {52    await usingApi(async () => {53      // nft54      const nftCollectionId = await createCollectionExpectSuccess();55      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');56      await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);57      await confirmSponsorshipExpectSuccess(nftCollectionId);5859      await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4);60    });61  });6263  it('Can transfer funds periodically', async () => {64    await usingApi(async (api) => {65      const waitForBlocks = 4;66      const period = 2;67      await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, period, 2);68      const bobsBalanceBefore = await getBalanceSingle(api, bob.address);6970      // discounting already waited-for operations71      await waitNewBlocks(waitForBlocks - 2);72      const bobsBalanceAfterFirst = await getBalanceSingle(api, bob.address);73      expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;7475      await waitNewBlocks(period);76      const bobsBalanceAfterSecond = await getBalanceSingle(api, bob.address);77      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;78    });79  });8081  it('Can sponsor scheduling a transaction', async () => {82    const collectionId = await createCollectionExpectSuccess();83    await setCollectionSponsorExpectSuccess(collectionId, bob.address);84    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');8586    await usingApi(async () => {87      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);8889      const aliceBalanceBefore = await getFreeBalance(alice);90      // no need to wait to check, fees must be deducted on scheduling, immediately91      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, 4);92      const aliceBalanceAfter = await getFreeBalance(alice);93      expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;94    });95  });9697  /*it('Can\'t schedule a transaction with no funds', async () => {98    await usingApi(async (api) => {99      // Find an empty, unused account100      const zeroBalance = await findUnusedAddress(api);101102      const collectionId = await createCollectionExpectSuccess();103      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');104105      await transferExpectSuccess(collectionId, tokenId, alice, zeroBalance);106107      await scheduleTransferAndWaitExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);108    });109  });*/110111  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {112    await usingApi(async (api) => {113      // Find an empty, unused account114      const zeroBalance = await findUnusedAddress(api);115116      const collectionId = await createCollectionExpectSuccess();117118      // Add zeroBalance address to allow list119      await enablePublicMintingExpectSuccess(alice, collectionId);120      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);121122      // Grace zeroBalance with money, enough to cover future transactions123      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);124      await submitTransactionAsync(alice, balanceTx);125126      // Mint a fresh NFT127      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');128129      // Schedule transfer of the NFT a few blocks ahead130      const waitForBlocks = 5;131      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks);132133      // Get rid of the account's funds before the scheduled transaction takes place134      const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?135      const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);136      const events = await submitTransactionAsync(alice, sudoTx);137      expect(getGenericResult(events).success).to.be.true;138139      // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions140      await waitNewBlocks(waitForBlocks - 3);141142      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));143    });144  });145146  /*it.skip('Going bankrupt after sponsoring a scheduled transaction does not nullify the transaction', async () => {147    await usingApi(async (api) => {148      // Find two empty, unused accounts149      const zeroBalance = await findUnusedAddress(api);150      const zeroBalanceSponsor = await findUnusedAddress(api);151152      const collectionId = await createCollectionExpectSuccess();153154      // Grace these with money, enough to cover future transactions155      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);156      await submitTransactionAsync(alice, balanceTx);157158      // Grace these with money, enough to cover future transactions159      const balanceSponsorTx = api.tx.balances.transfer(zeroBalanceSponsor.address, 1n * UNIQUE);160      await submitTransactionAsync(alice, balanceSponsorTx);161162      // Set a collection sponsor163      await setCollectionSponsorExpectSuccess(collectionId, zeroBalanceSponsor.address);164      await confirmSponsorshipExpectSuccess(collectionId);165166      // Add zeroBalance address to allow list167      await enablePublicMintingExpectSuccess(alice, collectionId);168      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);169170      // Mint a fresh NFT171      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');172173      // Schedule transfer of the NFT a few blocks ahead174      // const waitForBlocks = 4;175      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);176      //await waitAfterScheduleExpectSuccess(collectionId, tokenId, alice, 3);177178      // Get rid of the account's funds before the scheduled transaction takes place179      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalanceSponsor.address, 0, 0);180      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);181      const events = await submitTransactionAsync(alice, sudoTx);182      expect(getGenericResult(events).success).to.be.true;183184      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions185      await waitNewBlocks(4);186187      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));188    });189  });*/190191  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {192    const collectionId = await createCollectionExpectSuccess();193194    await usingApi(async (api) => {195      const zeroBalance = await findUnusedAddress(api);196197      /*await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {198        sponsoredDataRateLimit: 2,199      });*/200      //console.log(await getDetailedCollectionInfo(api, nftCollectionId));201      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);202      await submitTransactionAsync(alice, balanceTx);203204      await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);205      await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);206207      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);208209      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, 5);210211      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);212      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);213      const events = await submitTransactionAsync(alice, sudoTx);214      expect(getGenericResult(events).success).to.be.true;215216      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions217      await waitNewBlocks(2);218219      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));220    });221  });222});223224describe.skip('Scheduling EVM smart contracts', () => {225  let alice: IKeyringPair;226  let bob: IKeyringPair;227228  before(async() => {229    await usingApi(async () => {230      alice = privateKey('//Alice');231      bob = privateKey('//Bob');232    });233  });234235  // todo contract testing236  it.skip('NFT: Sponsoring of transfers is rate limited', async () => {237    const collectionId = await createCollectionExpectSuccess();238    await setCollectionSponsorExpectSuccess(collectionId, bob.address);239    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');240241    await usingApi(async (api) => {242      // Find unused address243      const zeroBalance = await findUnusedAddress(api);244245      // Mint token for alice246      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);247248      // Transfer this token from Alice to unused address and back249      // Alice to Zero gets sponsored250      const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);251      const events1 = await submitTransactionAsync(alice, aliceToZero);252      const result1 = getGenericResult(events1);253254      // Second transfer should fail255      const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();256      const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);257      const badTransaction = async function () {258        await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);259      };260      await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');261      const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();262263      // Try again after Zero gets some balance - now it should succeed264      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);265      await submitTransactionAsync(alice, balancetx);266      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);267      const result2 = getGenericResult(events2);268269      expect(result1.success).to.be.true;270      expect(result2.success).to.be.true;271      expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);272    });273  });274});
after · tests/src/scheduler.test.ts
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56import chai, {expect} from 'chai';7import chaiAsPromised from 'chai-as-promised';8import privateKey from './substrate/privateKey';9import {10  default as usingApi, 11  submitTransactionAsync,12  submitTransactionExpectFailAsync,13} from './substrate/substrate-api';14import {15  createItemExpectSuccess,16  createCollectionExpectSuccess,17  scheduleTransferExpectSuccess,18  scheduleTransferAndWaitExpectSuccess,19  setCollectionSponsorExpectSuccess,20  confirmSponsorshipExpectSuccess,21  findUnusedAddress,22  UNIQUE,23  enablePublicMintingExpectSuccess,24  addToAllowListExpectSuccess,25  waitNewBlocks,26  normalizeAccountId,27  getTokenOwner,28  getGenericResult,29  scheduleTransferFundsPeriodicExpectSuccess,30  getFreeBalance,31  confirmSponsorshipByKeyExpectSuccess,32} from './util/helpers';33import {IKeyringPair} from '@polkadot/types/types';34import {getBalanceSingle} from './substrate/get-balance';3536chai.use(chaiAsPromised);3738describe('Scheduling token and balance transfers', () => {39  let alice: IKeyringPair;40  let bob: IKeyringPair;4142  before(async() => {43    await usingApi(async () => {44      alice = privateKey('//Alice');45      bob = privateKey('//Bob');46    });47  });4849  it('Can schedule a transfer of an owned token with delay', async () => {50    await usingApi(async () => {51      // nft52      const nftCollectionId = await createCollectionExpectSuccess();53      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');54      await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);55      await confirmSponsorshipExpectSuccess(nftCollectionId);5657      await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4);58    });59  });6061  it('Can transfer funds periodically', async () => {62    await usingApi(async (api) => {63      const waitForBlocks = 4;64      const period = 2;65      await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, period, 2);66      const bobsBalanceBefore = await getBalanceSingle(api, bob.address);6768      // discounting already waited-for operations69      await waitNewBlocks(waitForBlocks - 2);70      const bobsBalanceAfterFirst = await getBalanceSingle(api, bob.address);71      expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;7273      await waitNewBlocks(period);74      const bobsBalanceAfterSecond = await getBalanceSingle(api, bob.address);75      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;76    });77  });7879  it('Can sponsor scheduling a transaction', async () => {80    const collectionId = await createCollectionExpectSuccess();81    await setCollectionSponsorExpectSuccess(collectionId, bob.address);82    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');8384    await usingApi(async () => {85      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);8687      const aliceBalanceBefore = await getFreeBalance(alice);88      // no need to wait to check, fees must be deducted on scheduling, immediately89      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, 4);90      const aliceBalanceAfter = await getFreeBalance(alice);91      expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;92    });93  });9495  /*it('Can\'t schedule a transaction with no funds', async () => {96    await usingApi(async (api) => {97      // Find an empty, unused account98      const zeroBalance = await findUnusedAddress(api);99100      const collectionId = await createCollectionExpectSuccess();101      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');102103      await transferExpectSuccess(collectionId, tokenId, alice, zeroBalance);104105      await scheduleTransferAndWaitExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);106    });107  });*/108109  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {110    await usingApi(async (api) => {111      // Find an empty, unused account112      const zeroBalance = await findUnusedAddress(api);113114      const collectionId = await createCollectionExpectSuccess();115116      // Add zeroBalance address to allow list117      await enablePublicMintingExpectSuccess(alice, collectionId);118      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);119120      // Grace zeroBalance with money, enough to cover future transactions121      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);122      await submitTransactionAsync(alice, balanceTx);123124      // Mint a fresh NFT125      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');126127      // Schedule transfer of the NFT a few blocks ahead128      const waitForBlocks = 5;129      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks);130131      // Get rid of the account's funds before the scheduled transaction takes place132      const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?133      const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);134      const events = await submitTransactionAsync(alice, sudoTx);135      expect(getGenericResult(events).success).to.be.true;136137      // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions138      await waitNewBlocks(waitForBlocks - 3);139140      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));141    });142  });143144  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {145    const collectionId = await createCollectionExpectSuccess();146147    await usingApi(async (api) => {148      const zeroBalance = await findUnusedAddress(api);149150      /*await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {151        sponsoredDataRateLimit: 2,152      });*/153      //console.log(await getDetailedCollectionInfo(api, nftCollectionId));154      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);155      await submitTransactionAsync(alice, balanceTx);156157      await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);158      await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);159160      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);161162      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, 5);163164      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);165      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);166      const events = await submitTransactionAsync(alice, sudoTx);167      expect(getGenericResult(events).success).to.be.true;168169      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions170      await waitNewBlocks(2);171172      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));173    });174  });175});176177describe.skip('Scheduling EVM smart contracts', () => {178  let alice: IKeyringPair;179  let bob: IKeyringPair;180181  before(async() => {182    await usingApi(async () => {183      alice = privateKey('//Alice');184      bob = privateKey('//Bob');185    });186  });187188  // todo contract testing189  it.skip('NFT: Sponsoring of transfers is rate limited', async () => {190    const collectionId = await createCollectionExpectSuccess();191    await setCollectionSponsorExpectSuccess(collectionId, bob.address);192    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');193194    await usingApi(async (api) => {195      // Find unused address196      const zeroBalance = await findUnusedAddress(api);197198      // Mint token for alice199      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);200201      // Transfer this token from Alice to unused address and back202      // Alice to Zero gets sponsored203      const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);204      const events1 = await submitTransactionAsync(alice, aliceToZero);205      const result1 = getGenericResult(events1);206207      // Second transfer should fail208      const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();209      const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);210      const badTransaction = async function () {211        await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);212      };213      await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');214      const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();215216      // Try again after Zero gets some balance - now it should succeed217      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);218      await submitTransactionAsync(alice, balancetx);219      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);220      const result2 = getGenericResult(events2);221222      expect(result1.success).to.be.true;223      expect(result2.success).to.be.true;224      expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);225    });226  });227});