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

difftreelog

source

tests/src/scheduler.seqtest.ts27.1 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {expect, itSub, Pallets, usingPlaygrounds} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {DevUniqueHelper} from './util/playgrounds/unique.dev';2021describe('Scheduling token and balance transfers', () => {22  let alice: IKeyringPair;23  let bob: IKeyringPair;24  let charlie: IKeyringPair;2526  before(async () => {27    await usingPlaygrounds(async (helper, privateKeyWrapper) => {28      alice = await privateKeyWrapper('//Alice');29      bob = await privateKeyWrapper('//Bob');30      charlie = await privateKeyWrapper('//Charlie');3132      await helper.testUtils.enable();33    });34  });3536  itSub.ifWithPallets('Can delay a transfer of an owned token', [Pallets.Scheduler], async ({helper}) => {37    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});38    const token = await collection.mintToken(alice);39    const schedulerId = await helper.arrange.makeScheduledId();40    const blocksBeforeExecution = 4;4142    await token.scheduleAfter(schedulerId, blocksBeforeExecution)43      .transfer(alice, {Substrate: bob.address});4445    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});4647    await helper.wait.newBlocks(blocksBeforeExecution + 1);4849    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});50  });5152  itSub.ifWithPallets('Can transfer funds periodically', [Pallets.Scheduler], async ({helper}) => {53    const scheduledId = await helper.arrange.makeScheduledId();54    const waitForBlocks = 1;5556    const amount = 1n * helper.balance.getOneTokenNominal();57    const periodic = {58      period: 2,59      repetitions: 2,60    };6162    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);6364    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})65      .balance.transferToSubstrate(alice, bob.address, amount);6667    await helper.wait.newBlocks(waitForBlocks + 1);6869    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);70    expect(bobsBalanceAfterFirst)71      .to.be.equal(72        bobsBalanceBefore + 1n * amount,73        '#1 Balance of the recipient should be increased by 1 * amount',74      );7576    await helper.wait.newBlocks(periodic.period);7778    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);79    expect(bobsBalanceAfterSecond)80      .to.be.equal(81        bobsBalanceBefore + 2n * amount,82        '#2 Balance of the recipient should be increased by 2 * amount',83      );84  });8586  itSub.ifWithPallets('Can cancel a scheduled operation which has not yet taken effect', [Pallets.Scheduler], async ({helper}) => {87    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});88    const token = await collection.mintToken(alice);8990    const scheduledId = await helper.arrange.makeScheduledId();91    const waitForBlocks = 4;9293    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});9495    await token.scheduleAfter(scheduledId, waitForBlocks)96      .transfer(alice, {Substrate: bob.address});9798    await helper.scheduler.cancelScheduled(alice, scheduledId);99100    await helper.wait.newBlocks(waitForBlocks + 1);101102    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});103  });104105  itSub.ifWithPallets('Can cancel a periodic operation (transfer of funds)', [Pallets.Scheduler], async ({helper}) => {106    const waitForBlocks = 1;107    const periodic = {108      period: 3,109      repetitions: 2,110    };111112    const scheduledId = await helper.arrange.makeScheduledId();113114    const amount = 1n * helper.balance.getOneTokenNominal();115116    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);117118    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})119      .balance.transferToSubstrate(alice, bob.address, amount);120121    await helper.wait.newBlocks(waitForBlocks + 1);122123    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);124125    expect(bobsBalanceAfterFirst)126      .to.be.equal(127        bobsBalanceBefore + 1n * amount,128        '#1 Balance of the recipient should be increased by 1 * amount',129      );130131    await helper.scheduler.cancelScheduled(alice, scheduledId);132    await helper.wait.newBlocks(periodic.period);133134    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);135    expect(bobsBalanceAfterSecond)136      .to.be.equal(137        bobsBalanceAfterFirst,138        '#2 Balance of the recipient should not be changed',139      );140  });141142  itSub.ifWithPallets('Scheduled tasks are transactional', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {143    const scheduledId = await helper.arrange.makeScheduledId();144    const waitForBlocks = 4;145146    const initTestVal = 42;147    const changedTestVal = 111;148149    await helper.testUtils.setTestValue(alice, initTestVal);150151    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks)152      .testUtils.setTestValueAndRollback(alice, changedTestVal);153154    await helper.wait.newBlocks(waitForBlocks + 1);155156    const testVal = await helper.testUtils.testValue();157    expect(testVal, 'The test value should NOT be commited')158      .to.be.equal(initTestVal);159  });160161  itSub.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.Scheduler, Pallets.TestUtils], async function({helper}) {162    const scheduledId = await helper.arrange.makeScheduledId();163    const waitForBlocks = 4;164    const periodic = {165      period: 2,166      repetitions: 2,167    };168169    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);170    const scheduledLen = dummyTx.callIndex.length;171172    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))173      .partialFee.toBigInt();174175    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})176      .testUtils.justTakeFee(alice);177178    await helper.wait.newBlocks(1);179180    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);181    let diff;182183    await helper.wait.newBlocks(waitForBlocks);184185    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);186    expect(187      aliceBalanceAfterFirst < aliceInitBalance,188      '[after execution #1] Scheduled task should take a fee',189    ).to.be.true;190191    diff = aliceInitBalance - aliceBalanceAfterFirst;192    expect(diff).to.be.equal(193      expectedScheduledFee,194      'Scheduled task should take the right amount of fees',195    );196197    await helper.wait.newBlocks(periodic.period);198199    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);200    expect(201      aliceBalanceAfterSecond < aliceBalanceAfterFirst,202      '[after execution #2] Scheduled task should take a fee',203    ).to.be.true;204205    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;206    expect(diff).to.be.equal(207      expectedScheduledFee,208      'Scheduled task should take the right amount of fees',209    );210  });211212  // Check if we can cancel a scheduled periodic operation213  // in the same block in which it is running214  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {215    const currentBlockNumber = await helper.chain.getLatestBlockNumber();216    const blocksBeforeExecution = 10;217    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;218219    const [220      scheduledId,221      scheduledCancelId,222    ] = await helper.arrange.makeScheduledIds(2);223224    const periodic = {225      period: 5,226      repetitions: 5,227    };228229    const initTestVal = 0;230    const incTestVal = initTestVal + 1;231    const finalTestVal = initTestVal + 2;232233    await helper.testUtils.setTestValue(alice, initTestVal);234235    await helper.scheduler.scheduleAt<DevUniqueHelper>(scheduledId, firstExecutionBlockNumber, {periodic})236      .testUtils.incTestValue(alice);237238    // Cancel the inc tx after 2 executions239    // *in the same block* in which the second execution is scheduled240    await helper.scheduler.scheduleAt(241      scheduledCancelId,242      firstExecutionBlockNumber + periodic.period,243    ).scheduler.cancelScheduled(alice, scheduledId);244245    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);246247    // execution #0248    expect(await helper.testUtils.testValue())249      .to.be.equal(incTestVal);250251    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);252253    // execution #1254    expect(await helper.testUtils.testValue())255      .to.be.equal(finalTestVal);256257    for (let i = 1; i < periodic.repetitions; i++) {258      await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1));259      expect(await helper.testUtils.testValue())260        .to.be.equal(finalTestVal);261    }262  });263264  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {265    const scheduledId = await helper.arrange.makeScheduledId();266    const waitForBlocks = 4;267    const periodic = {268      period: 2,269      repetitions: 5,270    };271272    const initTestVal = 0;273    const maxTestVal = 2;274275    await helper.testUtils.setTestValue(alice, initTestVal);276277    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})278      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);279280    await helper.wait.newBlocks(waitForBlocks + 1);281282    // execution #0283    expect(await helper.testUtils.testValue())284      .to.be.equal(initTestVal + 1);285286    await helper.wait.newBlocks(periodic.period);287288    // execution #1289    expect(await helper.testUtils.testValue())290      .to.be.equal(initTestVal + 2);291292    await helper.wait.newBlocks(periodic.period);293294    // <canceled>295    expect(await helper.testUtils.testValue())296      .to.be.equal(initTestVal + 2);297  });298299  itSub.ifWithPallets('Root can cancel any scheduled operation', [Pallets.Scheduler], async ({helper}) => {300    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});301    const token = await collection.mintToken(bob);302303    const scheduledId = await helper.arrange.makeScheduledId();304    const waitForBlocks = 4;305306    await token.scheduleAfter(scheduledId, waitForBlocks)307      .transfer(bob, {Substrate: alice.address});308309    await helper.getSudo().scheduler.cancelScheduled(alice, scheduledId);310311    await helper.wait.newBlocks(waitForBlocks + 1);312313    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});314  });315316  itSub.ifWithPallets('Root can set prioritized scheduled operation', [Pallets.Scheduler], async ({helper}) => {317    const scheduledId = await helper.arrange.makeScheduledId();318    const waitForBlocks = 4;319320    const amount = 42n * helper.balance.getOneTokenNominal();321322    const balanceBefore = await helper.balance.getSubstrate(charlie.address);323324    await helper.getSudo()325      .scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42})326      .balance.forceTransferToSubstrate(alice, bob.address, charlie.address, amount);327328    await helper.wait.newBlocks(waitForBlocks + 1);329330    const balanceAfter = await helper.balance.getSubstrate(charlie.address);331332    expect(balanceAfter > balanceBefore).to.be.true;333334    const diff = balanceAfter - balanceBefore;335    expect(diff).to.be.equal(amount);336  });337338  itSub.ifWithPallets("Root can change scheduled operation's priority", [Pallets.Scheduler], async ({helper}) => {339    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});340    const token = await collection.mintToken(bob);341342    const scheduledId = await helper.arrange.makeScheduledId();343    const waitForBlocks = 6;344345    await token.scheduleAfter(scheduledId, waitForBlocks)346      .transfer(bob, {Substrate: alice.address});347348    const priority = 112;349    await helper.getSudo().scheduler.changePriority(alice, scheduledId, priority);350351    const priorityChanged = await helper.wait.event(352      waitForBlocks,353      'scheduler',354      'PriorityChanged',355    );356357    expect(priorityChanged !== null).to.be.true;358    expect(priorityChanged!.event.data[2].toString()).to.be.equal(priority.toString());359  });360361  itSub.ifWithPallets('Prioritized operations executes in valid order', [Pallets.Scheduler], async ({helper}) => {362    const [363      scheduledFirstId,364      scheduledSecondId,365    ] = await helper.arrange.makeScheduledIds(2);366367    const currentBlockNumber = await helper.chain.getLatestBlockNumber();368    const blocksBeforeExecution = 6;369    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;370371    const prioHigh = 0;372    const prioLow = 255;373374    const periodic = {375      period: 6,376      repetitions: 2,377    };378379    const amount = 1n * helper.balance.getOneTokenNominal();380381    // Scheduler a task with a lower priority first, then with a higher priority382    await helper.getSudo().scheduler.scheduleAt(scheduledFirstId, firstExecutionBlockNumber, {priority: prioLow, periodic})383      .balance.forceTransferToSubstrate(alice, alice.address, bob.address, amount);384385    await helper.getSudo().scheduler.scheduleAt(scheduledSecondId, firstExecutionBlockNumber, {priority: prioHigh, periodic})386      .balance.forceTransferToSubstrate(alice, alice.address, bob.address, amount);387388    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');389390    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);391392    // Flip priorities393    await helper.getSudo().scheduler.changePriority(alice, scheduledFirstId, prioHigh);394    await helper.getSudo().scheduler.changePriority(alice, scheduledSecondId, prioLow);395396    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);397398    const dispatchEvents = capture.extractCapturedEvents();399    expect(dispatchEvents.length).to.be.equal(4);400401    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());402403    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];404    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];405406    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);407    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);408409    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);410    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);411  });412413  itSub.ifWithPallets('Periodic operations always can be rescheduled', [Pallets.Scheduler], async ({helper}) => {414    const maxScheduledPerBlock = 50;415    const numFilledBlocks = 3;416    const ids = await helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1);417    const periodicId = ids[0];418    const fillIds = ids.slice(1);419420    const initTestVal = 0;421    const firstExecTestVal = 1;422    const secondExecTestVal = 2;423    await helper.testUtils.setTestValue(alice, initTestVal);424425    const currentBlockNumber = await helper.chain.getLatestBlockNumber();426    const blocksBeforeExecution = 8;427    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;428429    const period = 5;430431    const periodic = {432      period,433      repetitions: 2,434    };435436    // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur437    const txs = [];438    for (let offset = 0; offset < numFilledBlocks; offset ++) {439      for (let i = 0; i < maxScheduledPerBlock; i++) {440441        const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]);442443        const when = firstExecutionBlockNumber + period + offset;444        const tx = helper.constructApiCall('api.tx.scheduler.scheduleNamed', [fillIds[i + offset * maxScheduledPerBlock], when, null, null, scheduledTx]);445446        txs.push(tx);447      }448    }449    await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true);450451    await helper.scheduler.scheduleAt<DevUniqueHelper>(periodicId, firstExecutionBlockNumber, {periodic})452      .testUtils.incTestValue(alice);453454    await helper.wait.newBlocks(blocksBeforeExecution);455    expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal);456457    await helper.wait.newBlocks(period + numFilledBlocks);458459    // The periodic operation should be postponed by `numFilledBlocks`460    for (let i = 0; i < numFilledBlocks; i++) {461      expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal);462    }463464    // After the `numFilledBlocks` the periodic operation will eventually be executed465    expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal);466  });467});468469describe('Negative Test: Scheduling', () => {470  let alice: IKeyringPair;471  let bob: IKeyringPair;472473  before(async () => {474    await usingPlaygrounds(async (helper, privateKeyWrapper) => {475      alice = await privateKeyWrapper('//Alice');476      bob = await privateKeyWrapper('//Bob');477478      await helper.testUtils.enable();479    });480  });481482  itSub.ifWithPallets("Can't overwrite a scheduled ID", [Pallets.Scheduler], async ({helper}) => {483    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});484    const token = await collection.mintToken(alice);485486    const scheduledId = await helper.arrange.makeScheduledId();487    const waitForBlocks = 4;488489    await token.scheduleAfter(scheduledId, waitForBlocks)490      .transfer(alice, {Substrate: bob.address});491492    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks);493    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))494      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);495496    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);497498    await helper.wait.newBlocks(waitForBlocks + 1);499500    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);501502    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});503    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);504  });505506  itSub.ifWithPallets("Can't cancel an operation which is not scheduled", [Pallets.Scheduler], async ({helper}) => {507    const scheduledId = await helper.arrange.makeScheduledId();508    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))509      .to.be.rejectedWith(/scheduler\.NotFound/);510  });511512  itSub.ifWithPallets("Can't cancel a non-owned scheduled operation", [Pallets.Scheduler], async ({helper}) => {513    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});514    const token = await collection.mintToken(alice);515516    const scheduledId = await helper.arrange.makeScheduledId();517    const waitForBlocks = 4;518519    await token.scheduleAfter(scheduledId, waitForBlocks)520      .transfer(alice, {Substrate: bob.address});521522    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))523      .to.be.rejectedWith(/BadOrigin/);524525    await helper.wait.newBlocks(waitForBlocks + 1);526527    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});528  });529530  itSub.ifWithPallets("Regular user can't set prioritized scheduled operation", [Pallets.Scheduler], async ({helper}) => {531    const scheduledId = await helper.arrange.makeScheduledId();532    const waitForBlocks = 4;533534    const amount = 42n * helper.balance.getOneTokenNominal();535536    const balanceBefore = await helper.balance.getSubstrate(bob.address);537538    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42});539    540    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))541      .to.be.rejectedWith(/BadOrigin/);542543    await helper.wait.newBlocks(waitForBlocks + 1);544545    const balanceAfter = await helper.balance.getSubstrate(bob.address);546547    expect(balanceAfter).to.be.equal(balanceBefore);548  });549550  itSub.ifWithPallets("Regular user can't change scheduled operation's priority", [Pallets.Scheduler], async ({helper}) => {551    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});552    const token = await collection.mintToken(bob);553554    const scheduledId = await helper.arrange.makeScheduledId();555    const waitForBlocks = 4;556557    await token.scheduleAfter(scheduledId, waitForBlocks)558      .transfer(bob, {Substrate: alice.address});559560    const priority = 112;561    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))562      .to.be.rejectedWith(/BadOrigin/);563564    const priorityChanged = await helper.wait.event(565      waitForBlocks,566      'scheduler',567      'PriorityChanged',568    );569570    expect(priorityChanged === null).to.be.true;571  });572});573574// Implementation of the functionality tested here was postponed/shelved575describe.skip('Sponsoring scheduling', () => {576  // let alice: IKeyringPair;577  // let bob: IKeyringPair;578579  // before(async() => {580  //   await usingApi(async (_, privateKeyWrapper) => {581  //     alice = privateKeyWrapper('//Alice');582  //     bob = privateKeyWrapper('//Bob');583  //   });584  // });585586  it('Can sponsor scheduling a transaction', async () => {587    // const collectionId = await createCollectionExpectSuccess();588    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);589    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');590591    // await usingApi(async api => {592    //   const scheduledId = await makeScheduledId();593    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);594595    //   const bobBalanceBefore = await getFreeBalance(bob);596    //   const waitForBlocks = 4;597    //   // no need to wait to check, fees must be deducted on scheduling, immediately598    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);599    //   const bobBalanceAfter = await getFreeBalance(bob);600    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;601    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;602    //   // wait for sequentiality matters603    //   await waitNewBlocks(waitForBlocks - 1);604    // });605  });606607  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {608    // await usingApi(async (api, privateKeyWrapper) => {609    //   // Find an empty, unused account610    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);611612    //   const collectionId = await createCollectionExpectSuccess();613614    //   // Add zeroBalance address to allow list615    //   await enablePublicMintingExpectSuccess(alice, collectionId);616    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);617618    //   // Grace zeroBalance with money, enough to cover future transactions619    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);620    //   await submitTransactionAsync(alice, balanceTx);621622    //   // Mint a fresh NFT623    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');624    //   const scheduledId = await makeScheduledId();625626    //   // Schedule transfer of the NFT a few blocks ahead627    //   const waitForBlocks = 5;628    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);629630    //   // Get rid of the account's funds before the scheduled transaction takes place631    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);632    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);633    //   expect(getGenericResult(events).success).to.be.true;634    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?635    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);636    //   const events = await submitTransactionAsync(alice, sudoTx);637    //   expect(getGenericResult(events).success).to.be.true;*/638639    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions640    //   await waitNewBlocks(waitForBlocks - 3);641642    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));643    // });644  });645646  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {647    // const collectionId = await createCollectionExpectSuccess();648649    // await usingApi(async (api, privateKeyWrapper) => {650    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);651    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);652    //   await submitTransactionAsync(alice, balanceTx);653654    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);655    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);656657    //   const scheduledId = await makeScheduledId();658    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);659660    //   const waitForBlocks = 5;661    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);662663    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);664    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);665    //   const events = await submitTransactionAsync(alice, sudoTx);666    //   expect(getGenericResult(events).success).to.be.true;667668    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions669    //   await waitNewBlocks(waitForBlocks - 3);670671    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));672    // });673  });674675  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {676    // const collectionId = await createCollectionExpectSuccess();677    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);678    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');679680    // await usingApi(async (api, privateKeyWrapper) => {681    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);682683    //   await enablePublicMintingExpectSuccess(alice, collectionId);684    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);685686    //   const bobBalanceBefore = await getFreeBalance(bob);687688    //   const createData = {nft: {const_data: [], variable_data: []}};689    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);690    //   const scheduledId = await makeScheduledId();691692    //   /*const badTransaction = async function () {693    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);694    //   };695    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/696697    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);698699    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);700    // });701  });702});