git.delta.rocks / unique-network / refs/commits / d1896d4a495f

difftreelog

source

tests/src/eth/reFungible.test.ts34.9 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Pallets, requirePalletsOrSkip} from '../util';18import {expect, itEth, usingEthPlaygrounds} from './util';19import {IKeyringPair} from '@polkadot/types/types';20import { ITokenPropertyPermission } from '../util/playgrounds/types';2122describe('Refungible: Information getting', () => {23  let donor: IKeyringPair;2425  before(async function() {26    await usingEthPlaygrounds(async (helper, privateKey) => {27      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);2829      donor = await privateKey({filename: __filename});30    });31  });3233  itEth('totalSupply', async ({helper}) => {34    const caller = await helper.eth.createAccountWithBalance(donor);35    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'TotalSupply', '6', '6');36    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);3738    await contract.methods.mint(caller).send();3940    const totalSupply = await contract.methods.totalSupply().call();41    expect(totalSupply).to.equal('1');42  });4344  itEth('balanceOf', async ({helper}) => {45    const caller = await helper.eth.createAccountWithBalance(donor);46    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'BalanceOf', '6', '6');47    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);4849    await contract.methods.mint(caller).send();50    await contract.methods.mint(caller).send();51    await contract.methods.mint(caller).send();5253    const balance = await contract.methods.balanceOf(caller).call();54    expect(balance).to.equal('3');55  });5657  itEth('ownerOf', async ({helper}) => {58    const caller = await helper.eth.createAccountWithBalance(donor);59    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf', '6', '6');60    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);6162    const result = await contract.methods.mint(caller).send();63    const tokenId = result.events.Transfer.returnValues.tokenId;6465    const owner = await contract.methods.ownerOf(tokenId).call();66    expect(owner).to.equal(caller);67  });6869  itEth('ownerOf after burn', async ({helper}) => {70    const caller = await helper.eth.createAccountWithBalance(donor);71    const receiver = helper.eth.createAccount();72    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf-AfterBurn', '6', '6');73    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);7475    const result = await contract.methods.mint(caller).send();76    const tokenId = result.events.Transfer.returnValues.tokenId;77    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);7879    await tokenContract.methods.repartition(2).send();80    await tokenContract.methods.transfer(receiver, 1).send();8182    await tokenContract.methods.burnFrom(caller, 1).send();8384    const owner = await contract.methods.ownerOf(tokenId).call();85    expect(owner).to.equal(receiver);86  });8788  itEth('ownerOf for partial ownership', async ({helper}) => {89    const caller = await helper.eth.createAccountWithBalance(donor);90    const receiver = helper.eth.createAccount();91    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');92    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);9394    const result = await contract.methods.mint(caller).send();95    const tokenId = result.events.Transfer.returnValues.tokenId;96    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);9798    await tokenContract.methods.repartition(2).send();99    await tokenContract.methods.transfer(receiver, 1).send();100101    const owner = await contract.methods.ownerOf(tokenId).call();102    expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');103  });104});105106describe('Refungible: Plain calls', () => {107  let donor: IKeyringPair;108  let minter: IKeyringPair;109  let bob: IKeyringPair;110  let charlie: IKeyringPair;111112  before(async function() {113    await usingEthPlaygrounds(async (helper, privateKey) => {114      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);115116      donor = await privateKey({filename: __filename});117      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);118    });119  });120121  itEth('Can perform mint() & crossOwnerOf()', async ({helper}) => {122    const owner = await helper.eth.createAccountWithBalance(donor);123    const receiver = helper.eth.createAccount();124    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Minty', '6', '6', '');125    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);126127    const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();128129    const event = result.events.Transfer;130    expect(event.address).to.equal(collectionAddress);131    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');132    expect(event.returnValues.to).to.equal(receiver);133    const tokenId = event.returnValues.tokenId;134    expect(tokenId).to.be.equal('1');135136    expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);137    expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');138  });139  140  itEth('Can perform mintCross()', async ({helper}) => {141    const caller = await helper.eth.createAccountWithBalance(donor);142    const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);143    const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });144    const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,145      collectionAdmin: true,146      mutable: true}}; });147    148    149    const collection = await helper.rft.mintCollection(minter, {150      tokenPrefix: 'ethp',151      tokenPropertyPermissions: permissions,152    });153    await collection.addAdmin(minter, {Ethereum: caller});154    155    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);156    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller, true);157    let expectedTokenId = await contract.methods.nextTokenId().call();158    let result = await contract.methods.mintCross(receiverCross, []).send();159    let tokenId = result.events.Transfer.returnValues.tokenId;160    expect(tokenId).to.be.equal(expectedTokenId);161162    const event = result.events.Transfer;163    expect(event.address).to.be.equal(collectionAddress);164    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');165    expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));166    expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);167    168    expectedTokenId = await contract.methods.nextTokenId().call();169    result = await contract.methods.mintCross(receiverCross, properties).send();170    tokenId = result.events.Transfer.returnValues.tokenId;171172    expect(tokenId).to.be.equal(expectedTokenId);173    174    expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties175      .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));176  });177178  itEth.skip('Can perform mintBulk()', async ({helper}) => {179    const owner = await helper.eth.createAccountWithBalance(donor);180    const receiver = helper.eth.createAccount();181    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');182    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);183184    {185      const nextTokenId = await contract.methods.nextTokenId().call();186      expect(nextTokenId).to.be.equal('1');187      const result = await contract.methods.mintBulkWithTokenURI(188        receiver,189        [190          [nextTokenId, 'Test URI 0'],191          [+nextTokenId + 1, 'Test URI 1'],192          [+nextTokenId + 2, 'Test URI 2'],193        ],194      ).send();195196      const events = result.events.Transfer;197      for (let i = 0; i < 2; i++) {198        const event = events[i];199        expect(event.address).to.equal(collectionAddress);200        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');201        expect(event.returnValues.to).to.equal(receiver);202        expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));203      }204205      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');206      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');207      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');208    }209  });210211  itEth('Can perform setApprovalForAll()', async ({helper}) => {212    const owner = await helper.eth.createAccountWithBalance(donor);213    const operator = helper.eth.createAccount();214215    const collection = await helper.rft.mintCollection(minter, {});216217    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);218    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);219220    const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();221    expect(approvedBefore).to.be.equal(false);222223    {224      const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});225226      expect(result.events.ApprovalForAll).to.be.like({227        address: collectionAddress,228        event: 'ApprovalForAll',229        returnValues: {230          owner,231          operator,232          approved: true,233        },234      });235236      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();237      expect(approvedAfter).to.be.equal(true);238    }239240    {241      const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});242243      expect(result.events.ApprovalForAll).to.be.like({244        address: collectionAddress,245        event: 'ApprovalForAll',246        returnValues: {247          owner,248          operator,249          approved: false,250        },251      });252253      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();254      expect(approvedAfter).to.be.equal(false);255    }256  });257258  itEth('Can perform burn with ApprovalForAll', async ({helper}) => {259    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});260261    const owner = await helper.eth.createAccountWithBalance(donor);262    const operator = await helper.eth.createAccountWithBalance(donor, 100n);263264    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});265266    const address = helper.ethAddress.fromCollectionId(collection.collectionId);267    const contract = helper.ethNativeContract.collection(address, 'rft');268269    {270      await contract.methods.setApprovalForAll(operator, true).send({from: owner});271      const ownerCross = helper.ethCrossAccount.fromAddress(owner);272      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});273      const events = result.events.Transfer;274275      expect(events).to.be.like({276        address,277        event: 'Transfer',278        returnValues: {279          from: owner,280          to: '0x0000000000000000000000000000000000000000',281          tokenId: token.tokenId.toString(),282        },283      });284    }285  });286287  itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {288    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});289290    const owner = await helper.eth.createAccountWithBalance(donor);291    const operator = await helper.eth.createAccountWithBalance(donor, 100n);292293    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});294295    const address = helper.ethAddress.fromCollectionId(collection.collectionId);296    const contract = helper.ethNativeContract.collection(address, 'rft');297298    const rftToken = helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner);299300    {301      await rftToken.methods.approve(operator, 15n).send({from: owner});302      await contract.methods.setApprovalForAll(operator, true).send({from: owner});303      await rftToken.methods.burnFrom(owner, 10n).send({from: operator});304      const allowance = await rftToken.methods.allowance(owner, operator).call();305      expect(allowance).to.be.equal('5');306    }307  });308  309  itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {310    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});311312    const owner = await helper.eth.createAccountWithBalance(donor);313    const operator = await helper.eth.createAccountWithBalance(donor);314    const receiver = charlie;315316    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});317318    const address = helper.ethAddress.fromCollectionId(collection.collectionId);319    const contract = helper.ethNativeContract.collection(address, 'rft');320321    {322      await contract.methods.setApprovalForAll(operator, true).send({from: owner});323      const ownerCross = helper.ethCrossAccount.fromAddress(owner);324      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);325      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});326      const event = result.events.Transfer;327      expect(event).to.be.like({328        address: helper.ethAddress.fromCollectionId(collection.collectionId),329        event: 'Transfer',330        returnValues: {331          from: owner,332          to: helper.address.substrateToEth(receiver.address),333          tokenId: token.tokenId.toString(),334        },335      });336    }337338    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);339  });340341  itEth('Can perform burn()', async ({helper}) => {342    const caller = await helper.eth.createAccountWithBalance(donor);343    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');344    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);345346    const result = await contract.methods.mint(caller).send();347    const tokenId = result.events.Transfer.returnValues.tokenId;348    {349      const result = await contract.methods.burn(tokenId).send();350      const event = result.events.Transfer;351      expect(event.address).to.equal(collectionAddress);352      expect(event.returnValues.from).to.equal(caller);353      expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');354      expect(event.returnValues.tokenId).to.equal(tokenId.toString());355    }356  });357358  itEth('Can perform transferFrom()', async ({helper}) => {359    const caller = await helper.eth.createAccountWithBalance(donor);360    const receiver = helper.eth.createAccount();361    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');362    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);363364    const result = await contract.methods.mint(caller).send();365    const tokenId = result.events.Transfer.returnValues.tokenId;366367    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);368369    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);370    await tokenContract.methods.repartition(15).send();371372    {373      const tokenEvents: any = [];374      tokenContract.events.allEvents((_: any, event: any) => {375        tokenEvents.push(event);376      });377      const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();378      if (tokenEvents.length == 0) await helper.wait.newBlocks(1);379380      let event = result.events.Transfer;381      expect(event.address).to.equal(collectionAddress);382      expect(event.returnValues.from).to.equal(caller);383      expect(event.returnValues.to).to.equal(receiver);384      expect(event.returnValues.tokenId).to.equal(tokenId.toString());385386      event = tokenEvents[0];387      expect(event.address).to.equal(tokenAddress);388      expect(event.returnValues.from).to.equal(caller);389      expect(event.returnValues.to).to.equal(receiver);390      expect(event.returnValues.value).to.equal('15');391    }392393    {394      const balance = await contract.methods.balanceOf(receiver).call();395      expect(+balance).to.equal(1);396    }397398    {399      const balance = await contract.methods.balanceOf(caller).call();400      expect(+balance).to.equal(0);401    }402  });403404  // Soft-deprecated405  itEth('Can perform burnFrom()', async ({helper}) => {406    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});407408    const owner = await helper.eth.createAccountWithBalance(donor, 100n);409    const spender = await helper.eth.createAccountWithBalance(donor, 100n);410411    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});412413    const address = helper.ethAddress.fromCollectionId(collection.collectionId);414    const contract = helper.ethNativeContract.collection(address, 'rft', spender, true);415416    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);417    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);418    await tokenContract.methods.repartition(15).send();419    await tokenContract.methods.approve(spender, 15).send();420421    {422      const result = await contract.methods.burnFrom(owner, token.tokenId).send();423      const event = result.events.Transfer;424      expect(event).to.be.like({425        address: helper.ethAddress.fromCollectionId(collection.collectionId),426        event: 'Transfer',427        returnValues: {428          from: owner,429          to: '0x0000000000000000000000000000000000000000',430          tokenId: token.tokenId.toString(),431        },432      });433    }434435    expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);436  });437438  itEth('Can perform burnFromCross()', async ({helper}) => {439    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});440    441    const owner = bob;442    const spender = await helper.eth.createAccountWithBalance(donor, 100n);443444    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});445446    const address = helper.ethAddress.fromCollectionId(collection.collectionId);447    const contract = helper.ethNativeContract.collection(address, 'rft');448449    await token.repartition(owner, 15n);450    await token.approve(owner, {Ethereum: spender}, 15n);451452    {453      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);454      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});455      const event = result.events.Transfer;456      expect(event).to.be.like({457        address: helper.ethAddress.fromCollectionId(collection.collectionId),458        event: 'Transfer',459        returnValues: {460          from: helper.address.substrateToEth(owner.address),461          to: '0x0000000000000000000000000000000000000000',462          tokenId: token.tokenId.toString(),463        },464      });465    }466467    expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);468  });469470  itEth('Can perform transferFromCross()', async ({helper}) => {471    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});472473    const owner = bob;474    const spender = await helper.eth.createAccountWithBalance(donor, 100n);475    const receiver = charlie;476477    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});478479    const address = helper.ethAddress.fromCollectionId(collection.collectionId);480    const contract = helper.ethNativeContract.collection(address, 'rft');481482    await token.repartition(owner, 15n);483    await token.approve(owner, {Ethereum: spender}, 15n);484485    {486      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);487      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);488      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});489      const event = result.events.Transfer;490      expect(event).to.be.like({491        address: helper.ethAddress.fromCollectionId(collection.collectionId),492        event: 'Transfer',493        returnValues: {494          from: helper.address.substrateToEth(owner.address),495          to: helper.address.substrateToEth(receiver.address),496          tokenId: token.tokenId.toString(),497        },498      });499    }500501    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);502  });503504  itEth('Can perform transfer()', async ({helper}) => {505    const caller = await helper.eth.createAccountWithBalance(donor);506    const receiver = helper.eth.createAccount();507    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');508    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);509510    const result = await contract.methods.mint(caller).send();511    const tokenId = result.events.Transfer.returnValues.tokenId;512513    {514      const result = await contract.methods.transfer(receiver, tokenId).send();515516      const event = result.events.Transfer;517      expect(event.address).to.equal(collectionAddress);518      expect(event.returnValues.from).to.equal(caller);519      expect(event.returnValues.to).to.equal(receiver);520      expect(event.returnValues.tokenId).to.equal(tokenId.toString());521    }522523    {524      const balance = await contract.methods.balanceOf(caller).call();525      expect(+balance).to.equal(0);526    }527528    {529      const balance = await contract.methods.balanceOf(receiver).call();530      expect(+balance).to.equal(1);531    }532  });533  534  itEth('Can perform transferCross()', async ({helper}) => {535    const sender = await helper.eth.createAccountWithBalance(donor);536    const receiverEth = await helper.eth.createAccountWithBalance(donor);537    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);538    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);539540    const collection = await helper.rft.mintCollection(minter, {});541    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);542    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender);543544    const token = await collection.mintToken(minter, 50n, {Ethereum: sender});545546    {547      // Can transferCross to ethereum address:548      const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender});549      // Check events:550      const event = result.events.Transfer;551      expect(event.address).to.equal(collectionAddress);552      expect(event.returnValues.from).to.equal(sender);553      expect(event.returnValues.to).to.equal(receiverEth);554      expect(event.returnValues.tokenId).to.equal(token.tokenId.toString());555      // Sender's balance decreased:556      const senderBalance = await collectionEvm.methods.balanceOf(sender).call();557      expect(+senderBalance).to.equal(0);558      expect(await token.getBalance({Ethereum: sender})).to.eq(0n);559      // Receiver's balance increased:560      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();561      expect(+receiverBalance).to.equal(1);562      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n);563    }564    565    {566      // Can transferCross to substrate address:567      const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth});568      // Check events:569      const event = substrateResult.events.Transfer;570      expect(event.address).to.be.equal(collectionAddress);571      expect(event.returnValues.from).to.be.equal(receiverEth);572      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));573      expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);574      // Sender's balance decreased:575      const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call();576      expect(+senderBalance).to.equal(0);577      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n);578      // Receiver's balance increased:579      const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});580      expect(receiverBalance).to.contain(token.tokenId);581      expect(await token.getBalance({Substrate: minter.address})).to.eq(50n);582    }583  });584585  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {586    const sender = await helper.eth.createAccountWithBalance(donor);587    const tokenOwner = await helper.eth.createAccountWithBalance(donor);588    const receiverSub = minter;589    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);590591    const collection = await helper.rft.mintCollection(minter, {});592    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);593    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender);594595    await collection.mintToken(minter, 50n, {Ethereum: sender});596    const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner});597598    // Cannot transferCross someone else's token:599    const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;600    await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;601    // Cannot transfer token if it does not exist:602    await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;603  }));604605  itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {606    const caller = await helper.eth.createAccountWithBalance(donor);607    const receiver = helper.eth.createAccount();608    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');609    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);610611    const result = await contract.methods.mint(caller).send();612    const tokenId = result.events.Transfer.returnValues.tokenId;613614    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);615616    await tokenContract.methods.repartition(2).send();617    await tokenContract.methods.transfer(receiver, 1).send();618619    const events: any = [];620    contract.events.allEvents((_: any, event: any) => {621      events.push(event);622    });623624    await tokenContract.methods.transfer(receiver, 1).send();625    if (events.length == 0) await helper.wait.newBlocks(1);626    const event = events[0];627628    expect(event.address).to.equal(collectionAddress);629    expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');630    expect(event.returnValues.to).to.equal(receiver);631    expect(event.returnValues.tokenId).to.equal(tokenId.toString());632  });633634  itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {635    const caller = await helper.eth.createAccountWithBalance(donor);636    const receiver = helper.eth.createAccount();637    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');638    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);639640    const result = await contract.methods.mint(caller).send();641    const tokenId = result.events.Transfer.returnValues.tokenId;642643    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);644645    await tokenContract.methods.repartition(2).send();646647    const events: any = [];648    contract.events.allEvents((_: any, event: any) => {649      events.push(event);650    });651652    await tokenContract.methods.transfer(receiver, 1).send();653    if (events.length == 0) await helper.wait.newBlocks(1);654    const event = events[0];655656    expect(event.address).to.equal(collectionAddress);657    expect(event.returnValues.from).to.equal(caller);658    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');659    expect(event.returnValues.tokenId).to.equal(tokenId.toString());660  });661});662663describe('RFT: Fees', () => {664  let donor: IKeyringPair;665666  before(async function() {667    await usingEthPlaygrounds(async (helper, privateKey) => {668      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);669670      donor = await privateKey({filename: __filename});671    });672  });673674  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {675    const caller = await helper.eth.createAccountWithBalance(donor);676    const receiver = helper.eth.createAccount();677    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');678    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);679680    const result = await contract.methods.mint(caller).send();681    const tokenId = result.events.Transfer.returnValues.tokenId;682683    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());684    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));685    expect(cost > 0n);686  });687688  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {689    const caller = await helper.eth.createAccountWithBalance(donor);690    const receiver = helper.eth.createAccount();691    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');692    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);693694    const result = await contract.methods.mint(caller).send();695    const tokenId = result.events.Transfer.returnValues.tokenId;696697    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());698    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));699    expect(cost > 0n);700  });701});702703describe('Common metadata', () => {704  let donor: IKeyringPair;705  let alice: IKeyringPair;706707  before(async function() {708    await usingEthPlaygrounds(async (helper, privateKey) => {709      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);710711      donor = await privateKey({filename: __filename});712      [alice] = await helper.arrange.createAccounts([20n], donor);713    });714  });715716  itEth('Returns collection name', async ({helper}) => {717    const caller = helper.eth.createAccount();718    const tokenPropertyPermissions = [{719      key: 'URI',720      permission: {721        mutable: true,722        collectionAdmin: true,723        tokenOwner: false,724      },725    }];726    const collection = await helper.rft.mintCollection(727      alice,728      {729        name: 'Leviathan',730        tokenPrefix: '11',731        properties: [{key: 'ERC721Metadata', value: '1'}],732        tokenPropertyPermissions,733      },734    );735736    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);737    const name = await contract.methods.name().call();738    expect(name).to.equal('Leviathan');739  });740741  itEth('Returns symbol name', async ({helper}) => {742    const caller = await helper.eth.createAccountWithBalance(donor);743    const tokenPropertyPermissions = [{744      key: 'URI',745      permission: {746        mutable: true,747        collectionAdmin: true,748        tokenOwner: false,749      },750    }];751    const {collectionId} = await helper.rft.mintCollection(752      alice,753      {754        name: 'Leviathan',755        tokenPrefix: '12',756        properties: [{key: 'ERC721Metadata', value: '1'}],757        tokenPropertyPermissions,758      },759    );760761    const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);762    const symbol = await contract.methods.symbol().call();763    expect(symbol).to.equal('12');764  });765});766767describe('Negative tests', () => {768  let donor: IKeyringPair;769  let minter: IKeyringPair;770  let alice: IKeyringPair;771772  before(async function() {773    await usingEthPlaygrounds(async (helper, privateKey) => {774      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);775      776      donor = await privateKey({filename: __filename});777      [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);778    });779  });780781  itEth('[negative] Cant perform burn without approval', async ({helper}) => {782    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});783784    const owner = await helper.eth.createAccountWithBalance(donor, 100n);785    const spender = await helper.eth.createAccountWithBalance(donor, 100n);786787    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});788789    const address = helper.ethAddress.fromCollectionId(collection.collectionId);790    const contract = helper.ethNativeContract.collection(address, 'rft');791792    const ownerCross = helper.ethCrossAccount.fromAddress(owner);793794    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;795796    await contract.methods.setApprovalForAll(spender, true).send({from: owner});797    await contract.methods.setApprovalForAll(spender, false).send({from: owner});798799    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;800  });801802  itEth('[negative] Cant perform transfer without approval', async ({helper}) => {803    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});804    const owner = await helper.eth.createAccountWithBalance(donor, 100n);805    const receiver = alice;806807    const spender = await helper.eth.createAccountWithBalance(donor, 100n);808809    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});810811    const address = helper.ethAddress.fromCollectionId(collection.collectionId);812    const contract = helper.ethNativeContract.collection(address, 'rft');813814    const ownerCross = helper.ethCrossAccount.fromAddress(owner);815    const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);816    817    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;818819    await contract.methods.setApprovalForAll(spender, true).send({from: owner});820    await contract.methods.setApprovalForAll(spender, false).send({from: owner});821    822    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;823  });824});