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 requirePallets,37 Pallets,38} from './util/helpers';39import {IKeyringPair} from '@polkadot/types/types';4041chai.use(chaiAsPromised);42const expect = chai.expect;4344let alice: IKeyringPair;45let bob: IKeyringPair;46let charlie: IKeyringPair;4748describe('integration test: ext. confirmSponsorship():', () => {4950 before(async () => {51 await usingApi(async (api, privateKeyWrapper) => {52 alice = privateKeyWrapper('//Alice');53 bob = privateKeyWrapper('//Bob');54 charlie = privateKeyWrapper('//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, privateKeyWrapper) => {82 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();8384 85 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);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, privateKeyWrapper) => {109 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();110111 112 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);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 function() {130 await requirePallets(this, [Pallets.ReFungible]);131132 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});133 await setCollectionSponsorExpectSuccess(collectionId, bob.address);134 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');135136 await usingApi(async (api, privateKeyWrapper) => {137 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();138139 140 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);141142 143 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);144145 146 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);147 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);148 const result1 = getGenericResult(events1);149150 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();151152 expect(result1.success).to.be.true;153 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;154 });155 });156157 it('CreateItem fees are paid by the sponsor after confirmation', async () => {158 const collectionId = await createCollectionExpectSuccess();159 await setCollectionSponsorExpectSuccess(collectionId, bob.address);160 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');161162 163 await enableAllowListExpectSuccess(alice, collectionId);164165 166 await enablePublicMintingExpectSuccess(alice, collectionId);167168 169 await usingApi(async (api, privateKeyWrapper) => {170 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();171172 173 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);174175 176 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);177178 179 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);180181 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();182183 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;184 });185 });186187 it('NFT: Sponsoring of transfers is rate limited', async () => {188 const collectionId = await createCollectionExpectSuccess();189 await setCollectionSponsorExpectSuccess(collectionId, bob.address);190 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');191192 await usingApi(async (api, privateKeyWrapper) => {193 194 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);195196 197 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);198199 200 201 const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);202 const events1 = await submitTransactionAsync(alice, aliceToZero);203 const result1 = getGenericResult(events1);204205 206 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();207 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);208 const badTransaction = async function () {209 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);210 };211 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');212 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();213214 215 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);216 await submitTransactionAsync(alice, balancetx);217 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);218 const result2 = getGenericResult(events2);219220 expect(result1.success).to.be.true;221 expect(result2.success).to.be.true;222 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);223 });224 });225226 it('Fungible: Sponsoring is rate limited', async () => {227 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});228 await setCollectionSponsorExpectSuccess(collectionId, bob.address);229 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');230231 await usingApi(async (api, privateKeyWrapper) => {232 233 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);234235 236 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);237238 239 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);240 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);241 const result1 = getGenericResult(events1);242 expect(result1.success).to.be.true;243244 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();245 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejected;246 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();247248 249 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);250 await submitTransactionAsync(alice, balancetx);251 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);252 const result2 = getGenericResult(events2);253 expect(result2.success).to.be.true;254255 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);256 });257 });258259 it('ReFungible: Sponsoring is rate limited', async function() {260 await requirePallets(this, [Pallets.ReFungible]);261262 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});263 await setCollectionSponsorExpectSuccess(collectionId, bob.address);264 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');265266 await usingApi(async (api, privateKeyWrapper) => {267 268 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);269270 271 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);272273 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);274275 276 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);277 const result1 = getGenericResult(events1);278 expect(result1.success).to.be.true;279280 281 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();282 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');283 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();284 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);285286 287 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);288 await submitTransactionAsync(alice, balancetx);289 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);290 const result2 = getGenericResult(events2);291 expect(result2.success).to.be.true;292 });293 });294295 it('NFT: Sponsoring of createItem is rate limited', async () => {296 const collectionId = await createCollectionExpectSuccess();297 await setCollectionSponsorExpectSuccess(collectionId, bob.address);298 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');299300 301 await enableAllowListExpectSuccess(alice, collectionId);302303 304 await enablePublicMintingExpectSuccess(alice, collectionId);305306 await usingApi(async (api, privateKeyWrapper) => {307 308 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);309310 311 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);312313 314 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);315316 317 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();318319 const badTransaction = async function () {320 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);321 };322 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');323 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();324325 326 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);327 await submitTransactionAsync(alice, balancetx);328 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);329330 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);331 });332 });333334});335336describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {337 before(async () => {338 await usingApi(async (api, privateKeyWrapper) => {339 alice = privateKeyWrapper('//Alice');340 bob = privateKeyWrapper('//Bob');341 charlie = privateKeyWrapper('//Charlie');342 });343 });344345 it('(!negative test!) Confirm sponsorship for a collection that never existed', async () => {346 347 let collectionId = 0;348 await usingApi(async (api) => {349 collectionId = await getCreatedCollectionCount(api) + 1;350 });351352 await confirmSponsorshipExpectFailure(collectionId, '//Bob');353 });354355 it('(!negative test!) Confirm sponsorship using a non-sponsor address', async () => {356 const collectionId = await createCollectionExpectSuccess();357 await setCollectionSponsorExpectSuccess(collectionId, bob.address);358359 await usingApi(async (api) => {360 const transfer = api.tx.balances.transfer(charlie.address, 1e15);361 await submitTransactionAsync(alice, transfer);362 });363364 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');365 });366367 it('(!negative test!) Confirm sponsorship using owner address', async () => {368 const collectionId = await createCollectionExpectSuccess();369 await setCollectionSponsorExpectSuccess(collectionId, bob.address);370 await confirmSponsorshipExpectFailure(collectionId, '//Alice');371 });372373 it('(!negative test!) Confirm sponsorship by collection admin', async () => {374 const collectionId = await createCollectionExpectSuccess();375 await setCollectionSponsorExpectSuccess(collectionId, bob.address);376 await addCollectionAdminExpectSuccess(alice, collectionId, charlie.address);377 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');378 });379380 it('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async () => {381 const collectionId = await createCollectionExpectSuccess();382 await confirmSponsorshipExpectFailure(collectionId, '//Bob');383 });384385 it('(!negative test!) Confirm sponsorship in a collection that was destroyed', async () => {386 const collectionId = await createCollectionExpectSuccess();387 await destroyCollectionExpectSuccess(collectionId);388 await confirmSponsorshipExpectFailure(collectionId, '//Bob');389 });390391 it('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async () => {392 const collectionId = await createCollectionExpectSuccess();393 await setCollectionSponsorExpectSuccess(collectionId, bob.address);394 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');395396 await usingApi(async (api, privateKeyWrapper) => {397 398 const ownerZeroBalance = await findUnusedAddress(api, privateKeyWrapper);399400 401 const senderZeroBalance = await findUnusedAddress(api, privateKeyWrapper);402403 404 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', ownerZeroBalance.address);405406 const sponsorBalanceBeforeTx = (await api.query.system.account(bob.address)).data.free.toBigInt();407408 409 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);410 await expect(submitTransactionExpectFailAsync(senderZeroBalance, zeroToAlice)).to.be.rejected;411412 const sponsorBalanceAfterTx = (await api.query.system.account(bob.address)).data.free.toBigInt();413414 expect(sponsorBalanceAfterTx).to.equal(sponsorBalanceBeforeTx);415 });416 });417});