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

difftreelog

fix(test) remove token schedulers from tests

PraetorP2023-09-14parent: #04e6d94.patch.diff
in: master

3 files changed

modifiedtests/src/maintenance.seqtest.tsdiffbeforeafterboth
--- a/tests/src/maintenance.seqtest.ts
+++ b/tests/src/maintenance.seqtest.ts
@@ -192,19 +192,22 @@
       const blocksToWait = 6;
 
       // Scheduling works before the maintenance
-      await nftBeforeMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM})
-        .transfer(bob, {Substrate: superuser.address});
+      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM})
+        .nft.transferToken(bob, collection.collectionId, nftBeforeMM.tokenId, {Substrate: superuser.address});
+
 
       await helper.wait.newBlocks(blocksToWait + 1);
       expect(await nftBeforeMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
 
       // Schedule a transaction that should occur *during* the maintenance
-      await nftDuringMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM})
-        .transfer(bob, {Substrate: superuser.address});
+      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM})
+        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
+
 
       // Schedule a transaction that should occur *after* the maintenance
-      await nftDuringMM.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM})
-        .transfer(bob, {Substrate: superuser.address});
+      await helper.scheduler.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM})
+        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
+
 
       await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
       expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
@@ -214,16 +217,16 @@
       expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: bob.address});
 
       // Any attempts to schedule a tx during the MM should be rejected
-      await expect(nftDuringMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM})
-        .transfer(bob, {Substrate: superuser.address}))
+      await expect(helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM})
+        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address}))
         .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
 
       await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
       expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
 
       // Scheduling works after the maintenance
-      await nftAfterMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM})
-        .transfer(bob, {Substrate: superuser.address});
+      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM})
+        .nft.transferToken(bob, collection.collectionId, nftAfterMM.tokenId, {Substrate: superuser.address});
 
       await helper.wait.newBlocks(blocksToWait + 1);
 
modifiedtests/src/scheduler.seqtest.tsdiffbeforeafterboth
before · tests/src/scheduler.seqtest.ts
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, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {DevUniqueHelper, Event} 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.UniqueScheduler]);3031      superuser = await privateKey('//Alice');32      const donor = await privateKey({url: import.meta.url});33      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);3435      await helper.testUtils.enable();36    });37  });3839  beforeEach(async () => {40    await usingPlaygrounds(async (helper) => {41      await helper.wait.noScheduledTasks();42    });43  });4445  itSched('Can delay a transfer of an owned token', async (scheduleKind, {helper}) => {46    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});47    const token = await collection.mintToken(alice);48    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;49    const blocksBeforeExecution = 4;5051    await token.scheduleAfter(blocksBeforeExecution, {scheduledId})52      .transfer(alice, {Substrate: bob.address});53    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;5455    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});5657    await helper.wait.forParachainBlockNumber(executionBlock);5859    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});60  });6162  itSched('Can transfer funds periodically', async (scheduleKind, {helper}) => {63    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;64    const waitForBlocks = 1;6566    const amount = 1n * helper.balance.getOneTokenNominal();67    const periodic = {68      period: 2,69      repetitions: 2,70    };7172    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);7374    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic})75      .balance.transferToSubstrate(alice, bob.address, amount);76    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;7778    await helper.wait.forParachainBlockNumber(executionBlock);7980    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);81    expect(bobsBalanceAfterFirst)82      .to.be.equal(83        bobsBalanceBefore + 1n * amount,84        '#1 Balance of the recipient should be increased by 1 * amount',85      );8687    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);8889    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);90    expect(bobsBalanceAfterSecond)91      .to.be.equal(92        bobsBalanceBefore + 2n * amount,93        '#2 Balance of the recipient should be increased by 2 * amount',94      );95  });9697  itSub('Can cancel a scheduled operation which has not yet taken effect', async ({helper}) => {98    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});99    const token = await collection.mintToken(alice);100101    const scheduledId = helper.arrange.makeScheduledId();102    const waitForBlocks = 4;103104    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});105106    await token.scheduleAfter(waitForBlocks, {scheduledId})107      .transfer(alice, {Substrate: bob.address});108    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;109110    await helper.scheduler.cancelScheduled(alice, scheduledId);111112    await helper.wait.forParachainBlockNumber(executionBlock);113114    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});115  });116117  itSub('Can cancel a periodic operation (transfer of funds)', async ({helper}) => {118    const waitForBlocks = 1;119    const periodic = {120      period: 3,121      repetitions: 2,122    };123124    const scheduledId = helper.arrange.makeScheduledId();125126    const amount = 1n * helper.balance.getOneTokenNominal();127128    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);129130    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic})131      .balance.transferToSubstrate(alice, bob.address, amount);132    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;133134    await helper.wait.forParachainBlockNumber(executionBlock);135136    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);137138    expect(bobsBalanceAfterFirst)139      .to.be.equal(140        bobsBalanceBefore + 1n * amount,141        '#1 Balance of the recipient should be increased by 1 * amount',142      );143144    await helper.scheduler.cancelScheduled(alice, scheduledId);145    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);146147    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);148    expect(bobsBalanceAfterSecond)149      .to.be.equal(150        bobsBalanceAfterFirst,151        '#2 Balance of the recipient should not be changed',152      );153  });154155  itSched('scheduler will not insert more tasks than allowed', async (scheduleKind, {helper}) => {156    const maxScheduledPerBlock = 50;157    let fillScheduledIds = new Array(maxScheduledPerBlock);158    let extraScheduledId = undefined;159160    if(scheduleKind == 'named') {161      const scheduledIds = helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1);162      fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock);163      extraScheduledId = scheduledIds[maxScheduledPerBlock];164    }165166    // Since the dev node has Instant Seal,167    // we need a larger gap between the current block and the target one.168    //169    // We will schedule `maxScheduledPerBlock` transaction into the target block,170    // so we need at least `maxScheduledPerBlock`-wide gap.171    // We add some additional blocks to this gap to mitigate possible PolkadotJS delays.172    const waitForBlocks = await helper.arrange.isDevNode() ? maxScheduledPerBlock + 5 : 5;173174    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks;175176    const amount = 1n * helper.balance.getOneTokenNominal();177178    const balanceBefore = await helper.balance.getSubstrate(bob.address);179180    // Fill the target block181    for(let i = 0; i < maxScheduledPerBlock; i++) {182      await helper.scheduler.scheduleAt(executionBlock, {scheduledId: fillScheduledIds[i]})183        .balance.transferToSubstrate(superuser, bob.address, amount);184    }185186    // Try to schedule a task into a full block187    await expect(helper.scheduler.scheduleAt(executionBlock, {scheduledId: extraScheduledId})188      .balance.transferToSubstrate(superuser, bob.address, amount))189      .to.be.rejectedWith(/scheduler\.AgendaIsExhausted/);190191    await helper.wait.forParachainBlockNumber(executionBlock);192193    const balanceAfter = await helper.balance.getSubstrate(bob.address);194195    expect(balanceAfter > balanceBefore).to.be.true;196197    const diff = balanceAfter - balanceBefore;198    expect(diff).to.be.equal(amount * BigInt(maxScheduledPerBlock));199  });200201  itSched.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async (scheduleKind, {helper}) => {202    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;203    const waitForBlocks = 4;204205    const initTestVal = 42;206    const changedTestVal = 111;207208    await helper.testUtils.setTestValue(alice, initTestVal);209210    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId})211      .testUtils.setTestValueAndRollback(alice, changedTestVal);212    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;213214    await helper.wait.forParachainBlockNumber(executionBlock);215216    const testVal = await helper.testUtils.testValue();217    expect(testVal, 'The test value should NOT be commited')218      .to.be.equal(initTestVal);219  });220221  itSched.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function(scheduleKind, {helper}) {222    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;223    const waitForBlocks = 4;224    const periodic = {225      period: 2,226      repetitions: 2,227    };228229    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);230    const scheduledLen = dummyTx.callIndex.length;231232    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))233      .partialFee.toBigInt();234235    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId, periodic})236      .testUtils.justTakeFee(alice);237    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;238239    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);240    let diff;241242    await helper.wait.forParachainBlockNumber(executionBlock);243244    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);245    expect(246      aliceBalanceAfterFirst < aliceInitBalance,247      '[after execution #1] Scheduled task should take a fee',248    ).to.be.true;249250    diff = aliceInitBalance - aliceBalanceAfterFirst;251    expect(diff).to.be.equal(252      expectedScheduledFee,253      'Scheduled task should take the right amount of fees',254    );255256    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);257258    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);259    expect(260      aliceBalanceAfterSecond < aliceBalanceAfterFirst,261      '[after execution #2] Scheduled task should take a fee',262    ).to.be.true;263264    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;265    expect(diff).to.be.equal(266      expectedScheduledFee,267      'Scheduled task should take the right amount of fees',268    );269  });270271  // Check if we can cancel a scheduled periodic operation272  // in the same block in which it is running273  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.TestUtils], async ({helper}) => {274    const currentBlockNumber = await helper.chain.getLatestBlockNumber();275    const blocksBeforeExecution = 10;276    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;277278    const [279      scheduledId,280      scheduledCancelId,281    ] = helper.arrange.makeScheduledIds(2);282283    const periodic = {284      period: 5,285      repetitions: 5,286    };287288    const initTestVal = 0;289    const incTestVal = initTestVal + 1;290    const finalTestVal = initTestVal + 2;291292    await helper.testUtils.setTestValue(alice, initTestVal);293294    await helper.scheduler.scheduleAt<DevUniqueHelper>(firstExecutionBlockNumber, {scheduledId, periodic})295      .testUtils.incTestValue(alice);296297    // Cancel the inc tx after 2 executions298    // *in the same block* in which the second execution is scheduled299    await helper.scheduler.scheduleAt(300      firstExecutionBlockNumber + periodic.period,301      {scheduledId: scheduledCancelId},302    ).scheduler.cancelScheduled(alice, scheduledId);303304    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);305306    // execution #0307    expect(await helper.testUtils.testValue())308      .to.be.equal(incTestVal);309310    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);311312    // execution #1313    expect(await helper.testUtils.testValue())314      .to.be.equal(finalTestVal);315316    for(let i = 1; i < periodic.repetitions; i++) {317      await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1));318      expect(await helper.testUtils.testValue())319        .to.be.equal(finalTestVal);320    }321  });322323  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => {324    const scheduledId = helper.arrange.makeScheduledId();325    const waitForBlocks = 4;326    const periodic = {327      period: 2,328      repetitions: 5,329    };330331    const initTestVal = 0;332    const maxTestVal = 2;333334    await helper.testUtils.setTestValue(alice, initTestVal);335336    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId, periodic})337      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);338    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;339340    await helper.wait.forParachainBlockNumber(executionBlock);341342    // execution #0343    expect(await helper.testUtils.testValue())344      .to.be.equal(initTestVal + 1);345346    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);347348    // execution #1349    expect(await helper.testUtils.testValue())350      .to.be.equal(initTestVal + 2);351352    await helper.wait.forParachainBlockNumber(executionBlock + 2 * periodic.period);353354    // <canceled>355    expect(await helper.testUtils.testValue())356      .to.be.equal(initTestVal + 2);357  });358359  itSub('Root can cancel any scheduled operation', async ({helper}) => {360    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});361    const token = await collection.mintToken(bob);362363    const scheduledId = helper.arrange.makeScheduledId();364    const waitForBlocks = 4;365366    await token.scheduleAfter(waitForBlocks, {scheduledId})367      .transfer(bob, {Substrate: alice.address});368    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;369370    await helper.getSudo().scheduler.cancelScheduled(superuser, scheduledId);371372    await helper.wait.forParachainBlockNumber(executionBlock);373374    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});375  });376377  itSched('Root can set prioritized scheduled operation', async (scheduleKind, {helper}) => {378    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;379    const waitForBlocks = 4;380381    const amount = 42n * helper.balance.getOneTokenNominal();382383    const balanceBefore = await helper.balance.getSubstrate(charlie.address);384385    await helper.getSudo()386      .scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42})387      .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount);388    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;389390    await helper.wait.forParachainBlockNumber(executionBlock);391392    const balanceAfter = await helper.balance.getSubstrate(charlie.address);393394    expect(balanceAfter > balanceBefore).to.be.true;395396    const diff = balanceAfter - balanceBefore;397    expect(diff).to.be.equal(amount);398  });399400  itSub("Root can change scheduled operation's priority", async ({helper}) => {401    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});402    const token = await collection.mintToken(bob);403404    const scheduledId = helper.arrange.makeScheduledId();405    const waitForBlocks = 6;406407    await token.scheduleAfter(waitForBlocks, {scheduledId})408      .transfer(bob, {Substrate: alice.address});409    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;410411    const priority = 112;412    await helper.getSudo().scheduler.changePriority(superuser, scheduledId, priority);413414    const priorityChanged = await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged);415416    const [blockNumber, index] = priorityChanged.task();417    expect(blockNumber).to.be.equal(executionBlock);418    expect(index).to.be.equal(0);419420    expect(priorityChanged.priority().toString()).to.be.equal(priority.toString());421  });422423  itSub('Prioritized operations execute in valid order', async ({helper}) => {424    const [425      scheduledFirstId,426      scheduledSecondId,427    ] = helper.arrange.makeScheduledIds(2);428429    const currentBlockNumber = await helper.chain.getLatestBlockNumber();430    const blocksBeforeExecution = 6;431    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;432433    const prioHigh = 0;434    const prioLow = 255;435436    const periodic = {437      period: 6,438      repetitions: 2,439    };440441    const amount = 1n * helper.balance.getOneTokenNominal();442443    // Scheduler a task with a lower priority first, then with a higher priority444    await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, {445      scheduledId: scheduledFirstId,446      priority: prioLow,447      periodic,448    }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);449450    await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, {451      scheduledId: scheduledSecondId,452      priority: prioHigh,453      periodic,454    }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);455456    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');457458    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);459460    // Flip priorities461    await helper.getSudo().scheduler.changePriority(superuser, scheduledFirstId, prioHigh);462    await helper.getSudo().scheduler.changePriority(superuser, scheduledSecondId, prioLow);463464    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);465466    const dispatchEvents = capture.extractCapturedEvents();467    expect(dispatchEvents.length).to.be.equal(4);468469    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());470471    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];472    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];473474    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);475    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);476477    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);478    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);479  });480481  itSched('Periodic operations always can be rescheduled', async (scheduleKind, {helper}) => {482    const maxScheduledPerBlock = 50;483    const numFilledBlocks = 3;484    const ids = helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1);485    const periodicId = scheduleKind == 'named' ? ids[0] : undefined;486    const fillIds = ids.slice(1);487488    const fillScheduleFn = scheduleKind == 'named' ? 'scheduleNamed' : 'schedule';489490    const initTestVal = 0;491    const firstExecTestVal = 1;492    const secondExecTestVal = 2;493    await helper.testUtils.setTestValue(alice, initTestVal);494495    const currentBlockNumber = await helper.chain.getLatestBlockNumber();496    const blocksBeforeExecution = 8;497    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;498499    const period = 5;500501    const periodic = {502      period,503      repetitions: 2,504    };505506    // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur507    const txs = [];508    for(let offset = 0; offset < numFilledBlocks; offset ++) {509      for(let i = 0; i < maxScheduledPerBlock; i++) {510511        const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]);512513        const when = firstExecutionBlockNumber + period + offset;514        const mandatoryArgs = [when, null, null, scheduledTx];515        const scheduleArgs = scheduleKind == 'named'516          ? [fillIds[i + offset * maxScheduledPerBlock], ...mandatoryArgs]517          : mandatoryArgs;518519        const tx = helper.constructApiCall(`api.tx.scheduler.${fillScheduleFn}`, scheduleArgs);520521        txs.push(tx);522      }523    }524    await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true);525526    await helper.scheduler.scheduleAt<DevUniqueHelper>(firstExecutionBlockNumber, {527      scheduledId: periodicId,528      periodic,529    }).testUtils.incTestValue(alice);530531    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);532    expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal);533534    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + period + numFilledBlocks);535536    // The periodic operation should be postponed by `numFilledBlocks`537    for(let i = 0; i < numFilledBlocks; i++) {538      expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal);539    }540541    // After the `numFilledBlocks` the periodic operation will eventually be executed542    expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal);543  });544545  itSched('scheduled operations does not change nonce', async (scheduleKind, {helper}) => {546    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;547    const blocksBeforeExecution = 4;548549    await helper.scheduler550      .scheduleAfter<DevUniqueHelper>(blocksBeforeExecution, {scheduledId})551      .balance.transferToSubstrate(alice, bob.address, 1n);552    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;553554    const initNonce = await helper.chain.getNonce(alice.address);555556    await helper.wait.forParachainBlockNumber(executionBlock);557558    const finalNonce = await helper.chain.getNonce(alice.address);559560    expect(initNonce).to.be.equal(finalNonce);561  });562});563564describe('Negative Test: Scheduling', () => {565  let alice: IKeyringPair;566  let bob: IKeyringPair;567568  before(async function() {569    await usingPlaygrounds(async (helper, privateKey) => {570      requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]);571572      const donor = await privateKey({url: import.meta.url});573      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);574575      await helper.testUtils.enable();576    });577  });578579  itSub("Can't overwrite a scheduled ID", async ({helper}) => {580    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});581    const token = await collection.mintToken(alice);582583    const scheduledId = helper.arrange.makeScheduledId();584    const waitForBlocks = 4;585586    await token.scheduleAfter(waitForBlocks, {scheduledId})587      .transfer(alice, {Substrate: bob.address});588    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;589590    const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId});591    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))592      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);593594    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);595596    await helper.wait.forParachainBlockNumber(executionBlock);597598    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);599600    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});601    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);602  });603604  itSub("Can't cancel an operation which is not scheduled", async ({helper}) => {605    const scheduledId = helper.arrange.makeScheduledId();606    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))607      .to.be.rejectedWith(/scheduler\.NotFound/);608  });609610  itSub("Can't cancel a non-owned scheduled operation", async ({helper}) => {611    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});612    const token = await collection.mintToken(alice);613614    const scheduledId = helper.arrange.makeScheduledId();615    const waitForBlocks = 4;616617    await token.scheduleAfter(waitForBlocks, {scheduledId})618      .transfer(alice, {Substrate: bob.address});619    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;620621    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))622      .to.be.rejectedWith(/BadOrigin/);623624    await helper.wait.forParachainBlockNumber(executionBlock);625626    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});627  });628629  itSched("Regular user can't set prioritized scheduled operation", async (scheduleKind, {helper}) => {630    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;631    const waitForBlocks = 4;632633    const amount = 42n * helper.balance.getOneTokenNominal();634635    const balanceBefore = await helper.balance.getSubstrate(bob.address);636637    const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42});638639    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))640      .to.be.rejectedWith(/BadOrigin/);641642    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;643644    await helper.wait.forParachainBlockNumber(executionBlock);645646    const balanceAfter = await helper.balance.getSubstrate(bob.address);647648    expect(balanceAfter).to.be.equal(balanceBefore);649  });650651  itSub("Regular user can't change scheduled operation's priority", async ({helper}) => {652    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});653    const token = await collection.mintToken(bob);654655    const scheduledId = helper.arrange.makeScheduledId();656    const waitForBlocks = 4;657658    await token.scheduleAfter(waitForBlocks, {scheduledId})659      .transfer(bob, {Substrate: alice.address});660661    const priority = 112;662    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))663      .to.be.rejectedWith(/BadOrigin/);664665    await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged);666  });667});668669// Implementation of the functionality tested here was postponed/shelved670describe.skip('Sponsoring scheduling', () => {671  // let alice: IKeyringPair;672  // let bob: IKeyringPair;673674  // before(async() => {675  //   await usingApi(async (_, privateKey) => {676  //     alice = privateKey('//Alice');677  //     bob = privateKey('//Bob');678  //   });679  // });680681  it('Can sponsor scheduling a transaction', async () => {682    // const collectionId = await createCollectionExpectSuccess();683    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);684    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');685686    // await usingApi(async api => {687    //   const scheduledId = await makeScheduledId();688    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);689690    //   const bobBalanceBefore = await getFreeBalance(bob);691    //   const waitForBlocks = 4;692    //   // no need to wait to check, fees must be deducted on scheduling, immediately693    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);694    //   const bobBalanceAfter = await getFreeBalance(bob);695    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;696    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;697    //   // wait for sequentiality matters698    //   await waitNewBlocks(waitForBlocks - 1);699    // });700  });701702  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {703    // await usingApi(async (api, privateKey) => {704    //   // Find an empty, unused account705    //   const zeroBalance = await findUnusedAddress(api, privateKey);706707    //   const collectionId = await createCollectionExpectSuccess();708709    //   // Add zeroBalance address to allow list710    //   await enablePublicMintingExpectSuccess(alice, collectionId);711    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);712713    //   // Grace zeroBalance with money, enough to cover future transactions714    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);715    //   await submitTransactionAsync(alice, balanceTx);716717    //   // Mint a fresh NFT718    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');719    //   const scheduledId = await makeScheduledId();720721    //   // Schedule transfer of the NFT a few blocks ahead722    //   const waitForBlocks = 5;723    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);724725    //   // Get rid of the account's funds before the scheduled transaction takes place726    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);727    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);728    //   expect(getGenericResult(events).success).to.be.true;729    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?730    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);731    //   const events = await submitTransactionAsync(alice, sudoTx);732    //   expect(getGenericResult(events).success).to.be.true;*/733734    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions735    //   await waitNewBlocks(waitForBlocks - 3);736737    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));738    // });739  });740741  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {742    // const collectionId = await createCollectionExpectSuccess();743744    // await usingApi(async (api, privateKey) => {745    //   const zeroBalance = await findUnusedAddress(api, privateKey);746    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);747    //   await submitTransactionAsync(alice, balanceTx);748749    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);750    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);751752    //   const scheduledId = await makeScheduledId();753    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);754755    //   const waitForBlocks = 5;756    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);757758    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);759    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);760    //   const events = await submitTransactionAsync(alice, sudoTx);761    //   expect(getGenericResult(events).success).to.be.true;762763    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions764    //   await waitNewBlocks(waitForBlocks - 3);765766    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));767    // });768  });769770  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {771    // const collectionId = await createCollectionExpectSuccess();772    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);773    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');774775    // await usingApi(async (api, privateKey) => {776    //   const zeroBalance = await findUnusedAddress(api, privateKey);777778    //   await enablePublicMintingExpectSuccess(alice, collectionId);779    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);780781    //   const bobBalanceBefore = await getFreeBalance(bob);782783    //   const createData = {nft: {const_data: [], variable_data: []}};784    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);785    //   const scheduledId = await makeScheduledId();786787    //   /*const badTransaction = async function () {788    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);789    //   };790    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/791792    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);793794    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);795    // });796  });797});
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -609,7 +609,7 @@
     this.wait = new WaitGroup(this);
   }
 
-  getSudo<T extends UniqueHelper>() {
+  getSudo<T extends AstarHelper>() {
     // eslint-disable-next-line @typescript-eslint/naming-convention
     const SudoHelperType = SudoHelper(this.helperBase);
     return this.clone(SudoHelperType) as T;
@@ -636,7 +636,7 @@
     super(logger, options);
     this.wait = new WaitGroup(this);
   }
-  getSudo<T extends UniqueHelper>() {
+  getSudo<T extends AcalaHelper>() {
     // eslint-disable-next-line @typescript-eslint/naming-convention
     const SudoHelperType = SudoHelper(this.helperBase);
     return this.clone(SudoHelperType) as T;