1234567891011121314151617import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {Contract} from 'web3-eth-contract';20import exp from 'constants';21import {ITokenPropertyPermission} from '../util/playgrounds/types';222324describe('NFT: Information getting', () => {25 let donor: IKeyringPair;26 let alice: IKeyringPair;2728 before(async function() {29 await usingEthPlaygrounds(async (helper, privateKey) => {30 donor = await privateKey({filename: __filename});31 [alice] = await helper.arrange.createAccounts([10n], donor);32 });33 });3435 itEth('totalSupply', async ({helper}) => {36 const collection = await helper.nft.mintCollection(alice, {});37 await collection.mintToken(alice);3839 const caller = await helper.eth.createAccountWithBalance(donor);4041 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);42 const totalSupply = await contract.methods.totalSupply().call();4344 expect(totalSupply).to.equal('1');45 });4647 itEth('balanceOf', async ({helper}) => {48 const collection = await helper.nft.mintCollection(alice, {});49 const caller = await helper.eth.createAccountWithBalance(donor);5051 await collection.mintToken(alice, {Ethereum: caller});52 await collection.mintToken(alice, {Ethereum: caller});53 await collection.mintToken(alice, {Ethereum: caller});5455 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);56 const balance = await contract.methods.balanceOf(caller).call();5758 expect(balance).to.equal('3');59 });6061 itEth('ownerOf', async ({helper}) => {62 const collection = await helper.nft.mintCollection(alice, {});63 const caller = await helper.eth.createAccountWithBalance(donor);6465 const token = await collection.mintToken(alice, {Ethereum: caller});6667 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);6869 const owner = await contract.methods.ownerOf(token.tokenId).call();7071 expect(owner).to.equal(caller);72 });7374 itEth('name/symbol is available regardless of ERC721Metadata support', async ({helper}) => {75 const collection = await helper.nft.mintCollection(alice, {name: 'test', tokenPrefix: 'TEST'});76 const caller = helper.eth.createAccount();7778 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);7980 expect(await contract.methods.name().call()).to.equal('test');81 expect(await contract.methods.symbol().call()).to.equal('TEST');82 });83});8485describe('Check ERC721 token URI for NFT', () => {86 let donor: IKeyringPair;8788 before(async function() {89 await usingEthPlaygrounds(async (_helper, privateKey) => {90 donor = await privateKey({filename: __filename});91 });92 });9394 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {95 const owner = await helper.eth.createAccountWithBalance(donor);96 const receiver = helper.eth.createAccount();9798 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);99 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);100101 const result = await contract.methods.mint(receiver).send();102 const tokenId = result.events.Transfer.returnValues.tokenId;103 expect(tokenId).to.be.equal('1');104105 if (propertyKey && propertyValue) {106 107 await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();108 }109110 const event = result.events.Transfer;111 expect(event.address).to.be.equal(collectionAddress);112 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');113 expect(event.returnValues.to).to.be.equal(receiver);114 expect(event.returnValues.tokenId).to.be.equal(tokenId);115116 return {contract, nextTokenId: tokenId};117 }118119 itEth('Empty tokenURI', async ({helper}) => {120 const {contract, nextTokenId} = await setup(helper, '');121 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');122 });123124 itEth('TokenURI from url', async ({helper}) => {125 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');126 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');127 });128129 itEth('TokenURI from baseURI', async ({helper}) => {130 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');131 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');132 });133134 itEth('TokenURI from baseURI + suffix', async ({helper}) => {135 const suffix = '/some/suffix';136 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);137 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);138 });139});140141describe('NFT: Plain calls', () => {142 let donor: IKeyringPair;143 let minter: IKeyringPair;144 let bob: IKeyringPair;145 let charlie: IKeyringPair;146147 before(async function() {148 await usingEthPlaygrounds(async (helper, privateKey) => {149 donor = await privateKey({filename: __filename});150 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);151 });152 });153154 itEth('Can perform mint() & get crossOwner()', async ({helper}) => {155 const owner = await helper.eth.createAccountWithBalance(donor);156 const receiver = helper.eth.createAccount();157158 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');159 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);160161 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();162 const tokenId = result.events.Transfer.returnValues.tokenId;163 expect(tokenId).to.be.equal('1');164165 const event = result.events.Transfer;166 expect(event.address).to.be.equal(collectionAddress);167 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');168 expect(event.returnValues.to).to.be.equal(receiver);169170 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');171 console.log(await contract.methods.crossOwnerOf(tokenId).call());172 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);173 174 175 176 177 });178 179 itEth('Can perform mintCross()', async ({helper}) => {180 const caller = await helper.eth.createAccountWithBalance(donor);181 const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);182 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });183 const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,184 collectionAdmin: true,185 mutable: true}}; });186 187 188 const collection = await helper.nft.mintCollection(minter, {189 tokenPrefix: 'ethp',190 tokenPropertyPermissions: permissions,191 });192 await collection.addAdmin(minter, {Ethereum: caller});193 194 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);195 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true);196 let expectedTokenId = await contract.methods.nextTokenId().call();197 let result = await contract.methods.mintCross(receiverCross, []).send();198 let tokenId = result.events.Transfer.returnValues.tokenId;199 expect(tokenId).to.be.equal(expectedTokenId);200201 const event = result.events.Transfer;202 expect(event.address).to.be.equal(collectionAddress);203 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');204 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));205 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);206 207 expectedTokenId = await contract.methods.nextTokenId().call();208 result = await contract.methods.mintCross(receiverCross, properties).send();209 tokenId = result.events.Transfer.returnValues.tokenId;210211 expect(tokenId).to.be.equal(expectedTokenId);212 213 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties214 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));215 });216 217 218 itEth.skip('Can perform mintBulk()', async ({helper}) => {219 const caller = await helper.eth.createAccountWithBalance(donor);220 const receiver = helper.eth.createAccount();221222 const collection = await helper.nft.mintCollection(minter);223 await collection.addAdmin(minter, {Ethereum: caller});224225 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);226 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);227 {228 const bulkSize = 3;229 const nextTokenId = await contract.methods.nextTokenId().call();230 expect(nextTokenId).to.be.equal('1');231 const result = await contract.methods.mintBulkWithTokenURI(232 receiver,233 Array.from({length: bulkSize}, (_, i) => (234 [+nextTokenId + i, `Test URI ${i}`]235 )),236 ).send({from: caller});237238 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);239 for (let i = 0; i < bulkSize; i++) {240 const event = events[i];241 expect(event.address).to.equal(collectionAddress);242 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');243 expect(event.returnValues.to).to.equal(receiver);244 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);245246 expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);247 }248 }249 });250251 itEth('Can perform burn()', async ({helper}) => {252 const caller = await helper.eth.createAccountWithBalance(donor);253254 const collection = await helper.nft.mintCollection(minter, {});255 const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});256257 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);258 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);259260 {261 const result = await contract.methods.burn(tokenId).send({from: caller});262263 const event = result.events.Transfer;264 expect(event.address).to.be.equal(collectionAddress);265 expect(event.returnValues.from).to.be.equal(caller);266 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');267 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);268 }269 });270271 itEth('Can perform approve()', async ({helper}) => {272 const owner = await helper.eth.createAccountWithBalance(donor);273 const spender = helper.eth.createAccount();274275 const collection = await helper.nft.mintCollection(minter, {});276 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});277278 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);279 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);280281 {282 const result = await contract.methods.approve(spender, tokenId).send({from: owner});283284 const event = result.events.Approval;285 expect(event.address).to.be.equal(collectionAddress);286 expect(event.returnValues.owner).to.be.equal(owner);287 expect(event.returnValues.approved).to.be.equal(spender);288 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);289 }290 });291292 itEth('Can perform setApprovalForAll()', async ({helper}) => {293 const owner = await helper.eth.createAccountWithBalance(donor);294 const operator = helper.eth.createAccount();295296 const collection = await helper.nft.mintCollection(minter, {});297298 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);299 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);300301 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();302 expect(approvedBefore).to.be.equal(false);303304 {305 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});306307 expect(result.events.ApprovalForAll).to.be.like({308 address: collectionAddress,309 event: 'ApprovalForAll',310 returnValues: {311 owner,312 operator,313 approved: true,314 },315 });316317 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();318 expect(approvedAfter).to.be.equal(true);319 }320321 {322 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});323324 expect(result.events.ApprovalForAll).to.be.like({325 address: collectionAddress,326 event: 'ApprovalForAll',327 returnValues: {328 owner,329 operator,330 approved: false,331 },332 });333334 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();335 expect(approvedAfter).to.be.equal(false);336 }337 });338339 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {340 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});341342 const owner = await helper.eth.createAccountWithBalance(donor);343 const operator = await helper.eth.createAccountWithBalance(donor, 100n);344345 const token = await collection.mintToken(minter, {Ethereum: owner});346347 const address = helper.ethAddress.fromCollectionId(collection.collectionId);348 const contract = helper.ethNativeContract.collection(address, 'nft');349350 {351 await contract.methods.setApprovalForAll(operator, true).send({from: owner});352 const ownerCross = helper.ethCrossAccount.fromAddress(owner);353 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});354 const events = result.events.Transfer;355356 expect(events).to.be.like({357 address,358 event: 'Transfer',359 returnValues: {360 from: owner,361 to: '0x0000000000000000000000000000000000000000',362 tokenId: token.tokenId.toString(),363 },364 });365 }366 });367 368 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {369 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});370371 const owner = await helper.eth.createAccountWithBalance(donor);372 const operator = await helper.eth.createAccountWithBalance(donor);373 const receiver = charlie;374375 const token = await collection.mintToken(minter, {Ethereum: owner});376377 const address = helper.ethAddress.fromCollectionId(collection.collectionId);378 const contract = helper.ethNativeContract.collection(address, 'nft');379380 {381 await contract.methods.setApprovalForAll(operator, true).send({from: owner});382 const ownerCross = helper.ethCrossAccount.fromAddress(owner);383 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);384 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});385 const event = result.events.Transfer;386 expect(event).to.be.like({387 address: helper.ethAddress.fromCollectionId(collection.collectionId),388 event: 'Transfer',389 returnValues: {390 from: owner,391 to: helper.address.substrateToEth(receiver.address),392 tokenId: token.tokenId.toString(),393 },394 });395 }396397 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});398 });399400 itEth('Can perform burnFromCross()', async ({helper}) => {401 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});402 const ownerSub = bob;403 const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);404 const ownerEth = await helper.eth.createAccountWithBalance(donor, 100n);405 const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);406407 const burnerEth = await helper.eth.createAccountWithBalance(donor, 100n);408 const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);409410 const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});411 const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});412413 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);414 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');415416 417 await token1.approve(ownerSub, {Ethereum: burnerEth});418 await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});419420 421 const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});422 const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});423 const events1 = result1.events.Transfer;424 const events2 = result2.events.Transfer;425426 427 [428 [events1, token1, helper.address.substrateToEth(ownerSub.address)], 429 [events2, token2, ownerEth],430 ].map(burnData => {431 expect(burnData[0]).to.be.like({432 address: collectionAddress,433 event: 'Transfer',434 returnValues: {435 from: burnData[2],436 to: '0x0000000000000000000000000000000000000000',437 tokenId: burnData[1].tokenId.toString(),438 },439 });440 });441442 expect(await token1.doesExist()).to.be.false;443 expect(await token2.doesExist()).to.be.false;444 });445446 itEth('Can perform approveCross()', async ({helper}) => {447 448 const owner = await helper.eth.createAccountWithBalance(donor, 100n);449 const ownerCross = helper.ethCrossAccount.fromAddress(owner);450 const receiverSub = charlie;451 const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);452 const receiverEth = await helper.eth.createAccountWithBalance(donor, 100n);453 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);454455 456 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});457 const token1 = await collection.mintToken(minter, {Ethereum: owner});458 const token2 = await collection.mintToken(minter, {Ethereum: owner});459460 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');461462 463 const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});464 const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});465 const eventSub = resultSub.events.Approval;466 const eventEth = resultEth.events.Approval;467 expect(eventSub).to.be.like({468 address: helper.ethAddress.fromCollectionId(collection.collectionId),469 event: 'Approval',470 returnValues: {471 owner,472 approved: helper.address.substrateToEth(receiverSub.address),473 tokenId: token1.tokenId.toString(),474 },475 });476 expect(eventEth).to.be.like({477 address: helper.ethAddress.fromCollectionId(collection.collectionId),478 event: 'Approval',479 returnValues: {480 owner,481 approved: receiverEth,482 tokenId: token2.tokenId.toString(),483 },484 });485486 487 await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});488 expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});489 490 await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});491 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});492 });493494 itEth('Can reaffirm approved address', async ({helper}) => {495 const owner = await helper.eth.createAccountWithBalance(donor, 100n);496 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);497 const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);498 const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);499 const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);500 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});501 const token1 = await collection.mintToken(minter, {Ethereum: owner});502 const token2 = await collection.mintToken(minter, {Ethereum: owner});503 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');504505 506 await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});507 await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});508509 510 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;511 512 await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});513514 515 await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});516 await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});517518 519 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;520 });521522 itEth('Can perform transferFrom()', async ({helper}) => {523 const owner = await helper.eth.createAccountWithBalance(donor);524 const spender = await helper.eth.createAccountWithBalance(donor);525 const receiver = helper.eth.createAccount();526527 const collection = await helper.nft.mintCollection(minter, {});528 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});529530 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);531 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);532533 await contract.methods.approve(spender, tokenId).send({from: owner});534535 {536 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});537538 const event = result.events.Transfer;539 expect(event.address).to.be.equal(collectionAddress);540 expect(event.returnValues.from).to.be.equal(owner);541 expect(event.returnValues.to).to.be.equal(receiver);542 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);543 }544545 {546 const balance = await contract.methods.balanceOf(receiver).call();547 expect(+balance).to.equal(1);548 }549550 {551 const balance = await contract.methods.balanceOf(owner).call();552 expect(+balance).to.equal(0);553 }554 });555556 itEth('Can perform transferFromCross()', async ({helper}) => {557 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});558559 const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);560 const spender = await helper.eth.createAccountWithBalance(donor);561562 const token = await collection.mintToken(minter, {Substrate: owner.address});563564 const address = helper.ethAddress.fromCollectionId(collection.collectionId);565 const contract = helper.ethNativeContract.collection(address, 'nft');566567 await token.approve(owner, {Ethereum: spender});568569 {570 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);571 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);572 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});573 const event = result.events.Transfer;574 expect(event).to.be.like({575 address: helper.ethAddress.fromCollectionId(collection.collectionId),576 event: 'Transfer',577 returnValues: {578 from: helper.address.substrateToEth(owner.address),579 to: helper.address.substrateToEth(receiver.address),580 tokenId: token.tokenId.toString(),581 },582 });583 }584585 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});586 });587588 itEth('Can perform transfer()', async ({helper}) => {589 const collection = await helper.nft.mintCollection(minter, {});590 const owner = await helper.eth.createAccountWithBalance(donor);591 const receiver = helper.eth.createAccount();592593 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});594595 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);596 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);597598 {599 const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});600601 const event = result.events.Transfer;602 expect(event.address).to.be.equal(collectionAddress);603 expect(event.returnValues.from).to.be.equal(owner);604 expect(event.returnValues.to).to.be.equal(receiver);605 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);606 }607608 {609 const balance = await contract.methods.balanceOf(owner).call();610 expect(+balance).to.equal(0);611 }612613 {614 const balance = await contract.methods.balanceOf(receiver).call();615 expect(+balance).to.equal(1);616 }617 });618 619 itEth('Can perform transferCross()', async ({helper}) => {620 const collection = await helper.nft.mintCollection(minter, {});621 const owner = await helper.eth.createAccountWithBalance(donor);622 const receiverEth = await helper.eth.createAccountWithBalance(donor);623 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);624 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);625 626 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});627628 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);629 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);630631 {632 633 const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});634 635 const event = result.events.Transfer;636 expect(event.address).to.be.equal(collectionAddress);637 expect(event.returnValues.from).to.be.equal(owner);638 expect(event.returnValues.to).to.be.equal(receiverEth);639 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);640 641 642 const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();643 expect(+ownerBalance).to.equal(0);644 645 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();646 expect(+receiverBalance).to.equal(1);647 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});648 }649 650 {651 652 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});653 654 const event = substrateResult.events.Transfer;655 expect(event.address).to.be.equal(collectionAddress);656 expect(event.returnValues.from).to.be.equal(receiverEth);657 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));658 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);659 660 661 const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();662 expect(+ownerBalance).to.equal(0);663 664 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});665 expect(receiverBalance).to.contain(tokenId);666 }667 });668669 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {670 const sender = await helper.eth.createAccountWithBalance(donor);671 const tokenOwner = await helper.eth.createAccountWithBalance(donor);672 const receiverSub = minter;673 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);674675 const collection = await helper.nft.mintCollection(minter, {});676 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);677 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', sender);678679 await collection.mintToken(minter, {Ethereum: sender});680 const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});681682 683 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;684 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;685 686 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;687 }));688});689690describe('NFT: Fees', () => {691 let donor: IKeyringPair;692 let alice: IKeyringPair;693 let bob: IKeyringPair;694 let charlie: IKeyringPair;695696 before(async function() {697 await usingEthPlaygrounds(async (helper, privateKey) => {698 donor = await privateKey({filename: __filename});699 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);700 });701 });702703 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {704 const owner = await helper.eth.createAccountWithBalance(donor);705 const spender = helper.eth.createAccount();706707 const collection = await helper.nft.mintCollection(alice, {});708 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});709710 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);711712 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));713 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));714 });715716 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {717 const owner = await helper.eth.createAccountWithBalance(donor);718 const spender = await helper.eth.createAccountWithBalance(donor);719720 const collection = await helper.nft.mintCollection(alice, {});721 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});722723 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);724725 await contract.methods.approve(spender, tokenId).send({from: owner});726727 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));728 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));729 });730731 itEth('Can perform transferFromCross()', async ({helper}) => {732 const collectionMinter = alice;733 const owner = bob;734 const receiver = charlie;735 const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});736737 const spender = await helper.eth.createAccountWithBalance(donor, 100n);738739 const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});740741 const address = helper.ethAddress.fromCollectionId(collection.collectionId);742 const contract = helper.ethNativeContract.collection(address, 'nft');743744 await token.approve(owner, {Ethereum: spender});745746 {747 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);748 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);749 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});750 const event = result.events.Transfer;751 expect(event).to.be.like({752 address: helper.ethAddress.fromCollectionId(collection.collectionId),753 event: 'Transfer',754 returnValues: {755 from: helper.address.substrateToEth(owner.address),756 to: helper.address.substrateToEth(receiver.address),757 tokenId: token.tokenId.toString(),758 },759 });760 }761762 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});763 });764765 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {766 const owner = await helper.eth.createAccountWithBalance(donor);767 const receiver = helper.eth.createAccount();768769 const collection = await helper.nft.mintCollection(alice, {});770 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});771772 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);773774 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));775 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));776 });777});778779describe('NFT: Substrate calls', () => {780 let donor: IKeyringPair;781 let alice: IKeyringPair;782783 before(async function() {784 await usingEthPlaygrounds(async (helper, privateKey) => {785 donor = await privateKey({filename: __filename});786 [alice] = await helper.arrange.createAccounts([20n], donor);787 });788 });789790 itEth('Events emitted for mint()', async ({helper}) => {791 const collection = await helper.nft.mintCollection(alice, {});792 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);793 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');794795 const events: any = [];796 contract.events.allEvents((_: any, event: any) => {797 events.push(event);798 });799800 const {tokenId} = await collection.mintToken(alice);801 if (events.length == 0) await helper.wait.newBlocks(1);802 const event = events[0];803804 expect(event.event).to.be.equal('Transfer');805 expect(event.address).to.be.equal(collectionAddress);806 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');807 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));808 expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());809 });810811 itEth('Events emitted for burn()', async ({helper}) => {812 const collection = await helper.nft.mintCollection(alice, {});813 const token = await collection.mintToken(alice);814815 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);816 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');817818 const events: any = [];819 contract.events.allEvents((_: any, event: any) => {820 events.push(event);821 });822823 await token.burn(alice);824 if (events.length == 0) await helper.wait.newBlocks(1);825 const event = events[0];826827 expect(event.event).to.be.equal('Transfer');828 expect(event.address).to.be.equal(collectionAddress);829 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));830 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');831 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());832 });833834 itEth('Events emitted for approve()', async ({helper}) => {835 const receiver = helper.eth.createAccount();836837 const collection = await helper.nft.mintCollection(alice, {});838 const token = await collection.mintToken(alice);839840 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);841 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');842843 const events: any = [];844 contract.events.allEvents((_: any, event: any) => {845 events.push(event);846 });847848 await token.approve(alice, {Ethereum: receiver});849 if (events.length == 0) await helper.wait.newBlocks(1);850 const event = events[0];851852 expect(event.event).to.be.equal('Approval');853 expect(event.address).to.be.equal(collectionAddress);854 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));855 expect(event.returnValues.approved).to.be.equal(receiver);856 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());857 });858859 itEth('Events emitted for transferFrom()', async ({helper}) => {860 const [bob] = await helper.arrange.createAccounts([10n], donor);861 const receiver = helper.eth.createAccount();862863 const collection = await helper.nft.mintCollection(alice, {});864 const token = await collection.mintToken(alice);865 await token.approve(alice, {Substrate: bob.address});866867 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);868 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');869870 const events: any = [];871 contract.events.allEvents((_: any, event: any) => {872 events.push(event);873 });874875 await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});876877 if (events.length == 0) await helper.wait.newBlocks(1);878 const event = events[0];879880 expect(event.address).to.be.equal(collectionAddress);881 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));882 expect(event.returnValues.to).to.be.equal(receiver);883 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);884 });885886 itEth('Events emitted for transfer()', async ({helper}) => {887 const receiver = helper.eth.createAccount();888889 const collection = await helper.nft.mintCollection(alice, {});890 const token = await collection.mintToken(alice);891892 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);893 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');894895 const events: any = [];896 contract.events.allEvents((_: any, event: any) => {897 events.push(event);898 });899900 await token.transfer(alice, {Ethereum: receiver});901902 if (events.length == 0) await helper.wait.newBlocks(1);903 const event = events[0];904905 expect(event.address).to.be.equal(collectionAddress);906 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));907 expect(event.returnValues.to).to.be.equal(receiver);908 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);909 });910});911912describe('Common metadata', () => {913 let donor: IKeyringPair;914 let alice: IKeyringPair;915916 before(async function() {917 await usingEthPlaygrounds(async (helper, privateKey) => {918 donor = await privateKey({filename: __filename});919 [alice] = await helper.arrange.createAccounts([20n], donor);920 });921 });922923 itEth('Returns collection name', async ({helper}) => {924 const caller = await helper.eth.createAccountWithBalance(donor);925 const tokenPropertyPermissions = [{926 key: 'URI',927 permission: {928 mutable: true,929 collectionAdmin: true,930 tokenOwner: false,931 },932 }];933 const collection = await helper.nft.mintCollection(934 alice,935 {936 name: 'oh River',937 tokenPrefix: 'CHANGE',938 properties: [{key: 'ERC721Metadata', value: '1'}],939 tokenPropertyPermissions,940 },941 );942943 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);944 const name = await contract.methods.name().call();945 expect(name).to.equal('oh River');946 });947948 itEth('Returns symbol name', async ({helper}) => {949 const caller = await helper.eth.createAccountWithBalance(donor);950 const tokenPropertyPermissions = [{951 key: 'URI',952 permission: {953 mutable: true,954 collectionAdmin: true,955 tokenOwner: false,956 },957 }];958 const collection = await helper.nft.mintCollection(959 alice,960 {961 name: 'oh River',962 tokenPrefix: 'CHANGE',963 properties: [{key: 'ERC721Metadata', value: '1'}],964 tokenPropertyPermissions,965 },966 );967968 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);969 const symbol = await contract.methods.symbol().call();970 expect(symbol).to.equal('CHANGE');971 });972});973974describe('Negative tests', () => {975 let donor: IKeyringPair;976 let minter: IKeyringPair;977 let alice: IKeyringPair;978979 before(async function() {980 await usingEthPlaygrounds(async (helper, privateKey) => {981 donor = await privateKey({filename: __filename});982 [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);983 });984 });985986 itEth('[negative] Cant perform burn without approval', async ({helper}) => {987 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});988989 const owner = await helper.eth.createAccountWithBalance(donor, 100n);990 const spender = await helper.eth.createAccountWithBalance(donor, 100n);991992 const token = await collection.mintToken(minter, {Ethereum: owner});993994 const address = helper.ethAddress.fromCollectionId(collection.collectionId);995 const contract = helper.ethNativeContract.collection(address, 'nft');996997 const ownerCross = helper.ethCrossAccount.fromAddress(owner);998 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;9991000 await contract.methods.setApprovalForAll(spender, true).send({from: owner});1001 await contract.methods.setApprovalForAll(spender, false).send({from: owner});10021003 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;1004 });10051006 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {1007 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});1008 const receiver = alice;10091010 const owner = await helper.eth.createAccountWithBalance(donor, 100n);1011 const spender = await helper.eth.createAccountWithBalance(donor, 100n);10121013 const token = await collection.mintToken(minter, {Ethereum: owner});10141015 const address = helper.ethAddress.fromCollectionId(collection.collectionId);1016 const contract = helper.ethNativeContract.collection(address, 'nft');10171018 const ownerCross = helper.ethCrossAccount.fromAddress(owner);1019 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);10201021 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;10221023 await contract.methods.setApprovalForAll(spender, true).send({from: owner});1024 await contract.methods.setApprovalForAll(spender, false).send({from: owner});1025 1026 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;1027 });1028});