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

difftreelog

source

tests/src/scheduler.test.ts29.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, requirePalletsOrSkip, usingPlaygrounds} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {DevUniqueHelper} from './util/playgrounds/unique.dev';2021describe('Scheduling token and balance transfers', () => {22  let superuser: IKeyringPair;23  let alice: IKeyringPair;24  let bob: IKeyringPair;25  let charlie: IKeyringPair;2627  before(async function() {28    await usingPlaygrounds(async (helper, privateKey) => {29      requirePalletsOrSkip(this, helper, [Pallets.Scheduler]);3031      superuser = await privateKey('//Alice');32      const donor = await privateKey({filename: __filename});33      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);3435      await helper.testUtils.enable();36    });37  });3839  itSub('Can delay a transfer of an owned token', async ({helper}) => {40    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});41    const token = await collection.mintToken(alice);42    const schedulerId = await helper.arrange.makeScheduledId();43    const blocksBeforeExecution = 4;44    45    await token.scheduleAfter(schedulerId, blocksBeforeExecution)46      .transfer(alice, {Substrate: bob.address});47    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;4849    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});5051    await helper.wait.forParachainBlockNumber(executionBlock);5253    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});54  });5556  itSub('Can transfer funds periodically', async ({helper}) => {57    const scheduledId = await helper.arrange.makeScheduledId();58    const waitForBlocks = 1;5960    const amount = 1n * helper.balance.getOneTokenNominal();61    const periodic = {62      period: 2,63      repetitions: 2,64    };6566    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);6768    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})69      .balance.transferToSubstrate(alice, bob.address, amount);70    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;7172    await helper.wait.forParachainBlockNumber(executionBlock);7374    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);75    expect(bobsBalanceAfterFirst)76      .to.be.equal(77        bobsBalanceBefore + 1n * amount,78        '#1 Balance of the recipient should be increased by 1 * amount',79      );8081    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);8283    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);84    expect(bobsBalanceAfterSecond)85      .to.be.equal(86        bobsBalanceBefore + 2n * amount,87        '#2 Balance of the recipient should be increased by 2 * amount',88      );89  });9091  itSub('Can cancel a scheduled operation which has not yet taken effect', async ({helper}) => {92    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});93    const token = await collection.mintToken(alice);9495    const scheduledId = await helper.arrange.makeScheduledId();96    const waitForBlocks = 4;9798    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});99100    await token.scheduleAfter(scheduledId, waitForBlocks)101      .transfer(alice, {Substrate: bob.address});102    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;103104    await helper.scheduler.cancelScheduled(alice, scheduledId);105106    await helper.wait.forParachainBlockNumber(executionBlock);107108    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});109  });110111  itSub('Can cancel a periodic operation (transfer of funds)', async ({helper}) => {112    const waitForBlocks = 1;113    const periodic = {114      period: 3,115      repetitions: 2,116    };117118    const scheduledId = await helper.arrange.makeScheduledId();119120    const amount = 1n * helper.balance.getOneTokenNominal();121122    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);123124    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})125      .balance.transferToSubstrate(alice, bob.address, amount);126    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;127128    await helper.wait.forParachainBlockNumber(executionBlock);129130    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);131132    expect(bobsBalanceAfterFirst)133      .to.be.equal(134        bobsBalanceBefore + 1n * amount,135        '#1 Balance of the recipient should be increased by 1 * amount',136      );137138    await helper.scheduler.cancelScheduled(alice, scheduledId);139    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);140141    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);142    expect(bobsBalanceAfterSecond)143      .to.be.equal(144        bobsBalanceAfterFirst,145        '#2 Balance of the recipient should not be changed',146      );147  });148149  itSub.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async ({helper}) => {150    const scheduledId = await helper.arrange.makeScheduledId();151    const waitForBlocks = 4;152153    const initTestVal = 42;154    const changedTestVal = 111;155156    await helper.testUtils.setTestValue(alice, initTestVal);157158    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks)159      .testUtils.setTestValueAndRollback(alice, changedTestVal);160    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;161162    await helper.wait.forParachainBlockNumber(executionBlock);163164    const testVal = await helper.testUtils.testValue();165    expect(testVal, 'The test value should NOT be commited')166      .to.be.equal(initTestVal);167  });168169  itSub.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function({helper}) {170    const scheduledId = await helper.arrange.makeScheduledId();171    const waitForBlocks = 4;172    const periodic = {173      period: 2,174      repetitions: 2,175    };176177    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);178    const scheduledLen = dummyTx.callIndex.length;179180    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))181      .partialFee.toBigInt();182183    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})184      .testUtils.justTakeFee(alice);185    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;186187    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);188    let diff;189190    await helper.wait.forParachainBlockNumber(executionBlock);191192    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);193    expect(194      aliceBalanceAfterFirst < aliceInitBalance,195      '[after execution #1] Scheduled task should take a fee',196    ).to.be.true;197198    diff = aliceInitBalance - aliceBalanceAfterFirst;199    expect(diff).to.be.equal(200      expectedScheduledFee,201      'Scheduled task should take the right amount of fees',202    );203204    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);205206    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);207    expect(208      aliceBalanceAfterSecond < aliceBalanceAfterFirst,209      '[after execution #2] Scheduled task should take a fee',210    ).to.be.true;211212    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;213    expect(diff).to.be.equal(214      expectedScheduledFee,215      'Scheduled task should take the right amount of fees',216    );217  });218219  // Check if we can cancel a scheduled periodic operation220  // in the same block in which it is running221  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.TestUtils], async ({helper}) => {222    const currentBlockNumber = await helper.chain.getLatestBlockNumber();223    const blocksBeforeExecution = 10;224    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;225226    const [227      scheduledId,228      scheduledCancelId,229    ] = await helper.arrange.makeScheduledIds(2);230231    const periodic = {232      period: 5,233      repetitions: 5,234    };235236    const initTestVal = 0;237    const incTestVal = initTestVal + 1;238    const finalTestVal = initTestVal + 2;239240    await helper.testUtils.setTestValue(alice, initTestVal);241242    await helper.scheduler.scheduleAt<DevUniqueHelper>(scheduledId, firstExecutionBlockNumber, {periodic})243      .testUtils.incTestValue(alice);244245    // Cancel the inc tx after 2 executions246    // *in the same block* in which the second execution is scheduled247    await helper.scheduler.scheduleAt(248      scheduledCancelId,249      firstExecutionBlockNumber + periodic.period,250    ).scheduler.cancelScheduled(alice, scheduledId);251252    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);253254    // execution #0255    expect(await helper.testUtils.testValue())256      .to.be.equal(incTestVal);257258    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);259260    // execution #1261    expect(await helper.testUtils.testValue())262      .to.be.equal(finalTestVal);263264    for (let i = 1; i < periodic.repetitions; i++) {265      await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1));266      expect(await helper.testUtils.testValue())267        .to.be.equal(finalTestVal);268    }269  });270271  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => {272    const scheduledId = await helper.arrange.makeScheduledId();273    const waitForBlocks = 4;274    const periodic = {275      period: 2,276      repetitions: 5,277    };278279    const initTestVal = 0;280    const maxTestVal = 2;281282    await helper.testUtils.setTestValue(alice, initTestVal);283284    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})285      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);286    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;287288    await helper.wait.forParachainBlockNumber(executionBlock);289290    // execution #0291    expect(await helper.testUtils.testValue())292      .to.be.equal(initTestVal + 1);293294    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);295296    // execution #1297    expect(await helper.testUtils.testValue())298      .to.be.equal(initTestVal + 2);299300    await helper.wait.forParachainBlockNumber(executionBlock + 2 * periodic.period);301302    // <canceled>303    expect(await helper.testUtils.testValue())304      .to.be.equal(initTestVal + 2);305  });306307  itSub('Root can cancel any scheduled operation', async ({helper}) => {308    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});309    const token = await collection.mintToken(bob);310311    const scheduledId = await helper.arrange.makeScheduledId();312    const waitForBlocks = 4;313314    await token.scheduleAfter(scheduledId, waitForBlocks)315      .transfer(bob, {Substrate: alice.address});316    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;317318    await helper.getSudo().scheduler.cancelScheduled(superuser, scheduledId);319320    await helper.wait.forParachainBlockNumber(executionBlock);321322    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});323  });324325  itSub('Root can set prioritized scheduled operation', async ({helper}) => {326    const scheduledId = await helper.arrange.makeScheduledId();327    const waitForBlocks = 4;328329    const amount = 42n * helper.balance.getOneTokenNominal();330331    const balanceBefore = await helper.balance.getSubstrate(charlie.address);332333    await helper.getSudo()334      .scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42})335      .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount);336    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;337338    await helper.wait.forParachainBlockNumber(executionBlock);339340    const balanceAfter = await helper.balance.getSubstrate(charlie.address);341342    expect(balanceAfter > balanceBefore).to.be.true;343344    const diff = balanceAfter - balanceBefore;345    expect(diff).to.be.equal(amount);346  });347348  itSub("Root can change scheduled operation's priority", async ({helper}) => {349    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});350    const token = await collection.mintToken(bob);351352    const scheduledId = await helper.arrange.makeScheduledId();353    const waitForBlocks = 6;354355    await token.scheduleAfter(scheduledId, waitForBlocks)356      .transfer(bob, {Substrate: alice.address});357    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;358359    const priority = 112;360    await helper.getSudo().scheduler.changePriority(superuser, scheduledId, priority);361362    const priorityChanged = await helper.wait.event(363      waitForBlocks,364      'scheduler',365      'PriorityChanged',366    );367368    expect(priorityChanged !== null).to.be.true;369370    const [blockNumber, index] = priorityChanged!.event.data[0].toJSON() as any[];371    expect(blockNumber).to.be.equal(executionBlock);372    expect(index).to.be.equal(0);373374    expect(priorityChanged!.event.data[1].toString()).to.be.equal(priority.toString());375  });376377  itSub('Prioritized operations execute in valid order', async ({helper}) => {378    const [379      scheduledFirstId,380      scheduledSecondId,381    ] = await helper.arrange.makeScheduledIds(2);382383    const currentBlockNumber = await helper.chain.getLatestBlockNumber();384    const blocksBeforeExecution = 6;385    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;386387    const prioHigh = 0;388    const prioLow = 255;389390    const periodic = {391      period: 6,392      repetitions: 2,393    };394395    const amount = 1n * helper.balance.getOneTokenNominal();396397    // Scheduler a task with a lower priority first, then with a higher priority398    await helper.getSudo().scheduler.scheduleAt(scheduledFirstId, firstExecutionBlockNumber, {priority: prioLow, periodic})399      .balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);400401    await helper.getSudo().scheduler.scheduleAt(scheduledSecondId, firstExecutionBlockNumber, {priority: prioHigh, periodic})402      .balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);403404    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');405406    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);407408    // Flip priorities409    await helper.getSudo().scheduler.changePriority(superuser, scheduledFirstId, prioHigh);410    await helper.getSudo().scheduler.changePriority(superuser, scheduledSecondId, prioLow);411412    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);413414    const dispatchEvents = capture.extractCapturedEvents();415    expect(dispatchEvents.length).to.be.equal(4);416417    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());418419    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];420    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];421422    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);423    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);424425    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);426    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);427  });428429  itSub('Periodic operations always can be rescheduled', async ({helper}) => {430    const maxScheduledPerBlock = 50;431    const numFilledBlocks = 3;432    const ids = await helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1);433    const periodicId = ids[0];434    const fillIds = ids.slice(1);435436    const initTestVal = 0;437    const firstExecTestVal = 1;438    const secondExecTestVal = 2;439    await helper.testUtils.setTestValue(alice, initTestVal);440441    const currentBlockNumber = await helper.chain.getLatestBlockNumber();442    const blocksBeforeExecution = 8;443    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;444445    const period = 5;446447    const periodic = {448      period,449      repetitions: 2,450    };451452    // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur453    const txs = [];454    for (let offset = 0; offset < numFilledBlocks; offset ++) {455      for (let i = 0; i < maxScheduledPerBlock; i++) {456457        const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]);458459        const when = firstExecutionBlockNumber + period + offset;460        const tx = helper.constructApiCall('api.tx.scheduler.scheduleNamed', [fillIds[i + offset * maxScheduledPerBlock], when, null, null, scheduledTx]);461462        txs.push(tx);463      }464    }465    await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true);466467    await helper.scheduler.scheduleAt<DevUniqueHelper>(periodicId, firstExecutionBlockNumber, {periodic})468      .testUtils.incTestValue(alice);469470    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);471    expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal);472473    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + period + numFilledBlocks);474475    // The periodic operation should be postponed by `numFilledBlocks`476    for (let i = 0; i < numFilledBlocks; i++) {477      expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal);478    }479480    // After the `numFilledBlocks` the periodic operation will eventually be executed481    expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal);482  });483484  itSub('scheduled operations does not change nonce', async ({helper}) => {485    const scheduledId = await helper.arrange.makeScheduledId();486    const blocksBeforeExecution = 4;487488    await helper.scheduler489      .scheduleAfter<DevUniqueHelper>(scheduledId, blocksBeforeExecution)490      .balance.transferToSubstrate(alice, bob.address, 1n);491    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;492493    const initNonce = await helper.chain.getNonce(alice.address);494495    await helper.wait.forParachainBlockNumber(executionBlock);496497    const finalNonce = await helper.chain.getNonce(alice.address);498499    expect(initNonce).to.be.equal(finalNonce);500  });501});502503describe('Negative Test: Scheduling', () => {504  let alice: IKeyringPair;505  let bob: IKeyringPair;506507  before(async function() {508    await usingPlaygrounds(async (helper, privateKey) => {509      requirePalletsOrSkip(this, helper, [Pallets.Scheduler]);510511      const donor = await privateKey({filename: __filename});512      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);513514      await helper.testUtils.enable();515    });516  });517518  itSub("Can't overwrite a scheduled ID", async ({helper}) => {519    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});520    const token = await collection.mintToken(alice);521522    const scheduledId = await helper.arrange.makeScheduledId();523    const waitForBlocks = 4;524525    await token.scheduleAfter(scheduledId, waitForBlocks)526      .transfer(alice, {Substrate: bob.address});527    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;528529    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks);530    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))531      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);532533    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);534535    await helper.wait.forParachainBlockNumber(executionBlock);536537    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);538539    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});540    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);541  });542543  itSub("Can't cancel an operation which is not scheduled", async ({helper}) => {544    const scheduledId = await helper.arrange.makeScheduledId();545    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))546      .to.be.rejectedWith(/scheduler\.NotFound/);547  });548549  itSub("Can't cancel a non-owned scheduled operation", async ({helper}) => {550    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});551    const token = await collection.mintToken(alice);552553    const scheduledId = await helper.arrange.makeScheduledId();554    const waitForBlocks = 4;555556    await token.scheduleAfter(scheduledId, waitForBlocks)557      .transfer(alice, {Substrate: bob.address});558    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;559560    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))561      .to.be.rejectedWith(/BadOrigin/);562563    await helper.wait.forParachainBlockNumber(executionBlock);564565    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});566  });567568  itSub("Regular user can't set prioritized scheduled operation", async ({helper}) => {569    const scheduledId = await helper.arrange.makeScheduledId();570    const waitForBlocks = 4;571572    const amount = 42n * helper.balance.getOneTokenNominal();573574    const balanceBefore = await helper.balance.getSubstrate(bob.address);575576    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42});577    578    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))579      .to.be.rejectedWith(/BadOrigin/);580581    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;582583    await helper.wait.forParachainBlockNumber(executionBlock);584585    const balanceAfter = await helper.balance.getSubstrate(bob.address);586587    expect(balanceAfter).to.be.equal(balanceBefore);588  });589590  itSub("Regular user can't change scheduled operation's priority", async ({helper}) => {591    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});592    const token = await collection.mintToken(bob);593594    const scheduledId = await helper.arrange.makeScheduledId();595    const waitForBlocks = 4;596597    await token.scheduleAfter(scheduledId, waitForBlocks)598      .transfer(bob, {Substrate: alice.address});599600    const priority = 112;601    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))602      .to.be.rejectedWith(/BadOrigin/);603604    const priorityChanged = await helper.wait.event(605      waitForBlocks,606      'scheduler',607      'PriorityChanged',608    );609610    expect(priorityChanged === null).to.be.true;611  });612});613614// Implementation of the functionality tested here was postponed/shelved615describe.skip('Sponsoring scheduling', () => {616  // let alice: IKeyringPair;617  // let bob: IKeyringPair;618619  // before(async() => {620  //   await usingApi(async (_, privateKey) => {621  //     alice = privateKey('//Alice');622  //     bob = privateKey('//Bob');623  //   });624  // });625626  it('Can sponsor scheduling a transaction', async () => {627    // const collectionId = await createCollectionExpectSuccess();628    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);629    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');630631    // await usingApi(async api => {632    //   const scheduledId = await makeScheduledId();633    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);634635    //   const bobBalanceBefore = await getFreeBalance(bob);636    //   const waitForBlocks = 4;637    //   // no need to wait to check, fees must be deducted on scheduling, immediately638    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);639    //   const bobBalanceAfter = await getFreeBalance(bob);640    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;641    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;642    //   // wait for sequentiality matters643    //   await waitNewBlocks(waitForBlocks - 1);644    // });645  });646647  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {648    // await usingApi(async (api, privateKey) => {649    //   // Find an empty, unused account650    //   const zeroBalance = await findUnusedAddress(api, privateKey);651652    //   const collectionId = await createCollectionExpectSuccess();653654    //   // Add zeroBalance address to allow list655    //   await enablePublicMintingExpectSuccess(alice, collectionId);656    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);657658    //   // Grace zeroBalance with money, enough to cover future transactions659    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);660    //   await submitTransactionAsync(alice, balanceTx);661662    //   // Mint a fresh NFT663    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');664    //   const scheduledId = await makeScheduledId();665666    //   // Schedule transfer of the NFT a few blocks ahead667    //   const waitForBlocks = 5;668    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);669670    //   // Get rid of the account's funds before the scheduled transaction takes place671    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);672    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);673    //   expect(getGenericResult(events).success).to.be.true;674    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?675    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);676    //   const events = await submitTransactionAsync(alice, sudoTx);677    //   expect(getGenericResult(events).success).to.be.true;*/678679    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions680    //   await waitNewBlocks(waitForBlocks - 3);681682    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));683    // });684  });685686  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {687    // const collectionId = await createCollectionExpectSuccess();688689    // await usingApi(async (api, privateKey) => {690    //   const zeroBalance = await findUnusedAddress(api, privateKey);691    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);692    //   await submitTransactionAsync(alice, balanceTx);693694    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);695    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);696697    //   const scheduledId = await makeScheduledId();698    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);699700    //   const waitForBlocks = 5;701    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);702703    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);704    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);705    //   const events = await submitTransactionAsync(alice, sudoTx);706    //   expect(getGenericResult(events).success).to.be.true;707708    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions709    //   await waitNewBlocks(waitForBlocks - 3);710711    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));712    // });713  });714715  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {716    // const collectionId = await createCollectionExpectSuccess();717    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);718    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');719720    // await usingApi(async (api, privateKey) => {721    //   const zeroBalance = await findUnusedAddress(api, privateKey);722723    //   await enablePublicMintingExpectSuccess(alice, collectionId);724    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);725726    //   const bobBalanceBefore = await getFreeBalance(bob);727728    //   const createData = {nft: {const_data: [], variable_data: []}};729    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);730    //   const scheduledId = await makeScheduledId();731732    //   /*const badTransaction = async function () {733    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);734    //   };735    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/736737    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);738739    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);740    // });741  });742});