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

difftreelog

source

tests/src/scheduler.test.ts11.4 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  setCollectionLimitsExpectSuccess,31  getDetailedCollectionInfo,32  getFreeBalance,33  confirmSponsorshipByKeyExpectSuccess,34} from './util/helpers';35import {IKeyringPair} from '@polkadot/types/types';36import {getBalanceSingle} from './substrate/get-balance';3738chai.use(chaiAsPromised);3940describe('Scheduling token and balance transfers', () => {41  let alice: IKeyringPair;42  let bob: IKeyringPair;4344  before(async() => {45    await usingApi(async () => {46      alice = privateKey('//Alice');47      bob = privateKey('//Bob');48    });49  });5051  it('Can schedule a transfer of an owned token with delay', async () => {52    await usingApi(async () => {53      // nft54      const nftCollectionId = await createCollectionExpectSuccess();55      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');56      await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);57      await confirmSponsorshipExpectSuccess(nftCollectionId);5859      await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4);60    });61  });6263  it('Can transfer funds periodically', async () => {64    await usingApi(async (api) => {65      const waitForBlocks = 4;66      const period = 2;67      await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, period, 2);68      const bobsBalanceBefore = await getBalanceSingle(api, bob.address);6970      // discounting already waited-for operations71      await waitNewBlocks(waitForBlocks - 2);72      const bobsBalanceAfterFirst = await getBalanceSingle(api, bob.address);73      expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;7475      await waitNewBlocks(period);76      const bobsBalanceAfterSecond = await getBalanceSingle(api, bob.address);77      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;78    });79  });8081  it('Can sponsor scheduling a transaction', async () => {82    const collectionId = await createCollectionExpectSuccess();83    await setCollectionSponsorExpectSuccess(collectionId, bob.address);84    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');8586    await usingApi(async () => {87      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);8889      const aliceBalanceBefore = await getFreeBalance(alice);90      // no need to wait to check, fees must be deducted on scheduling, immediately91      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, 4);92      const aliceBalanceAfter = await getFreeBalance(alice);93      expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;94    });95  });9697  /*it('Can\'t schedule a transaction with no funds', async () => {98    await usingApi(async (api) => {99      // Find an empty, unused account100      const zeroBalance = await findUnusedAddress(api);101102      const collectionId = await createCollectionExpectSuccess();103      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');104105      await transferExpectSuccess(collectionId, tokenId, alice, zeroBalance);106107      await scheduleTransferAndWaitExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);108    });109  });*/110111  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {112    await usingApi(async (api) => {113      // Find an empty, unused account114      const zeroBalance = await findUnusedAddress(api);115116      const collectionId = await createCollectionExpectSuccess();117118      // Add zeroBalance address to allow list119      await enablePublicMintingExpectSuccess(alice, collectionId);120      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);121122      // Grace zeroBalance with money, enough to cover future transactions123      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);124      await submitTransactionAsync(alice, balanceTx);125126      // Mint a fresh NFT127      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');128129      // Schedule transfer of the NFT a few blocks ahead130      const waitForBlocks = 5;131      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks);132133      // Get rid of the account's funds before the scheduled transaction takes place134      const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?135      const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);136      const events = await submitTransactionAsync(alice, sudoTx);137      expect(getGenericResult(events).success).to.be.true;138139      // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions140      await waitNewBlocks(waitForBlocks - 3);141142      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));143    });144  });145146  /*it.skip('Going bankrupt after sponsoring a scheduled transaction does not nullify the transaction', async () => {147    await usingApi(async (api) => {148      // Find two empty, unused accounts149      const zeroBalance = await findUnusedAddress(api);150      const zeroBalanceSponsor = await findUnusedAddress(api);151152      const collectionId = await createCollectionExpectSuccess();153154      // Grace these with money, enough to cover future transactions155      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);156      await submitTransactionAsync(alice, balanceTx);157158      // Grace these with money, enough to cover future transactions159      const balanceSponsorTx = api.tx.balances.transfer(zeroBalanceSponsor.address, 1n * UNIQUE);160      await submitTransactionAsync(alice, balanceSponsorTx);161162      // Set a collection sponsor163      await setCollectionSponsorExpectSuccess(collectionId, zeroBalanceSponsor.address);164      await confirmSponsorshipExpectSuccess(collectionId);165166      // Add zeroBalance address to allow list167      await enablePublicMintingExpectSuccess(alice, collectionId);168      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);169170      // Mint a fresh NFT171      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');172173      // Schedule transfer of the NFT a few blocks ahead174      // const waitForBlocks = 4;175      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);176      //await waitAfterScheduleExpectSuccess(collectionId, tokenId, alice, 3);177178      // Get rid of the account's funds before the scheduled transaction takes place179      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalanceSponsor.address, 0, 0);180      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);181      const events = await submitTransactionAsync(alice, sudoTx);182      expect(getGenericResult(events).success).to.be.true;183184      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions185      await waitNewBlocks(4);186187      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));188    });189  });*/190191  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {192    const collectionId = await createCollectionExpectSuccess();193194    await usingApi(async (api) => {195      const zeroBalance = await findUnusedAddress(api);196197      /*await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {198        sponsoredDataRateLimit: 2,199      });*/200      //console.log(await getDetailedCollectionInfo(api, nftCollectionId));201      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);202      await submitTransactionAsync(alice, balanceTx);203204      await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);205      await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);206207      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);208209      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, 5);210211      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);212      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);213      const events = await submitTransactionAsync(alice, sudoTx);214      expect(getGenericResult(events).success).to.be.true;215216      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions217      await waitNewBlocks(2);218219      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));220    });221  });222});223224describe.skip('Scheduling EVM smart contracts', () => {225  let alice: IKeyringPair;226  let bob: IKeyringPair;227228  before(async() => {229    await usingApi(async () => {230      alice = privateKey('//Alice');231      bob = privateKey('//Bob');232    });233  });234235  // todo contract testing236  it.skip('NFT: Sponsoring of transfers is rate limited', async () => {237    const collectionId = await createCollectionExpectSuccess();238    await setCollectionSponsorExpectSuccess(collectionId, bob.address);239    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');240241    await usingApi(async (api) => {242      // Find unused address243      const zeroBalance = await findUnusedAddress(api);244245      // Mint token for alice246      const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);247248      // Transfer this token from Alice to unused address and back249      // Alice to Zero gets sponsored250      const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);251      const events1 = await submitTransactionAsync(alice, aliceToZero);252      const result1 = getGenericResult(events1);253254      // Second transfer should fail255      const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();256      const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);257      const badTransaction = async function () {258        await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);259      };260      await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');261      const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();262263      // Try again after Zero gets some balance - now it should succeed264      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);265      await submitTransactionAsync(alice, balancetx);266      const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);267      const result2 = getGenericResult(events2);268269      expect(result1.success).to.be.true;270      expect(result2.success).to.be.true;271      expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);272    });273  });274});