12345import {IKeyringPair} from '@polkadot/types/types';6import {ApiPromise} from '@polkadot/api';7import chai from 'chai';8import chaiAsPromised from 'chai-as-promised';9import privateKey from './substrate/privateKey';10import {default as usingApi} from './substrate/substrate-api';11import {12 approveExpectFail,13 approveExpectSuccess,14 createCollectionExpectSuccess,15 createItemExpectSuccess,16 destroyCollectionExpectSuccess,17 setCollectionLimitsExpectSuccess,18 transferExpectSuccess,19 addCollectionAdminExpectSuccess,20 adminApproveFromExpectSuccess,21 getCreatedCollectionCount,22 transferFromExpectSuccess,23 transferFromExpectFail,24} from './util/helpers';2526chai.use(chaiAsPromised);27const expect = chai.expect;2829describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {30 let alice: IKeyringPair;31 let bob: IKeyringPair;32 let charlie: IKeyringPair;3334 before(async () => {35 await usingApi(async () => {36 alice = privateKey('//Alice');37 bob = privateKey('//Bob');38 charlie = privateKey('//Charlie');39 });40 });4142 it('Execute the extrinsic and check approvedList', async () => {43 const nftCollectionId = await createCollectionExpectSuccess();44 45 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');46 await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);47 48 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});49 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');50 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);51 52 const reFungibleCollectionId =53 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});54 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');55 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address);56 });5758 it('Remove approval by using 0 amount', async () => {59 const nftCollectionId = await createCollectionExpectSuccess();60 61 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');62 await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address, 1);63 await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address, 0);64 65 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});66 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');67 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 1);68 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 0);69 70 const reFungibleCollectionId =71 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});72 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');73 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 1);74 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);75 });7677 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {78 const collectionId = await createCollectionExpectSuccess();79 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);8081 await adminApproveFromExpectSuccess(collectionId, itemId, alice, bob.address, charlie.address);82 });83});8485describe('Normal user can approve other users to transfer:', () => {86 let alice: IKeyringPair;87 let bob: IKeyringPair;88 let charlie: IKeyringPair;8990 before(async () => {91 await usingApi(async () => {92 alice = privateKey('//Alice');93 bob = privateKey('//Bob');94 charlie = privateKey('//Charlie');95 });96 }); 9798 it('NFT', async () => {99 const collectionId = await createCollectionExpectSuccess();100 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);101 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);102 });103104 it('Fungible up to an approved amount', async () => {105 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});106 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address); 107 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);108 });109110 it('ReFungible up to an approved amount', async () => {111 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });112 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);113 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);114 });115});116117describe('Approved users can transferFrom up to approved amount:', () => {118 let alice: IKeyringPair;119 let bob: IKeyringPair;120 let charlie: IKeyringPair;121122 before(async () => {123 await usingApi(async () => {124 alice = privateKey('//Alice');125 bob = privateKey('//Bob');126 charlie = privateKey('//Charlie');127 });128 }); 129130 it('NFT', async () => {131 const collectionId = await createCollectionExpectSuccess();132 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);133 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);134 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');135 });136137 it('Fungible up to an approved amount', async () => {138 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});139 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address); 140 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);141 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');142 });143144 it('ReFungible up to an approved amount', async () => {145 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });146 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);147 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);148 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');149 });150});151152describe('Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:', () => {153 let alice: IKeyringPair;154 let bob: IKeyringPair;155 let charlie: IKeyringPair;156157 before(async () => {158 await usingApi(async () => {159 alice = privateKey('//Alice');160 bob = privateKey('//Bob');161 charlie = privateKey('//Charlie');162 });163 }); 164165 it('NFT', async () => {166 const collectionId = await createCollectionExpectSuccess();167 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);168 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);169 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');170 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);171 });172173 it('Fungible up to an approved amount', async () => {174 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});175 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address); 176 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);177 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');178 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);179 });180181 it('ReFungible up to an approved amount', async () => {182 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });183 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);184 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);185 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');186 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);187 });188});189190describe('Approved amount decreases by the transferred amount.:', () => {191 let alice: IKeyringPair;192 let bob: IKeyringPair;193 let charlie: IKeyringPair;194 let dave: IKeyringPair;195196 before(async () => {197 await usingApi(async () => {198 alice = privateKey('//Alice');199 bob = privateKey('//Bob');200 charlie = privateKey('//Charlie');201 dave = privateKey('//Dave');202 });203 }); 204205 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 () => {206 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});207 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address); 208 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 10);209 await transferFromExpectSuccess(collectionId, itemId, bob, alice, charlie, 2, 'Fungible');210 await transferFromExpectSuccess(collectionId, itemId, bob, alice, dave, 8, 'Fungible');211 });212});213214describe('User may clear the approvals to approving for 0 amount:', () => {215 let alice: IKeyringPair;216 let bob: IKeyringPair;217 let charlie: IKeyringPair;218219 before(async () => {220 await usingApi(async () => {221 alice = privateKey('//Alice');222 bob = privateKey('//Bob');223 charlie = privateKey('//Charlie');224 });225 });226227 it('NFT', async () => {228 const collectionId = await createCollectionExpectSuccess();229 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT');230 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);231 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 0);232 await transferFromExpectFail(collectionId, itemId, bob, bob, charlie, 1);233 });234235 it('Fungible', async () => {236 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});237 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');238 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 1);239 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 0);240 await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, bob, charlie, 1);241 });242243 it('ReFungible', async () => {244 const reFungibleCollectionId =245 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});246 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');247 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 1);248 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);249 await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, bob, charlie, 1);250 });251});252253describe('User cannot approve for the amount greater than they own:', () => {254 let alice: IKeyringPair;255 let bob: IKeyringPair;256 let charlie: IKeyringPair;257258 before(async () => {259 await usingApi(async () => {260 alice = privateKey('//Alice');261 bob = privateKey('//Bob');262 charlie = privateKey('//Charlie');263 });264 });265266 it('1 for NFT', async () => {267 const collectionId = await createCollectionExpectSuccess();268 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);269 await approveExpectFail(collectionId, itemId, bob, charlie, 2);270 });271272 it('Fungible', async () => {273 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});274 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');275 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, bob, charlie, 11);276 });277278 it('ReFungible', async () => {279 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});280 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');281 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, charlie, 101);282 });283});284285describe('Administrator and collection owner do not need approval in order to execute TransferFrom:', () => {286 let alice: IKeyringPair;287 let bob: IKeyringPair;288 let charlie: IKeyringPair;289 let dave: IKeyringPair;290291 before(async () => {292 await usingApi(async () => {293 alice = privateKey('//Alice');294 bob = privateKey('//Bob');295 charlie = privateKey('//Charlie');296 dave = privateKey('//Dave');297 });298 }); 299300 it('NFT', async () => {301 const collectionId = await createCollectionExpectSuccess();302 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);303 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');304 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);305 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'NFT');306 });307308 it('Fungible up to an approved amount', async () => {309 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});310 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address); 311 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');312 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);313 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'Fungible');314 });315316 it('ReFungible up to an approved amount', async () => {317 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });318 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);319 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');320 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);321 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'ReFungible');322 });323});324325describe('Repeated approvals add up', () => {326 let alice: IKeyringPair;327 let bob: IKeyringPair;328 let charlie: IKeyringPair;329 let dave: IKeyringPair;330331 before(async () => {332 await usingApi(async () => {333 alice = privateKey('//Alice');334 bob = privateKey('//Bob');335 charlie = privateKey('//Charlie');336 dave = privateKey('//Dave');337 });338 }); 339340 it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async () => {341 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});342 await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address);343 await approveExpectSuccess(collectionId, 0, alice, bob.address, 1);344 await approveExpectSuccess(collectionId, 0, alice, charlie.address, 1);345 346 347 348 });349350 it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async () => {351 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });352 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);353 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);354 await approveExpectSuccess(collectionId, itemId, alice, charlie.address, 1);355 356 357 358 });359360 361 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 () => {362 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});363 await createItemExpectSuccess(alice, collectionId, 'Fungible', dave.address);364 await approveExpectSuccess(collectionId, 0, dave, bob.address, 5);365 await approveExpectFail(collectionId, 0, dave, charlie, 6);366 });367368 369 it.skip('Cannot approve for more than total users amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async () => {370 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });371 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', dave.address);372 await approveExpectSuccess(collectionId, itemId, dave, bob.address, 50);373 await approveExpectFail(collectionId, itemId, dave, charlie, 51);374 });375});376377describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {378 let alice: IKeyringPair;379 let bob: IKeyringPair;380 let charlie: IKeyringPair;381382 before(async () => {383 await usingApi(async () => {384 alice = privateKey('//Alice');385 bob = privateKey('//Bob');386 charlie = privateKey('//Charlie');387 });388 });389390 it('can be called by collection admin on non-owned item', async () => {391 const collectionId = await createCollectionExpectSuccess();392 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);393394 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);395 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);396 });397});398399describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {400 let alice: IKeyringPair;401 let bob: IKeyringPair;402 let charlie: IKeyringPair;403404 before(async () => {405 await usingApi(async () => {406 alice = privateKey('//Alice');407 bob = privateKey('//Bob');408 charlie = privateKey('//Charlie');409 });410 });411412 it('Approve for a collection that does not exist', async () => {413 await usingApi(async (api: ApiPromise) => {414 415 const nftCollectionCount = await getCreatedCollectionCount(api);416 await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);417 418 const fungibleCollectionCount = await getCreatedCollectionCount(api);419 await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);420 421 const reFungibleCollectionCount = await getCreatedCollectionCount(api);422 await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);423 });424 });425426 it('Approve for a collection that was destroyed', async () => {427 428 const nftCollectionId = await createCollectionExpectSuccess();429 await destroyCollectionExpectSuccess(nftCollectionId);430 await approveExpectFail(nftCollectionId, 1, alice, bob);431 432 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});433 await destroyCollectionExpectSuccess(fungibleCollectionId);434 await approveExpectFail(fungibleCollectionId, 0, alice, bob);435 436 const reFungibleCollectionId =437 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});438 await destroyCollectionExpectSuccess(reFungibleCollectionId);439 await approveExpectFail(reFungibleCollectionId, 1, alice, bob);440 });441442 it('Approve transfer of a token that does not exist', async () => {443 444 const nftCollectionId = await createCollectionExpectSuccess();445 await approveExpectFail(nftCollectionId, 2, alice, bob);446 447 const reFungibleCollectionId =448 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});449 await approveExpectFail(reFungibleCollectionId, 2, alice, bob);450 });451452 it('Approve using the address that does not own the approved token', async () => {453 const nftCollectionId = await createCollectionExpectSuccess();454 455 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');456 await approveExpectFail(nftCollectionId, newNftTokenId, bob, alice);457 458 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});459 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');460 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice);461 462 const reFungibleCollectionId =463 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});464 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');465 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice);466 });467468 it('should fail if approved more ReFungibles than owned', async () => {469 const nftCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});470 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'ReFungible');471 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 100, 'ReFungible');472 await approveExpectSuccess(nftCollectionId, newNftTokenId, bob, alice.address, 100);473 await approveExpectFail(nftCollectionId, newNftTokenId, bob, alice, 101);474 });475476 it('should fail if approved more Fungibles than owned', async () => {477 const nftCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});478 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'Fungible');479 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 10, 'Fungible');480 await approveExpectSuccess(nftCollectionId, newNftTokenId, bob, alice.address, 10);481 await approveExpectFail(nftCollectionId, newNftTokenId, bob, alice, 11);482 });483484 it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {485 const collectionId = await createCollectionExpectSuccess();486 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);487 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});488489 await approveExpectFail(collectionId, itemId, alice, charlie);490 });491});