1234567891011121314151617import type {IKeyringPair} from '@polkadot/types/types';18import {Pallets} from '@unique/test-utils/util.js';19import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util/index.js';20import {CreateCollectionData} from './util/playgrounds/types.js';2122describe('EVM contract allowlist', () => {23 let donor: IKeyringPair;2425 before(async function() {26 await usingEthPlaygrounds(async (_helper, privateKey) => {27 donor = await privateKey({url: import.meta.url});28 });29 });3031 itEth('Contract allowlist can be toggled', async ({helper}) => {32 const owner = await helper.eth.createAccountWithBalance(donor);33 const flipper = await helper.eth.deployFlipper(owner);34 const helpers = helper.ethNativeContract.contractHelpers(owner);3536 37 expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;3839 40 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});41 expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.true;4243 44 await helpers.methods.toggleAllowlist(flipper.options.address, false).send({from: owner});45 expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;46 });4748 itEth('Non-allowlisted user can\'t call contract with allowlist enabled', async ({helper}) => {49 const owner = await helper.eth.createAccountWithBalance(donor);50 const caller = await helper.eth.createAccountWithBalance(donor);51 const flipper = await helper.eth.deployFlipper(owner);52 const helpers = await helper.ethNativeContract.contractHelpers(owner);5354 55 await flipper.methods.flip().send({from: caller});56 expect(await flipper.methods.getValue().call()).to.be.true;5758 59 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});60 await expect(flipper.methods.flip().send({from: caller})).to.rejected;61 expect(await flipper.methods.getValue().call()).to.be.true;6263 64 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});65 await flipper.methods.flip().send({from: caller});66 expect(await flipper.methods.getValue().call()).to.be.false;67 });68});6970describe('EVM collection allowlist', () => {71 let donor: IKeyringPair;7273 before(async function() {74 await usingEthPlaygrounds(async (_helper, privateKey) => {75 donor = await privateKey({url: import.meta.url});76 });77 });7879 80 itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => {81 const owner = await helper.eth.createAccountWithBalance(donor);82 const user = helper.eth.createAccount();83 const crossUser = helper.ethCrossAccount.fromAddress(user);8485 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');86 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);8788 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;89 await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});90 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true;9192 await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner});93 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;94 });959697 [98 {mode: 'nft' as const, requiredPallets: []},99 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},100 {mode: 'ft' as const, requiredPallets: []},101 ].map(testCase =>102 itEth.ifWithPallets(`Collection allowlist can be added and removed by [cross] address for ${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {103 const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();104 const [userSub] = await helper.arrange.createAccounts([10n], donor);105 const userEth = await helper.eth.createAccountWithBalance(donor);106 const mintParams = testCase.mode === 'ft' ? [userEth, 100] : [userEth];107108 const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();109 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);110 const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);111 const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);112 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);113114 115 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;116 await collectionEvm.methods.addToCollectionAllowListCross(userCrossSub).send({from: owner});117 await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner});118 await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner});119120 121 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;122 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;123 expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true;124 expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true;125126 await collectionEvm.methods.mint(...mintParams).send({from: owner}); 127 await collectionEvm.methods.mint(...mintParams).send({from: owner}); 128 await collectionEvm.methods.setCollectionAccess(SponsoringMode.Allowlisted).send({from: owner});129130 131 await collectionEvm.methods.transfer(owner, 1).send({from: userEth});132 await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth});133134 if(testCase.mode === 'ft') {135 expect(await helper.ft.getBalance(collectionId, {Ethereum: owner})).to.eq(1n);136 expect(await helper.ft.getBalance(collectionId, {Substrate: userSub.address})).to.eq(2n);137 } else {138 expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner});139 expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address});140 }141142 143 testCase.mode === 'ft'144 ? await helper.ft.transfer(userSub, collectionId, {Ethereum: userEth}, 2n)145 : await helper.collection.transferToken(userSub, collectionId, 2, {Ethereum: userEth});146147 148 await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner});149 await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossEth).send({from: owner});150 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;151 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;152 expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.false;153 expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false;154155 156 await collectionEvm.methods.mint(...mintParams).send({from: owner});157 await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/);158 }));159160 [161 162 {mode: 'nft' as const, cross: true, requiredPallets: []},163 {mode: 'rft' as const, cross: true, requiredPallets: [Pallets.ReFungible]},164 {mode: 'ft' as const, cross: true, requiredPallets: []},165 166 {mode: 'nft' as const, cross: false, requiredPallets: []},167 {mode: 'rft' as const, cross: false, requiredPallets: [Pallets.ReFungible]},168 {mode: 'ft' as const, cross: false, requiredPallets: []},169 ].map(testCase =>170 itEth.ifWithPallets(`Non-owner cannot add or remove from collection allowlist ${testCase.cross ? 'cross ' : ''}${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {171 172 const addToAllowList = testCase.cross ? 'addToCollectionAllowListCross' : 'addToCollectionAllowList';173 const removeFromAllowList = testCase.cross ? 'removeFromCollectionAllowListCross' : 'removeFromCollectionAllowList';174175 const owner = await helper.eth.createAccountWithBalance(donor);176 const notOwner = await helper.eth.createAccountWithBalance(donor);177 const userSub = donor;178 const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);179 const userEth = helper.eth.createAccount();180 const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);181182 const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();183 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, !testCase.cross);184185 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;186 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;187188 189 190 await expect(collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');191 192 if(testCase.cross)193 await expect(collectionEvm.methods[addToAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');194195 196 197 await collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).send({from: owner});198 199 if(testCase.cross) {200 await collectionEvm.methods[addToAllowList](userCrossSub).send({from: owner});201 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;202 }203 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;204205 206 207 await expect(collectionEvm.methods[removeFromAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');208 209 if(testCase.cross)210 await expect(collectionEvm.methods[removeFromAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');211 }));212});