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 {Keyring} from '@polkadot/api';38import {IKeyringPair} from '@polkadot/types/types';3940chai.use(chaiAsPromised);41const expect = chai.expect;4243let alice: IKeyringPair;44let bob: IKeyringPair;45let charlie: IKeyringPair;4647describe('integration test: ext. confirmSponsorship():', () => {4849 before(async () => {50 await usingApi(async () => {51 const keyring = new Keyring({type: 'sr25519'});52 alice = keyring.addFromUri('//Alice');53 bob = keyring.addFromUri('//Bob');54 charlie = keyring.addFromUri('//Charlie');55 });56 });5758 it('Confirm collection sponsorship', async () => {59 const collectionId = await createCollectionExpectSuccess();60 await setCollectionSponsorExpectSuccess(collectionId, bob.address);61 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');62 });63 it('Add sponsor to a collection after the same sponsor was already added and confirmed', async () => {64 const collectionId = await createCollectionExpectSuccess();65 await setCollectionSponsorExpectSuccess(collectionId, bob.address);66 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');67 await setCollectionSponsorExpectSuccess(collectionId, bob.address);68 });69 it('Add new sponsor to a collection after another sponsor was already added and confirmed', async () => {70 const collectionId = await createCollectionExpectSuccess();71 await setCollectionSponsorExpectSuccess(collectionId, bob.address);72 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');73 await setCollectionSponsorExpectSuccess(collectionId, charlie.address);74 });7576 it('NFT: Transfer fees are paid by the sponsor after confirmation', async () => {77 const collectionId = await createCollectionExpectSuccess();78 await setCollectionSponsorExpectSuccess(collectionId, bob.address);79 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');8081 await usingApi(async (api) => {82 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();8384 85 const zeroBalance = await findUnusedAddress(api);8687 88 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', zeroBalance.address);8990 91 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);92 const events = await submitTransactionAsync(zeroBalance, zeroToAlice);93 const result = getGenericResult(events);94 expect(result.success).to.be.true;9596 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();9798 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;99 });100101 });102103 it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {104 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});105 await setCollectionSponsorExpectSuccess(collectionId, bob.address);106 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');107108 await usingApi(async (api) => {109 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();110111 112 const zeroBalance = await findUnusedAddress(api);113114 115 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);116117 118 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);119 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);120 const result1 = getGenericResult(events1);121 expect(result1.success).to.be.true;122123 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();124125 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;126 });127 });128129 it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {130 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});131 await setCollectionSponsorExpectSuccess(collectionId, bob.address);132 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');133134 await usingApi(async (api) => {135 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();136137 138 const zeroBalance = await findUnusedAddress(api);139140 141 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);142143 144 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);145 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);146 const result1 = getGenericResult(events1);147148 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();149150 expect(result1.success).to.be.true;151 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;152 });153 });154155 it('CreateItem fees are paid by the sponsor after confirmation', async () => {156 const collectionId = await createCollectionExpectSuccess();157 await setCollectionSponsorExpectSuccess(collectionId, bob.address);158 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');159160 161 await enableAllowListExpectSuccess(alice, collectionId);162163 164 await enablePublicMintingExpectSuccess(alice, collectionId);165166 167 await usingApi(async (api) => {168 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();169170 171 const zeroBalance = await findUnusedAddress(api);172173 174 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);175176 177 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);178179 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();180181 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;182 });183 });184185 it('NFT: Sponsoring of transfers is rate limited', async () => {186 const collectionId = await createCollectionExpectSuccess();187 await setCollectionSponsorExpectSuccess(collectionId, bob.address);188 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');189190 await usingApi(async (api) => {191 192 const zeroBalance = await findUnusedAddress(api);193194 195 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);196197 198 199 const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);200 const events1 = await submitTransactionAsync(alice, aliceToZero);201 const result1 = getGenericResult(events1);202203 204 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();205 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);206 const badTransaction = async function () {207 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);208 };209 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');210 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();211212 213 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);214 await submitTransactionAsync(alice, balancetx);215 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);216 const result2 = getGenericResult(events2);217218 expect(result1.success).to.be.true;219 expect(result2.success).to.be.true;220 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);221 });222 });223224 it('Fungible: Sponsoring is rate limited', async () => {225 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});226 await setCollectionSponsorExpectSuccess(collectionId, bob.address);227 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');228229 await usingApi(async (api) => {230 231 const zeroBalance = await findUnusedAddress(api);232233 234 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);235236 237 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);238 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);239 const result1 = getGenericResult(events1);240 expect(result1.success).to.be.true;241242 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();243 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejected;244 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();245246 247 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);248 await submitTransactionAsync(alice, balancetx);249 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);250 const result2 = getGenericResult(events2);251 expect(result2.success).to.be.true;252253 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);254 });255 });256257 it('ReFungible: Sponsoring is rate limited', async () => {258 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});259 await setCollectionSponsorExpectSuccess(collectionId, bob.address);260 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');261262 await usingApi(async (api) => {263 264 const zeroBalance = await findUnusedAddress(api);265266 267 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);268269 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);270271 272 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);273 const result1 = getGenericResult(events1);274 expect(result1.success).to.be.true;275276 277 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();278 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');279 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();280 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);281282 283 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);284 await submitTransactionAsync(alice, balancetx);285 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);286 const result2 = getGenericResult(events2);287 expect(result2.success).to.be.true;288 });289 });290291 it('NFT: Sponsoring of createItem is rate limited', async () => {292 const collectionId = await createCollectionExpectSuccess();293 await setCollectionSponsorExpectSuccess(collectionId, bob.address);294 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');295296 297 await enableAllowListExpectSuccess(alice, collectionId);298299 300 await enablePublicMintingExpectSuccess(alice, collectionId);301302 await usingApi(async (api) => {303 304 const zeroBalance = await findUnusedAddress(api);305306 307 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);308309 310 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);311312 313 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();314315 const badTransaction = async function () {316 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);317 };318 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');319 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();320321 322 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);323 await submitTransactionAsync(alice, balancetx);324 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);325326 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);327 });328 });329330});331332describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {333 before(async () => {334 await usingApi(async () => {335 const keyring = new Keyring({type: 'sr25519'});336 alice = keyring.addFromUri('//Alice');337 bob = keyring.addFromUri('//Bob');338 charlie = keyring.addFromUri('//Charlie');339 });340 });341342 it('(!negative test!) Confirm sponsorship for a collection that never existed', async () => {343 344 let collectionId = 0;345 await usingApi(async (api) => {346 collectionId = await getCreatedCollectionCount(api) + 1;347 });348349 await confirmSponsorshipExpectFailure(collectionId, '//Bob');350 });351352 it('(!negative test!) Confirm sponsorship using a non-sponsor address', async () => {353 const collectionId = await createCollectionExpectSuccess();354 await setCollectionSponsorExpectSuccess(collectionId, bob.address);355356 await usingApi(async (api) => {357 const transfer = api.tx.balances.transfer(charlie.address, 1e15);358 await submitTransactionAsync(alice, transfer);359 });360361 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');362 });363364 it('(!negative test!) Confirm sponsorship using owner address', async () => {365 const collectionId = await createCollectionExpectSuccess();366 await setCollectionSponsorExpectSuccess(collectionId, bob.address);367 await confirmSponsorshipExpectFailure(collectionId, '//Alice');368 });369370 it('(!negative test!) Confirm sponsorship by collection admin', async () => {371 const collectionId = await createCollectionExpectSuccess();372 await setCollectionSponsorExpectSuccess(collectionId, bob.address);373 await addCollectionAdminExpectSuccess(alice, collectionId, charlie.address);374 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');375 });376377 it('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async () => {378 const collectionId = await createCollectionExpectSuccess();379 await confirmSponsorshipExpectFailure(collectionId, '//Bob');380 });381382 it('(!negative test!) Confirm sponsorship in a collection that was destroyed', async () => {383 const collectionId = await createCollectionExpectSuccess();384 await destroyCollectionExpectSuccess(collectionId);385 await confirmSponsorshipExpectFailure(collectionId, '//Bob');386 });387388 it('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async () => {389 const collectionId = await createCollectionExpectSuccess();390 await setCollectionSponsorExpectSuccess(collectionId, bob.address);391 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');392393 await usingApi(async (api) => {394 395 const ownerZeroBalance = await findUnusedAddress(api);396397 398 const senderZeroBalance = await findUnusedAddress(api);399400 401 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', ownerZeroBalance.address);402403 const sponsorBalanceBeforeTx = (await api.query.system.account(bob.address)).data.free.toBigInt();404405 406 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);407 await expect(submitTransactionExpectFailAsync(senderZeroBalance, zeroToAlice)).to.be.rejected;408409 const sponsorBalanceAfterTx = (await api.query.system.account(bob.address)).data.free.toBigInt();410411 expect(sponsorBalanceAfterTx).to.equal(sponsorBalanceBeforeTx);412 });413 });414});