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

difftreelog

source

tests/src/eth/collectionAdmin.test.ts23.9 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.3// Unique Network is free software: you can redistribute it and/or modify4// it under the terms of the GNU General Public License as published by5// the Free Software Foundation, either version 3 of the License, or6// (at your option) any later version.7//8// Unique Network is distributed in the hope that it will be useful,9// but WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the11// GNU General Public License for more details.1213// You should have received a copy of the GNU General Public License14// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1516import {IKeyringPair} from '@polkadot/types/types';17import {expect} from 'chai';18import {Pallets} from '../util';19import {IEthCrossAccountId} from '../util/playgrounds/types';20import {usingEthPlaygrounds, itEth} from './util';21import {EthUniqueHelper} from './util/playgrounds/unique.dev';2223async function recordEthFee(helper: EthUniqueHelper, userAddress: string, call: () => Promise<any>) {24  const before = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress));25  await call();26  await helper.wait.newBlocks(1);27  const after = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress));2829  expect(after < before).to.be.true;3031  return before - after;32}3334describe('Add collection admins', () => {35  let donor: IKeyringPair;3637  before(async function() {38    await usingEthPlaygrounds(async (_helper, privateKey) => {39      donor = await privateKey({url: import.meta.url});40    });41  });4243  [44    {mode: 'nft' as const, requiredPallets: []},45    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},46    {mode: 'ft' as const, requiredPallets: []},47  ].map(testCase => {48    itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {49      // arrange50      const owner = await helper.eth.createAccountWithBalance(donor);51      const adminSub = await privateKey('//admin2');52      const adminEth = helper.eth.createAccount().toLowerCase();5354      const adminDeprecated = helper.eth.createAccount().toLowerCase();55      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);56      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);5758      const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');59      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);6061      // Check isOwnerOrAdminCross returns false:62      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false;63      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false;64      expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false;6566      // Soft-deprecated: can addCollectionAdmin67      await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();68      // Can addCollectionAdminCross for substrate and ethereum address69      await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();70      await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();7172      // 1. Expect api.rpc.unique.adminlist returns admins:73      const adminListRpc = await helper.collection.getAdmins(collectionId);74      expect(adminListRpc).to.has.length(3);75      expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]);7677      // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist78      let adminListEth = await collectionEvm.methods.collectionAdmins().call();79      adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));80      expect(adminListRpc).to.be.like(adminListEth);8182      // 3. check isOwnerOrAdminCross returns true:83      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;84      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;85      expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true;86    });87  });8889  itEth('cross account admin can mint', async ({helper}) => {90    // arrange: create collection and accounts91    const owner = await helper.eth.createAccountWithBalance(donor);92    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', 'uri');93    const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();94    const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);95    const [adminSub] = await helper.arrange.createAccounts([100n], donor);96    const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);97    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);9899    // cannot mint while not admin100    await expect(collectionEvm.methods.mint(owner).send({from: adminEth})).to.be.rejected;101    await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);102103    // admin (sub and eth) can mint token:104    await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();105    await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();106    await collectionEvm.methods.mint(owner).send({from: adminEth});107    await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});108109    expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);110  });111112  itEth('cannot add invalid cross account admin', async ({helper}) => {113    const owner = await helper.eth.createAccountWithBalance(donor);114    const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);115116    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');117    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);118119    const adminCross = {120      eth: helper.address.substrateToEth(admin.address),121      sub: admin.addressRaw,122    };123    await expect(collectionEvm.methods.addCollectionAdminCross(adminCross).send()).to.be.rejected;124  });125126  itEth('can verify owner with methods.isOwnerOrAdmin[Cross]', async ({helper, privateKey}) => {127    const owner = await helper.eth.createAccountWithBalance(donor);128    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');129130    const adminDeprecated = helper.eth.createAccount();131    const admin1Cross = helper.ethCrossAccount.fromKeyringPair(await privateKey('admin'));132    const admin2Cross = helper.ethCrossAccount.fromAddress(helper.address.substrateToEth((await privateKey('admin3')).address));133    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);134135    // Soft-deprecated:136    expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.false;137    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.false;138    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.false;139140    await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();141    await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send();142    await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send();143144    // Soft-deprecated: isOwnerOrAdmin returns true145    expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.true;146    // Expect isOwnerOrAdminCross return true147    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.true;148    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.true;149  });150151  // Soft-deprecated152  itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {153    const owner = await helper.eth.createAccountWithBalance(donor);154    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');155156    const admin = await helper.eth.createAccountWithBalance(donor);157    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);158    await collectionEvm.methods.addCollectionAdmin(admin).send();159160    const user = helper.eth.createAccount();161    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin}))162      .to.be.rejectedWith('NoPermission');163164    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);165    expect(adminList.length).to.be.eq(1);166    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())167      .to.be.eq(admin.toLocaleLowerCase());168  });169170  // Soft-deprecated171  itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => {172    const owner = await helper.eth.createAccountWithBalance(donor);173    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');174175    const notAdmin = await helper.eth.createAccountWithBalance(donor);176    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);177178    const user = helper.eth.createAccount();179    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin}))180      .to.be.rejectedWith('NoPermission');181182    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);183    expect(adminList.length).to.be.eq(0);184  });185186  itEth('(!negative tests!) Add [cross] admin by ADMIN is not allowed', async ({helper}) => {187    const owner = await helper.eth.createAccountWithBalance(donor);188    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');189190    const [admin, notAdmin] = await helper.arrange.createAccounts([10n, 10n], donor);191    const adminCross = helper.ethCrossAccount.fromKeyringPair(admin);192    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);193    await collectionEvm.methods.addCollectionAdminCross(adminCross).send();194195    const notAdminCross = helper.ethCrossAccount.fromKeyringPair(notAdmin);196    await expect(collectionEvm.methods.addCollectionAdminCross(notAdminCross).call({from: adminCross.eth}))197      .to.be.rejectedWith('NoPermission');198199    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);200    expect(adminList.length).to.be.eq(1);201202    const admin0Cross = helper.ethCrossAccount.fromKeyringPair(adminList[0]);203    expect(admin0Cross.eth.toLocaleLowerCase())204      .to.be.eq(adminCross.eth.toLocaleLowerCase());205  });206207  itEth('(!negative tests!) Add [cross] admin by USER is not allowed', async ({helper}) => {208    const owner = await helper.eth.createAccountWithBalance(donor);209    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');210211    const notAdmin0 = await helper.eth.createAccountWithBalance(donor);212    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);213    const [notAdmin1] = await helper.arrange.createAccounts([10n], donor);214    const notAdmin1Cross = helper.ethCrossAccount.fromKeyringPair(notAdmin1);215    await expect(collectionEvm.methods.addCollectionAdminCross(notAdmin1Cross).call({from: notAdmin0}))216      .to.be.rejectedWith('NoPermission');217218    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);219    expect(adminList.length).to.be.eq(0);220  });221});222223describe('Remove collection admins', () => {224  let donor: IKeyringPair;225226  before(async function() {227    await usingEthPlaygrounds(async (_helper, privateKey) => {228      donor = await privateKey({url: import.meta.url});229    });230  });231232  // Soft-deprecated233  itEth('Remove admin by owner', async ({helper}) => {234    const owner = await helper.eth.createAccountWithBalance(donor);235    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');236237    const newAdmin = helper.eth.createAccount();238    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);239    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();240241    {242      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);243      expect(adminList.length).to.be.eq(1);244      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())245        .to.be.eq(newAdmin.toLocaleLowerCase());246    }247248    await collectionEvm.methods.removeCollectionAdmin(newAdmin).send();249    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);250    expect(adminList.length).to.be.eq(0);251  });252253  itEth('Remove [cross] admin by owner', async ({helper}) => {254    const owner = await helper.eth.createAccountWithBalance(donor);255    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');256257    const [adminSub] = await helper.arrange.createAccounts([10n], donor);258    const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();259    const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);260    const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);261262    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);263    await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();264    await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();265266    {267      const adminList = await helper.collection.getAdmins(collectionId);268      expect(adminList).to.deep.include({Substrate: adminSub.address});269      expect(adminList).to.deep.include({Ethereum: adminEth});270    }271272    await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();273    await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();274    const adminList = await helper.collection.getAdmins(collectionId);275    expect(adminList.length).to.be.eq(0);276277    // Non admin cannot mint:278    await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);279    await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;280  });281282  // Soft-deprecated283  itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => {284    const owner = await helper.eth.createAccountWithBalance(donor);285    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');286287    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);288289    const admin0 = await helper.eth.createAccountWithBalance(donor);290    await collectionEvm.methods.addCollectionAdmin(admin0).send();291    const admin1 = await helper.eth.createAccountWithBalance(donor);292    await collectionEvm.methods.addCollectionAdmin(admin1).send();293294    await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0}))295      .to.be.rejectedWith('NoPermission');296    {297      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);298      expect(adminList.length).to.be.eq(2);299      expect(adminList.toString().toLocaleLowerCase())300        .to.be.deep.contains(admin0.toLocaleLowerCase())301        .to.be.deep.contains(admin1.toLocaleLowerCase());302    }303  });304305  // Soft-deprecated306  itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => {307    const owner = await helper.eth.createAccountWithBalance(donor);308    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');309310    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);311312    const admin = await helper.eth.createAccountWithBalance(donor);313    await collectionEvm.methods.addCollectionAdmin(admin).send();314    const notAdmin = helper.eth.createAccount();315316    await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin}))317      .to.be.rejectedWith('NoPermission');318    {319      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);320      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())321        .to.be.eq(admin.toLocaleLowerCase());322      expect(adminList.length).to.be.eq(1);323    }324  });325326  itEth('(!negative tests!) Remove [cross] admin by ADMIN is not allowed', async ({helper}) => {327    const owner = await helper.eth.createAccountWithBalance(donor);328    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');329330    const [admin1] = await helper.arrange.createAccounts([10n], donor);331    const admin1Cross = helper.ethCrossAccount.fromKeyringPair(admin1);332    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);333    await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send();334335    const [admin2] = await helper.arrange.createAccounts([10n], donor);336    const admin2Cross = helper.ethCrossAccount.fromKeyringPair(admin2);337    await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send();338339    await expect(collectionEvm.methods.removeCollectionAdminCross(admin1Cross).call({from: admin2Cross.eth}))340      .to.be.rejectedWith('NoPermission');341342    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);343    expect(adminList.length).to.be.eq(2);344    expect(adminList.toString().toLocaleLowerCase())345      .to.be.deep.contains(admin1.address.toLocaleLowerCase())346      .to.be.deep.contains(admin2.address.toLocaleLowerCase());347  });348349  itEth('(!negative tests!) Remove [cross] admin by USER is not allowed', async ({helper}) => {350    const owner = await helper.eth.createAccountWithBalance(donor);351    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');352353    const [adminSub] = await helper.arrange.createAccounts([10n], donor);354    const adminSubCross = helper.ethCrossAccount.fromKeyringPair(adminSub);355    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);356    await collectionEvm.methods.addCollectionAdminCross(adminSubCross).send();357    const notAdminEth = await helper.eth.createAccountWithBalance(donor);358359    await expect(collectionEvm.methods.removeCollectionAdminCross(adminSubCross).call({from: notAdminEth}))360      .to.be.rejectedWith('NoPermission');361362    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);363    expect(adminList.length).to.be.eq(1);364    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())365      .to.be.eq(adminSub.address.toLocaleLowerCase());366  });367});368369// Soft-deprecated370describe('Change owner tests', () => {371  let donor: IKeyringPair;372373  before(async function() {374    await usingEthPlaygrounds(async (_helper, privateKey) => {375      donor = await privateKey({url: import.meta.url});376    });377  });378379  itEth('Change owner', async ({helper}) => {380    const owner = await helper.eth.createAccountWithBalance(donor);381    const newOwner = await helper.eth.createAccountWithBalance(donor);382    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');383    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);384385    await collectionEvm.methods.changeCollectionOwner(newOwner).send();386387    expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false;388    expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true;389  });390391  itEth('change owner call fee', async ({helper}) => {392    const owner = await helper.eth.createAccountWithBalance(donor);393    const newOwner = await helper.eth.createAccountWithBalance(donor);394    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');395    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);396    const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.changeCollectionOwner(newOwner).send());397    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));398    expect(cost > 0);399  });400401  itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => {402    const owner = await helper.eth.createAccountWithBalance(donor);403    const newOwner = await helper.eth.createAccountWithBalance(donor);404    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');405    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);406407    await expect(collectionEvm.methods.changeCollectionOwner(newOwner).send({from: newOwner})).to.be.rejected;408    expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false;409  });410});411412describe('Change substrate owner tests', () => {413  let donor: IKeyringPair;414415  before(async function() {416    await usingEthPlaygrounds(async (_helper, privateKey) => {417      donor = await privateKey({url: import.meta.url});418    });419  });420421  itEth('Change owner [cross]', async ({helper}) => {422    const owner = await helper.eth.createAccountWithBalance(donor);423    const ownerEth = await helper.eth.createAccountWithBalance(donor);424    const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);425    const [ownerSub] = await helper.arrange.createAccounts([10n], donor);426    const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);427428    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');429    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);430431    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.false;432433    // Can set ethereum owner:434    await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossEth).send({from: owner});435    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossEth).call()).to.be.true;436    expect(await helper.collection.getData(collectionId))437      .to.have.property('normalizedOwner').that.is.eq(helper.address.ethToSubstrate(ownerEth));438439    // Can set Substrate owner:440    await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossSub).send({from: ownerEth});441    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.true;442    expect(await helper.collection.getData(collectionId))443      .to.have.property('normalizedOwner').that.is.eq(helper.address.normalizeSubstrate(ownerSub.address));444  });445446  itEth.skip('change owner call fee', async ({helper}) => {447    const owner = await helper.eth.createAccountWithBalance(donor);448    const [newOwner] = await helper.arrange.createAccounts([10n], donor);449    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');450    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);451452    const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send());453    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));454    expect(cost > 0);455  });456457  itEth('(!negative tests!) call setOwner by non owner [cross]', async ({helper}) => {458    const owner = await helper.eth.createAccountWithBalance(donor);459    const otherReceiver = await helper.eth.createAccountWithBalance(donor);460    const [newOwner] = await helper.arrange.createAccounts([10n], donor);461    const newOwnerCross = helper.ethCrossAccount.fromKeyringPair(newOwner);462    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');463    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);464465    await expect(collectionEvm.methods.changeCollectionOwnerCross(newOwnerCross).send({from: otherReceiver})).to.be.rejected;466    expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.false;467  });468});