1234567891011121314151617import chai from 'chai';18import chaiAsPromised from 'chai-as-promised';19import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';20import {21 createCollectionExpectSuccess,22 setCollectionSponsorExpectSuccess,23 destroyCollectionExpectSuccess,24 confirmSponsorshipExpectSuccess,25 confirmSponsorshipExpectFailure,26 createItemExpectSuccess,27 findUnusedAddress,28 getGenericResult,29 enableAllowListExpectSuccess,30 enablePublicMintingExpectSuccess,31 addToAllowListExpectSuccess,32 normalizeAccountId,33 addCollectionAdminExpectSuccess,34 getCreatedCollectionCount,35 UNIQUE,36} from './util/helpers';37import {IKeyringPair} from '@polkadot/types/types';3839chai.use(chaiAsPromised);40const expect = chai.expect;4142let alice: IKeyringPair;43let bob: IKeyringPair;44let charlie: IKeyringPair;4546describe('integration test: ext. confirmSponsorship():', () => {4748 before(async () => {49 await usingApi(async (api, privateKeyWrapper) => {50 alice = privateKeyWrapper('//Alice');51 bob = privateKeyWrapper('//Bob');52 charlie = privateKeyWrapper('//Charlie');53 });54 });5556 it('Confirm collection sponsorship', async () => {57 const collectionId = await createCollectionExpectSuccess();58 await setCollectionSponsorExpectSuccess(collectionId, bob.address);59 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');60 });61 it('Add sponsor to a collection after the same sponsor was already added and confirmed', async () => {62 const collectionId = await createCollectionExpectSuccess();63 await setCollectionSponsorExpectSuccess(collectionId, bob.address);64 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');65 await setCollectionSponsorExpectSuccess(collectionId, bob.address);66 });67 it('Add new sponsor to a collection after another sponsor was already added and confirmed', async () => {68 const collectionId = await createCollectionExpectSuccess();69 await setCollectionSponsorExpectSuccess(collectionId, bob.address);70 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');71 await setCollectionSponsorExpectSuccess(collectionId, charlie.address);72 });7374 it('NFT: Transfer fees are paid by the sponsor after confirmation', async () => {75 const collectionId = await createCollectionExpectSuccess();76 await setCollectionSponsorExpectSuccess(collectionId, bob.address);77 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');7879 await usingApi(async (api, privateKeyWrapper) => {80 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();8182 83 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);8485 86 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', zeroBalance.address);8788 89 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);90 const events = await submitTransactionAsync(zeroBalance, zeroToAlice);91 const result = getGenericResult(events);92 expect(result.success).to.be.true;9394 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();9596 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;97 });9899 });100101 it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {102 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});103 await setCollectionSponsorExpectSuccess(collectionId, bob.address);104 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');105106 await usingApi(async (api, privateKeyWrapper) => {107 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();108109 110 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);111112 113 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);114115 116 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);117 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);118 const result1 = getGenericResult(events1);119 expect(result1.success).to.be.true;120121 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();122123 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;124 });125 });126127 it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {128 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});129 await setCollectionSponsorExpectSuccess(collectionId, bob.address);130 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');131132 await usingApi(async (api, privateKeyWrapper) => {133 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();134135 136 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);137138 139 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);140141 142 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);143 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);144 const result1 = getGenericResult(events1);145146 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();147148 expect(result1.success).to.be.true;149 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;150 });151 });152153 it('CreateItem fees are paid by the sponsor after confirmation', async () => {154 const collectionId = await createCollectionExpectSuccess();155 await setCollectionSponsorExpectSuccess(collectionId, bob.address);156 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');157158 159 await enableAllowListExpectSuccess(alice, collectionId);160161 162 await enablePublicMintingExpectSuccess(alice, collectionId);163164 165 await usingApi(async (api, privateKeyWrapper) => {166 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();167168 169 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);170171 172 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);173174 175 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);176177 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();178179 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;180 });181 });182183 it('NFT: Sponsoring of transfers is rate limited', async () => {184 const collectionId = await createCollectionExpectSuccess();185 await setCollectionSponsorExpectSuccess(collectionId, bob.address);186 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');187188 await usingApi(async (api, privateKeyWrapper) => {189 190 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);191192 193 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);194195 196 197 const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);198 const events1 = await submitTransactionAsync(alice, aliceToZero);199 const result1 = getGenericResult(events1);200201 202 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();203 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);204 const badTransaction = async function () {205 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);206 };207 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');208 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();209210 211 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);212 await submitTransactionAsync(alice, balancetx);213 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);214 const result2 = getGenericResult(events2);215216 expect(result1.success).to.be.true;217 expect(result2.success).to.be.true;218 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);219 });220 });221222 it('Fungible: Sponsoring is rate limited', async () => {223 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});224 await setCollectionSponsorExpectSuccess(collectionId, bob.address);225 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');226227 await usingApi(async (api, privateKeyWrapper) => {228 229 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);230231 232 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);233234 235 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);236 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);237 const result1 = getGenericResult(events1);238 expect(result1.success).to.be.true;239240 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();241 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejected;242 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();243244 245 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);246 await submitTransactionAsync(alice, balancetx);247 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);248 const result2 = getGenericResult(events2);249 expect(result2.success).to.be.true;250251 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);252 });253 });254255 it('ReFungible: Sponsoring is rate limited', async () => {256 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});257 await setCollectionSponsorExpectSuccess(collectionId, bob.address);258 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');259260 await usingApi(async (api, privateKeyWrapper) => {261 262 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);263264 265 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);266267 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);268269 270 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);271 const result1 = getGenericResult(events1);272 expect(result1.success).to.be.true;273274 275 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();276 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');277 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();278 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);279280 281 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);282 await submitTransactionAsync(alice, balancetx);283 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);284 const result2 = getGenericResult(events2);285 expect(result2.success).to.be.true;286 });287 });288289 it('NFT: Sponsoring of createItem is rate limited', async () => {290 const collectionId = await createCollectionExpectSuccess();291 await setCollectionSponsorExpectSuccess(collectionId, bob.address);292 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');293294 295 await enableAllowListExpectSuccess(alice, collectionId);296297 298 await enablePublicMintingExpectSuccess(alice, collectionId);299300 await usingApi(async (api, privateKeyWrapper) => {301 302 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);303304 305 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);306307 308 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);309310 311 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();312313 const badTransaction = async function () {314 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);315 };316 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');317 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();318319 320 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);321 await submitTransactionAsync(alice, balancetx);322 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);323324 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);325 });326 });327328});329330describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {331 before(async () => {332 await usingApi(async (api, privateKeyWrapper) => {333 alice = privateKeyWrapper('//Alice');334 bob = privateKeyWrapper('//Bob');335 charlie = privateKeyWrapper('//Charlie');336 });337 });338339 it('(!negative test!) Confirm sponsorship for a collection that never existed', async () => {340 341 let collectionId = 0;342 await usingApi(async (api) => {343 collectionId = await getCreatedCollectionCount(api) + 1;344 });345346 await confirmSponsorshipExpectFailure(collectionId, '//Bob');347 });348349 it('(!negative test!) Confirm sponsorship using a non-sponsor address', async () => {350 const collectionId = await createCollectionExpectSuccess();351 await setCollectionSponsorExpectSuccess(collectionId, bob.address);352353 await usingApi(async (api) => {354 const transfer = api.tx.balances.transfer(charlie.address, 1e15);355 await submitTransactionAsync(alice, transfer);356 });357358 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');359 });360361 it('(!negative test!) Confirm sponsorship using owner address', async () => {362 const collectionId = await createCollectionExpectSuccess();363 await setCollectionSponsorExpectSuccess(collectionId, bob.address);364 await confirmSponsorshipExpectFailure(collectionId, '//Alice');365 });366367 it('(!negative test!) Confirm sponsorship by collection admin', async () => {368 const collectionId = await createCollectionExpectSuccess();369 await setCollectionSponsorExpectSuccess(collectionId, bob.address);370 await addCollectionAdminExpectSuccess(alice, collectionId, charlie.address);371 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');372 });373374 it('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async () => {375 const collectionId = await createCollectionExpectSuccess();376 await confirmSponsorshipExpectFailure(collectionId, '//Bob');377 });378379 it('(!negative test!) Confirm sponsorship in a collection that was destroyed', async () => {380 const collectionId = await createCollectionExpectSuccess();381 await destroyCollectionExpectSuccess(collectionId);382 await confirmSponsorshipExpectFailure(collectionId, '//Bob');383 });384385 it('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async () => {386 const collectionId = await createCollectionExpectSuccess();387 await setCollectionSponsorExpectSuccess(collectionId, bob.address);388 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');389390 await usingApi(async (api, privateKeyWrapper) => {391 392 const ownerZeroBalance = await findUnusedAddress(api, privateKeyWrapper);393394 395 const senderZeroBalance = await findUnusedAddress(api, privateKeyWrapper);396397 398 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', ownerZeroBalance.address);399400 const sponsorBalanceBeforeTx = (await api.query.system.account(bob.address)).data.free.toBigInt();401402 403 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);404 await expect(submitTransactionExpectFailAsync(senderZeroBalance, zeroToAlice)).to.be.rejected;405406 const sponsorBalanceAfterTx = (await api.query.system.account(bob.address)).data.free.toBigInt();407408 expect(sponsorBalanceAfterTx).to.equal(sponsorBalanceBeforeTx);409 });410 });411});