1234567891011121314151617import chai, {expect} from 'chai';18import chaiAsPromised from 'chai-as-promised';19import privateKey from './substrate/privateKey';20import {21 default as usingApi, 22 submitTransactionAsync,23} from './substrate/substrate-api';24import {25 createItemExpectSuccess,26 createCollectionExpectSuccess,27 scheduleTransferExpectSuccess,28 scheduleTransferAndWaitExpectSuccess,29 setCollectionSponsorExpectSuccess,30 confirmSponsorshipExpectSuccess,31 findUnusedAddress,32 UNIQUE,33 enablePublicMintingExpectSuccess,34 addToAllowListExpectSuccess,35 waitNewBlocks,36 normalizeAccountId,37 getTokenOwner,38 getGenericResult,39 scheduleTransferFundsPeriodicExpectSuccess,40 getFreeBalance,41 confirmSponsorshipByKeyExpectSuccess,42 scheduleExpectFailure,43} from './util/helpers';44import {IKeyringPair} from '@polkadot/types/types';4546chai.use(chaiAsPromised);4748describe.skip('Scheduling token and balance transfers', () => {49 let alice: IKeyringPair;50 let bob: IKeyringPair;51 let scheduledIdBase: string;52 let scheduledIdSlider: number;5354 before(async() => {55 await usingApi(async () => {56 alice = privateKey('//Alice');57 bob = privateKey('//Bob');58 });5960 scheduledIdBase = '0x' + '0'.repeat(31);61 scheduledIdSlider = 0;62 });6364 65 function makeScheduledId(): string {66 return scheduledIdBase + ((scheduledIdSlider++) % 10);67 }6869 it('Can schedule a transfer of an owned token with delay', async () => {70 await usingApi(async () => {71 const nftCollectionId = await createCollectionExpectSuccess();72 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');73 await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);74 await confirmSponsorshipExpectSuccess(nftCollectionId);7576 await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4, makeScheduledId());77 });78 });7980 it('Can transfer funds periodically', async () => {81 await usingApi(async () => {82 const waitForBlocks = 4;83 const period = 2;84 await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, 2);85 const bobsBalanceBefore = await getFreeBalance(bob);8687 88 await waitNewBlocks(waitForBlocks - 2);89 const bobsBalanceAfterFirst = await getFreeBalance(bob);90 expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;9192 await waitNewBlocks(period);93 const bobsBalanceAfterSecond = await getFreeBalance(bob);94 expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;95 });96 });9798 it('Can sponsor scheduling a transaction', async () => {99 const collectionId = await createCollectionExpectSuccess();100 await setCollectionSponsorExpectSuccess(collectionId, bob.address);101 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');102103 await usingApi(async () => {104 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);105106 const bobBalanceBefore = await getFreeBalance(bob);107 const waitForBlocks = 4;108 109 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());110 const bobBalanceAfter = await getFreeBalance(bob);111 112 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;113 114 await waitNewBlocks(waitForBlocks - 1);115 });116 });117118 it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {119 await usingApi(async (api) => {120 121 const zeroBalance = await findUnusedAddress(api);122123 const collectionId = await createCollectionExpectSuccess();124125 126 await enablePublicMintingExpectSuccess(alice, collectionId);127 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);128129 130 const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);131 await submitTransactionAsync(alice, balanceTx);132133 134 const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');135136 137 const waitForBlocks = 5;138 await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());139140 141 const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);142 const events = await submitTransactionAsync(zeroBalance, balanceTx2);143 expect(getGenericResult(events).success).to.be.true;144 145146147148149 150 await waitNewBlocks(waitForBlocks - 3);151152 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));153 });154 });155156 it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {157 const collectionId = await createCollectionExpectSuccess();158159 await usingApi(async (api) => {160 const zeroBalance = await findUnusedAddress(api);161 const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);162 await submitTransactionAsync(alice, balanceTx);163164 await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);165 await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);166167 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);168169 const waitForBlocks = 5;170 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());171172 const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);173 const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);174 const events = await submitTransactionAsync(alice, sudoTx);175 expect(getGenericResult(events).success).to.be.true;176177 178 await waitNewBlocks(waitForBlocks - 3);179180 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));181 });182 });183184 it.skip('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {185 const collectionId = await createCollectionExpectSuccess();186 await setCollectionSponsorExpectSuccess(collectionId, bob.address);187 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');188189 await usingApi(async (api) => {190 const zeroBalance = await findUnusedAddress(api);191192 await enablePublicMintingExpectSuccess(alice, collectionId);193 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);194195 const bobBalanceBefore = await getFreeBalance(bob);196197 const createData = {nft: {const_data: [], variable_data: []}};198 const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);199200 201202203204205 await scheduleExpectFailure(creationTx, zeroBalance, 3, makeScheduledId(), 1, 3);206207 expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);208 });209 });210});