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

difftreelog

source

tests/src/scheduler.test.ts9.2 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56import chai, {expect} from 'chai';7import chaiAsPromised from 'chai-as-promised';8import privateKey from './substrate/privateKey';9import {10  default as usingApi, 11  submitTransactionAsync,12  submitTransactionExpectFailAsync,13} from './substrate/substrate-api';14import {15  createItemExpectSuccess,16  createCollectionExpectSuccess,17  scheduleTransferExpectSuccess,18  scheduleTransferAndWaitExpectSuccess,19  setCollectionSponsorExpectSuccess,20  confirmSponsorshipExpectSuccess,21  findUnusedAddress,22  UNIQUE,23  enablePublicMintingExpectSuccess,24  addToAllowListExpectSuccess,25  waitNewBlocks,26  normalizeAccountId,27  getTokenOwner,28  getGenericResult,29  scheduleTransferFundsPeriodicExpectSuccess,30  getFreeBalance,31  confirmSponsorshipByKeyExpectSuccess,32} from './util/helpers';33import {IKeyringPair} from '@polkadot/types/types';34import {getBalanceSingle} from './substrate/get-balance';3536chai.use(chaiAsPromised);3738describe('Scheduling token and balance transfers', () => {39  let alice: IKeyringPair;40  let bob: IKeyringPair;4142  before(async() => {43    await usingApi(async () => {44      alice = privateKey('//Alice');45      bob = privateKey('//Bob');46    });47  });4849  it('Can schedule a transfer of an owned token with delay', async () => {50    await usingApi(async () => {51      // nft52      const nftCollectionId = await createCollectionExpectSuccess();53      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');54      await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);55      await confirmSponsorshipExpectSuccess(nftCollectionId);5657      await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4);58    });59  });6061  it('Can transfer funds periodically', async () => {62    await usingApi(async (api) => {63      const waitForBlocks = 4;64      const period = 2;65      await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, period, 2);66      const bobsBalanceBefore = await getBalanceSingle(api, bob.address);6768      // discounting already waited-for operations69      await waitNewBlocks(waitForBlocks - 2);70      const bobsBalanceAfterFirst = await getBalanceSingle(api, bob.address);71      expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;7273      await waitNewBlocks(period);74      const bobsBalanceAfterSecond = await getBalanceSingle(api, bob.address);75      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;76    });77  });7879  it('Can sponsor scheduling a transaction', async () => {80    const collectionId = await createCollectionExpectSuccess();81    await setCollectionSponsorExpectSuccess(collectionId, bob.address);82    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');8384    await usingApi(async () => {85      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);8687      const aliceBalanceBefore = await getFreeBalance(alice);88      // no need to wait to check, fees must be deducted on scheduling, immediately89      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, 4);90      const aliceBalanceAfter = await getFreeBalance(alice);91      expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;92    });93  });9495  /*it('Can\'t schedule a transaction with no funds', async () => {96    await usingApi(async (api) => {97      // Find an empty, unused account98      const zeroBalance = await findUnusedAddress(api);99100      const collectionId = await createCollectionExpectSuccess();101      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');102103      await transferExpectSuccess(collectionId, tokenId, alice, zeroBalance);104105      await scheduleTransferAndWaitExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);106    });107  });*/108109  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {110    await usingApi(async (api) => {111      // Find an empty, unused account112      const zeroBalance = await findUnusedAddress(api);113114      const collectionId = await createCollectionExpectSuccess();115116      // Add zeroBalance address to allow list117      await enablePublicMintingExpectSuccess(alice, collectionId);118      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);119120      // Grace zeroBalance with money, enough to cover future transactions121      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);122      await submitTransactionAsync(alice, balanceTx);123124      // Mint a fresh NFT125      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');126127      // Schedule transfer of the NFT a few blocks ahead128      const waitForBlocks = 5;129      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks);130131      // Get rid of the account's funds before the scheduled transaction takes place132      const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?133      const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);134      const events = await submitTransactionAsync(alice, sudoTx);135      expect(getGenericResult(events).success).to.be.true;136137      // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions138      await waitNewBlocks(waitForBlocks - 3);139140      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));141    });142  });143144  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {145    const collectionId = await createCollectionExpectSuccess();146147    await usingApi(async (api) => {148      const zeroBalance = await findUnusedAddress(api);149150      /*await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {151        sponsoredDataRateLimit: 2,152      });*/153      //console.log(await getDetailedCollectionInfo(api, nftCollectionId));154      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);155      await submitTransactionAsync(alice, balanceTx);156157      await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);158      await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);159160      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);161162      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, 5);163164      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);165      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);166      const events = await submitTransactionAsync(alice, sudoTx);167      expect(getGenericResult(events).success).to.be.true;168169      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions170      await waitNewBlocks(2);171172      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));173    });174  });175});176177describe.skip('Scheduling EVM smart contracts', () => {178  let alice: IKeyringPair;179  let bob: IKeyringPair;180181  before(async() => {182    await usingApi(async () => {183      alice = privateKey('//Alice');184      bob = privateKey('//Bob');185    });186  });187188  // todo contract testing189  it.skip('NFT: Sponsoring of transfers is rate limited', async () => {190    const collectionId = await createCollectionExpectSuccess();191    await setCollectionSponsorExpectSuccess(collectionId, bob.address);192    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');193194    await usingApi(async (api) => {195      // Find unused address196      const zeroBalance = await findUnusedAddress(api);197198      // Mint token for alice199      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);200201      // Transfer this token from Alice to unused address and back202      // Alice to Zero gets sponsored203      const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);204      const events1 = await submitTransactionAsync(alice, aliceToZero);205      const result1 = getGenericResult(events1);206207      // Second transfer should fail208      const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();209      const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);210      const badTransaction = async function () {211        await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);212      };213      await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');214      const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();215216      // Try again after Zero gets some balance - now it should succeed217      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);218      await submitTransactionAsync(alice, balancetx);219      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);220      const result2 = getGenericResult(events2);221222      expect(result1.success).to.be.true;223      expect(result2.success).to.be.true;224      expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);225    });226  });227});