1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import {ApiPromise} from '@polkadot/api';19import chai from 'chai';20import chaiAsPromised from 'chai-as-promised';21import privateKey from './substrate/privateKey';22import {default as usingApi} from './substrate/substrate-api';23import {24 approveExpectFail,25 approveExpectSuccess,26 createCollectionExpectSuccess,27 createItemExpectSuccess,28 destroyCollectionExpectSuccess,29 setCollectionLimitsExpectSuccess,30 transferExpectSuccess,31 addCollectionAdminExpectSuccess,32 adminApproveFromExpectSuccess,33 getCreatedCollectionCount,34 transferFromExpectSuccess,35 transferFromExpectFail,36} from './util/helpers';3738chai.use(chaiAsPromised);3940describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {41 let alice: IKeyringPair;42 let bob: IKeyringPair;43 let charlie: IKeyringPair;4445 before(async () => {46 await usingApi(async () => {47 alice = privateKey('//Alice');48 bob = privateKey('//Bob');49 charlie = privateKey('//Charlie');50 });51 });5253 it('Execute the extrinsic and check approvedList', async () => {54 const nftCollectionId = await createCollectionExpectSuccess();55 56 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');57 await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);58 59 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});60 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');61 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);62 63 const reFungibleCollectionId =64 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});65 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');66 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address);67 });6869 it('Remove approval by using 0 amount', async () => {70 const nftCollectionId = await createCollectionExpectSuccess();71 72 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');73 await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address, 1);74 await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address, 0);75 76 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});77 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');78 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 1);79 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 0);80 81 const reFungibleCollectionId =82 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});83 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');84 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 1);85 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);86 });8788 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {89 const collectionId = await createCollectionExpectSuccess();90 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);9192 await adminApproveFromExpectSuccess(collectionId, itemId, alice, bob.address, charlie.address);93 });94});9596describe('Normal user can approve other users to transfer:', () => {97 let alice: IKeyringPair;98 let bob: IKeyringPair;99 let charlie: IKeyringPair;100101 before(async () => {102 await usingApi(async () => {103 alice = privateKey('//Alice');104 bob = privateKey('//Bob');105 charlie = privateKey('//Charlie');106 });107 }); 108109 it('NFT', async () => {110 const collectionId = await createCollectionExpectSuccess();111 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);112 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);113 });114115 it('Fungible up to an approved amount', async () => {116 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});117 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address); 118 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);119 });120121 it('ReFungible up to an approved amount', async () => {122 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});123 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);124 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);125 });126});127128describe('Approved users can transferFrom up to approved amount:', () => {129 let alice: IKeyringPair;130 let bob: IKeyringPair;131 let charlie: IKeyringPair;132133 before(async () => {134 await usingApi(async () => {135 alice = privateKey('//Alice');136 bob = privateKey('//Bob');137 charlie = privateKey('//Charlie');138 });139 }); 140141 it('NFT', async () => {142 const collectionId = await createCollectionExpectSuccess();143 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);144 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);145 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');146 });147148 it('Fungible up to an approved amount', async () => {149 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});150 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address); 151 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);152 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');153 });154155 it('ReFungible up to an approved amount', async () => {156 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});157 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);158 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);159 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');160 });161});162163describe('Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:', () => {164 let alice: IKeyringPair;165 let bob: IKeyringPair;166 let charlie: IKeyringPair;167168 before(async () => {169 await usingApi(async () => {170 alice = privateKey('//Alice');171 bob = privateKey('//Bob');172 charlie = privateKey('//Charlie');173 });174 }); 175176 it('NFT', async () => {177 const collectionId = await createCollectionExpectSuccess();178 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);179 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);180 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');181 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);182 });183184 it('Fungible up to an approved amount', async () => {185 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});186 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address); 187 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);188 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');189 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);190 });191192 it('ReFungible up to an approved amount', async () => {193 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});194 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);195 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);196 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');197 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);198 });199});200201describe('Approved amount decreases by the transferred amount.:', () => {202 let alice: IKeyringPair;203 let bob: IKeyringPair;204 let charlie: IKeyringPair;205 let dave: IKeyringPair;206207 before(async () => {208 await usingApi(async () => {209 alice = privateKey('//Alice');210 bob = privateKey('//Bob');211 charlie = privateKey('//Charlie');212 dave = privateKey('//Dave');213 });214 }); 215216 it('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async () => {217 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});218 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address); 219 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 10);220 await transferFromExpectSuccess(collectionId, itemId, bob, alice, charlie, 2, 'Fungible');221 await transferFromExpectSuccess(collectionId, itemId, bob, alice, dave, 8, 'Fungible');222 });223});224225describe('User may clear the approvals to approving for 0 amount:', () => {226 let alice: IKeyringPair;227 let bob: IKeyringPair;228 let charlie: IKeyringPair;229230 before(async () => {231 await usingApi(async () => {232 alice = privateKey('//Alice');233 bob = privateKey('//Bob');234 charlie = privateKey('//Charlie');235 });236 });237238 it('NFT', async () => {239 const collectionId = await createCollectionExpectSuccess();240 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT');241 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);242 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 0);243 await transferFromExpectFail(collectionId, itemId, bob, bob, charlie, 1);244 });245246 it('Fungible', async () => {247 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});248 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');249 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 1);250 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 0);251 await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, bob, charlie, 1);252 });253254 it('ReFungible', async () => {255 const reFungibleCollectionId =256 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});257 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');258 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 1);259 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);260 await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, bob, charlie, 1);261 });262});263264describe('User cannot approve for the amount greater than they own:', () => {265 let alice: IKeyringPair;266 let bob: IKeyringPair;267 let charlie: IKeyringPair;268269 before(async () => {270 await usingApi(async () => {271 alice = privateKey('//Alice');272 bob = privateKey('//Bob');273 charlie = privateKey('//Charlie');274 });275 });276277 it('1 for NFT', async () => {278 const collectionId = await createCollectionExpectSuccess();279 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);280 await approveExpectFail(collectionId, itemId, bob, charlie, 2);281 });282283 it('Fungible', async () => {284 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});285 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');286 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, bob, charlie, 11);287 });288289 it('ReFungible', async () => {290 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});291 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');292 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, charlie, 101);293 });294});295296describe('Administrator and collection owner do not need approval in order to execute TransferFrom:', () => {297 let alice: IKeyringPair;298 let bob: IKeyringPair;299 let charlie: IKeyringPair;300 let dave: IKeyringPair;301302 before(async () => {303 await usingApi(async () => {304 alice = privateKey('//Alice');305 bob = privateKey('//Bob');306 charlie = privateKey('//Charlie');307 dave = privateKey('//Dave');308 });309 }); 310311 it('NFT', async () => {312 const collectionId = await createCollectionExpectSuccess();313 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);314 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');315 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);316 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'NFT');317 });318319 it('Fungible up to an approved amount', async () => {320 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});321 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address); 322 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');323 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);324 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'Fungible');325 });326327 it('ReFungible up to an approved amount', async () => {328 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});329 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);330 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');331 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);332 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'ReFungible');333 });334});335336describe('Repeated approvals add up', () => {337 let alice: IKeyringPair;338 let bob: IKeyringPair;339 let charlie: IKeyringPair;340 let dave: IKeyringPair;341342 before(async () => {343 await usingApi(async () => {344 alice = privateKey('//Alice');345 bob = privateKey('//Bob');346 charlie = privateKey('//Charlie');347 dave = privateKey('//Dave');348 });349 }); 350351 it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async () => {352 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});353 await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address);354 await approveExpectSuccess(collectionId, 0, alice, bob.address, 1);355 await approveExpectSuccess(collectionId, 0, alice, charlie.address, 1);356 357 358 359 });360361 it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async () => {362 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});363 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);364 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);365 await approveExpectSuccess(collectionId, itemId, alice, charlie.address, 1);366 367 368 369 });370371 372 it.skip('Cannot approve for more than total user`s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async () => {373 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});374 await createItemExpectSuccess(alice, collectionId, 'Fungible', dave.address);375 await approveExpectSuccess(collectionId, 0, dave, bob.address, 5);376 await approveExpectFail(collectionId, 0, dave, charlie, 6);377 });378379 380 it.skip('Cannot approve for more than total users amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async () => {381 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});382 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', dave.address);383 await approveExpectSuccess(collectionId, itemId, dave, bob.address, 50);384 await approveExpectFail(collectionId, itemId, dave, charlie, 51);385 });386});387388describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {389 let alice: IKeyringPair;390 let bob: IKeyringPair;391 let charlie: IKeyringPair;392393 before(async () => {394 await usingApi(async () => {395 alice = privateKey('//Alice');396 bob = privateKey('//Bob');397 charlie = privateKey('//Charlie');398 });399 });400401 it('can be called by collection admin on non-owned item', async () => {402 const collectionId = await createCollectionExpectSuccess();403 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);404405 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);406 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);407 });408});409410describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {411 let alice: IKeyringPair;412 let bob: IKeyringPair;413 let charlie: IKeyringPair;414415 before(async () => {416 await usingApi(async () => {417 alice = privateKey('//Alice');418 bob = privateKey('//Bob');419 charlie = privateKey('//Charlie');420 });421 });422423 it('Approve for a collection that does not exist', async () => {424 await usingApi(async (api: ApiPromise) => {425 426 const nftCollectionCount = await getCreatedCollectionCount(api);427 await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);428 429 const fungibleCollectionCount = await getCreatedCollectionCount(api);430 await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);431 432 const reFungibleCollectionCount = await getCreatedCollectionCount(api);433 await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);434 });435 });436437 it('Approve for a collection that was destroyed', async () => {438 439 const nftCollectionId = await createCollectionExpectSuccess();440 await destroyCollectionExpectSuccess(nftCollectionId);441 await approveExpectFail(nftCollectionId, 1, alice, bob);442 443 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});444 await destroyCollectionExpectSuccess(fungibleCollectionId);445 await approveExpectFail(fungibleCollectionId, 0, alice, bob);446 447 const reFungibleCollectionId =448 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});449 await destroyCollectionExpectSuccess(reFungibleCollectionId);450 await approveExpectFail(reFungibleCollectionId, 1, alice, bob);451 });452453 it('Approve transfer of a token that does not exist', async () => {454 455 const nftCollectionId = await createCollectionExpectSuccess();456 await approveExpectFail(nftCollectionId, 2, alice, bob);457 458 const reFungibleCollectionId =459 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});460 await approveExpectFail(reFungibleCollectionId, 2, alice, bob);461 });462463 it('Approve using the address that does not own the approved token', async () => {464 const nftCollectionId = await createCollectionExpectSuccess();465 466 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');467 await approveExpectFail(nftCollectionId, newNftTokenId, bob, alice);468 469 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});470 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');471 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice);472 473 const reFungibleCollectionId =474 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});475 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');476 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice);477 });478479 it('should fail if approved more ReFungibles than owned', async () => {480 const nftCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});481 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'ReFungible');482 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 100, 'ReFungible');483 await approveExpectSuccess(nftCollectionId, newNftTokenId, bob, alice.address, 100);484 await approveExpectFail(nftCollectionId, newNftTokenId, bob, alice, 101);485 });486487 it('should fail if approved more Fungibles than owned', async () => {488 const nftCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});489 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'Fungible');490 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 10, 'Fungible');491 await approveExpectSuccess(nftCollectionId, newNftTokenId, bob, alice.address, 10);492 await approveExpectFail(nftCollectionId, newNftTokenId, bob, alice, 11);493 });494495 it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {496 const collectionId = await createCollectionExpectSuccess();497 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);498 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});499500 await approveExpectFail(collectionId, itemId, alice, charlie);501 });502});