git.delta.rocks / unique-network / refs/commits / 48638492add9

difftreelog

add removeFromAllowList in playgrounds

rkv2022-09-09parent: #fd44216.patch.diff
in: master

2 files changed

modifiedtests/src/allowLists.test.tsdiffbeforeafterboth
before · tests/src/allowLists.test.ts
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 {IKeyringPair} from '@polkadot/types/types';18import chai from 'chai';19import chaiAsPromised from 'chai-as-promised';20import { usingPlaygrounds } from './util/playgrounds';2122chai.use(chaiAsPromised);23const expect = chai.expect;2425let donor: IKeyringPair;2627before(async () => {28  await usingPlaygrounds(async (_, privateKeyWrapper) => {29    donor = privateKeyWrapper('//Alice');30  });31});3233let alice: IKeyringPair;34let bob: IKeyringPair;35let charlie: IKeyringPair;3637describe('Integration Test ext. Allow list tests', () => {3839  before(async () => {40    await usingPlaygrounds(async (helper) => {41      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);42    });43  });4445  it('Owner can add address to allow list', async () => {46    await usingPlaygrounds(async (helper) => {47      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});48      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});49      const allowList = await helper.nft.getAllowList(collectionId);50      expect(allowList).to.be.deep.contains({Substrate: bob.address});51    });52  });5354  it('Admin can add address to allow list', async () => {55    await usingPlaygrounds(async (helper) => {56      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});57      await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});5859      await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});60      const allowList = await helper.nft.getAllowList(collectionId);61      expect(allowList).to.be.deep.contains({Substrate: charlie.address});62    });63  });6465  it('Non-privileged user cannot add address to allow list', async () => {66    await usingPlaygrounds(async (helper) => {67      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});68      const addToAllowListTx = async () => helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});69      expect(addToAllowListTx()).to.be.rejected;70    });71  });7273  it('Nobody can add address to allow list of non-existing collection', async () => {74    const collectionId = (1<<32) - 1;75    await usingPlaygrounds(async (helper) => {76      const addToAllowListTx = async () => helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});77      expect(addToAllowListTx()).to.be.rejected;78    });79  });8081  it('Nobody can add address to allow list of destroyed collection', async () => {82    await usingPlaygrounds(async (helper) => {83      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});84      await helper.collection.burn(alice, collectionId);85      const addToAllowListTx = async () => helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});86      expect(addToAllowListTx()).to.be.rejected;87    });88  });8990  it('If address is already added to allow list, nothing happens', async () => {91    await usingPlaygrounds(async (helper) => {92      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});93      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});94      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});95      const allowList = await helper.nft.getAllowList(collectionId);96      expect(allowList).to.be.deep.contains({Substrate: bob.address});97    });98  });99100  it('Owner can remove address from allow list', async () => {101    await usingPlaygrounds(async (helper) => {102      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});103      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});104105      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.106      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: bob.address}).signAndSend(alice);107108      const allowList = await helper.nft.getAllowList(collectionId);109110      expect(allowList).to.be.not.deep.contains({Substrate: bob.address});111    });112  });113114  it('Admin can remove address from allow list', async () => {115    await usingPlaygrounds(async (helper) => {116      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});117      await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address});118      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});119      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.120      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: bob.address}).signAndSend(charlie);121122      const allowList = await helper.nft.getAllowList(collectionId);123124      expect(allowList).to.be.not.deep.contains({Substrate: bob.address});125    });126  });127128  it('Non-privileged user cannot remove address from allow list', async () => {129    await usingPlaygrounds(async (helper) => {130      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});131      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});132      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.133      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: charlie.address}).signAndSend(bob);134135      const allowList = await helper.nft.getAllowList(collectionId);136137      expect(allowList).to.be.deep.contains({Substrate: charlie.address});138    });139  });140141  it('Nobody can remove address from allow list of non-existing collection', async () => {142    const collectionId = (1<<32) - 1;143    await usingPlaygrounds(async (helper) => {144      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.145      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: charlie.address}).signAndSend(bob);146147      const allowList = await helper.nft.getAllowList(collectionId);148149      expect(allowList).to.be.not.deep.contains({Substrate: charlie.address});150    });151  });152153  it('Nobody can remove address from allow list of deleted collection', async () => {154    await usingPlaygrounds(async (helper) => {155      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});156      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});157      await helper.collection.burn(alice, collectionId);158159      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.160      const removeTx = async () => helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: bob.address}).signAndSend(alice);161162      expect(removeTx()).to.be.rejected;163    });164  });165166  it('If address is already removed from allow list, nothing happens', async () => {167    await usingPlaygrounds(async (helper) => {168      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});169      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});170171      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.172      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: bob.address}).signAndSend(alice);173      const allowListBefore = await helper.nft.getAllowList(collectionId);174      expect(allowListBefore).to.be.not.deep.contains({Substrate: bob.address});175176      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: bob.address}).signAndSend(alice);177178      const allowListAfter = await helper.nft.getAllowList(collectionId);179      expect(allowListAfter).to.be.not.deep.contains({Substrate: bob.address});180    });181  });182183  it('If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async () => {184    await usingPlaygrounds(async (helper) => {185      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});186      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});187      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});188      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});189190      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});191      expect(transferResult()).to.be.rejected;192    });193  });194195  it('If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async () => {196    await usingPlaygrounds(async (helper) => {197      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});198      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});199      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});200      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});201      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});202      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});203204      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.205      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: alice.address}).signAndSend(alice);206207      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});208      expect(transferResult()).to.be.rejected;209    });210  });211212  it('If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async () => {213    await usingPlaygrounds(async (helper) => {214      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});215      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});216      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});217      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});218219      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});220      expect(transferResult()).to.be.rejected;221    });222  });223224  it('If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async () => {225    await usingPlaygrounds(async (helper) => {226      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});227      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});228      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});229      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});230      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});231232      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});233234      //FIXME: removeFromAllowList doesn't not implemented in unique helpers yet. Change to helper later.235      await helper.api?.tx.unique.removeFromAllowList(collectionId, {Substrate: alice.address}).signAndSend(alice);236237      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});238      expect(transferResult()).to.be.rejected;239    });240  });241242  it('If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async () => {243    await usingPlaygrounds(async (helper) => {244      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});245      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});246      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});247      const burnTx = async () => helper.nft.burnToken(bob, collectionId, tokenId);248      expect(burnTx()).to.be.rejected;249    });250  });251252  it('If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method)', async () => {253    await usingPlaygrounds(async (helper) => {254      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});255      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});256      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});257      const approveTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address});258      await expect(approveTx()).to.be.rejected;259    });260  });261262  it('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async () => {263    await usingPlaygrounds(async (helper) => {264      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});265      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});266      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});267      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});268      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});269      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});270      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);271      expect(owner.Substrate).to.be.equal(charlie.address);272    });273  });274275  it('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async () => {276    await usingPlaygrounds(async (helper) => {277      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});278      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});279      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});280      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});281      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});282      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});283284      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});285      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);286      expect(owner.Substrate).to.be.equal(charlie.address);287    });288  });289290  it('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async () => {291    await usingPlaygrounds(async (helper) => {292      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});293      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});294      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});295      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});296      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});297298      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});299      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);300      expect(owner.Substrate).to.be.equal(charlie.address);301    });302  });303304  it('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async () => {305    await usingPlaygrounds(async (helper) => {306      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});307      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});308      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});309      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});310      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});311      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});312313      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});314      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);315      expect(owner.Substrate).to.be.equal(charlie.address);316    });317  });318319  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner', async () => {320    await usingPlaygrounds(async (helper) => {321      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});322      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});323      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});324      const token = await helper.nft.getToken(collectionId, tokenId);325      expect(token).to.be.not.null;326    });327  });328329  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin', async () => {330    await usingPlaygrounds(async (helper) => {331      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});332      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});333      await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});334      const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});335      const token = await helper.nft.getToken(collectionId, tokenId);336      expect(token).to.be.not.null;337    });338  });339340  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow-listed address', async () => {341    await usingPlaygrounds(async (helper) => {342      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});343      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});344      await helper.collection.addToAllowList(alice, collectionId, {Substrate: bob.address});345      const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});346      await expect(mintTokenTx()).to.be.rejected;347    });348  });349350  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address', async () => {351    await usingPlaygrounds(async (helper) => {352      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});353      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});354      const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});355      await expect(mintTokenTx()).to.be.rejected;356    });357  });358359  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner', async () => {360    await usingPlaygrounds(async (helper) => {361      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});362      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});363      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});364      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);365      expect(owner.Substrate).to.be.equal(alice.address);366    });367  });368369  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin', async () => {370    await usingPlaygrounds(async (helper) => {371      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});372      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});373      await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});374      const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});375      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);376      expect(owner.Substrate).to.be.equal(bob.address);377    });378  });379380  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address', async () => {381    await usingPlaygrounds(async (helper) => {382      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});383      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});384      const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});385      await expect(mintTokenTx()).to.be.rejected;386    });387  });388389  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address', async () => {390    await usingPlaygrounds(async (helper) => {391      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});392      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});393      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});394      const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});395      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);396      expect(owner.Substrate).to.be.equal(bob.address);397    });398  });399});
after · tests/src/allowLists.test.ts
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 {IKeyringPair} from '@polkadot/types/types';18import chai from 'chai';19import chaiAsPromised from 'chai-as-promised';20import {usingPlaygrounds} from './util/playgrounds';2122chai.use(chaiAsPromised);23const expect = chai.expect;2425let donor: IKeyringPair;2627before(async () => {28  await usingPlaygrounds(async (_, privateKey) => {29    donor = privateKey('//Alice');30  });31});3233let alice: IKeyringPair;34let bob: IKeyringPair;35let charlie: IKeyringPair;3637describe('Integration Test ext. Allow list tests', () => {3839  before(async () => {40    await usingPlaygrounds(async (helper) => {41      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);42    });43  });4445  it('Owner can add address to allow list', async () => {46    await usingPlaygrounds(async (helper) => {47      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});48      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});49      const allowList = await helper.nft.getAllowList(collectionId);50      expect(allowList).to.be.deep.contains({Substrate: bob.address});51    });52  });5354  it('Admin can add address to allow list', async () => {55    await usingPlaygrounds(async (helper) => {56      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});57      await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});5859      await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});60      const allowList = await helper.nft.getAllowList(collectionId);61      expect(allowList).to.be.deep.contains({Substrate: charlie.address});62    });63  });6465  it('Non-privileged user cannot add address to allow list', async () => {66    await usingPlaygrounds(async (helper) => {67      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});68      const addToAllowListTx = async () => helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});69      await expect(addToAllowListTx()).to.be.rejected;70    });71  });7273  it('Nobody can add address to allow list of non-existing collection', async () => {74    const collectionId = (1<<32) - 1;75    await usingPlaygrounds(async (helper) => {76      const addToAllowListTx = async () => helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});77      await expect(addToAllowListTx()).to.be.rejected;78    });79  });8081  it('Nobody can add address to allow list of destroyed collection', async () => {82    await usingPlaygrounds(async (helper) => {83      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});84      await helper.collection.burn(alice, collectionId);85      const addToAllowListTx = async () => helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});86      await expect(addToAllowListTx()).to.be.rejected;87    });88  });8990  it('If address is already added to allow list, nothing happens', async () => {91    await usingPlaygrounds(async (helper) => {92      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});93      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});94      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});95      const allowList = await helper.nft.getAllowList(collectionId);96      expect(allowList).to.be.deep.contains({Substrate: bob.address});97    });98  });99100  it('Owner can remove address from allow list', async () => {101    await usingPlaygrounds(async (helper) => {102      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});103      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});104105      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});106107      const allowList = await helper.nft.getAllowList(collectionId);108109      expect(allowList).to.be.not.deep.contains({Substrate: bob.address});110    });111  });112113  it('Admin can remove address from allow list', async () => {114    await usingPlaygrounds(async (helper) => {115      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});116      await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address});117      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});118      await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address});119120      const allowList = await helper.nft.getAllowList(collectionId);121122      expect(allowList).to.be.not.deep.contains({Substrate: bob.address});123    });124  });125126  it('Non-privileged user cannot remove address from allow list', async () => {127    await usingPlaygrounds(async (helper) => {128      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});129      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});130      const removeTx = async () => helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address});131      await expect(removeTx()).to.be.rejected;132      const allowList = await helper.nft.getAllowList(collectionId);133134      expect(allowList).to.be.deep.contains({Substrate: charlie.address});135    });136  });137138  it('Nobody can remove address from allow list of non-existing collection', async () => {139    const collectionId = (1<<32) - 1;140    await usingPlaygrounds(async (helper) => {141      const removeTx = async () => helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address});142      await expect(removeTx()).to.be.rejected;143    });144  });145146  it('Nobody can remove address from allow list of deleted collection', async () => {147    await usingPlaygrounds(async (helper) => {148      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});149      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});150      await helper.collection.burn(alice, collectionId);151      const removeTx = async () => helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});152153      await expect(removeTx()).to.be.rejected;154    });155  });156157  it('If address is already removed from allow list, nothing happens', async () => {158    await usingPlaygrounds(async (helper) => {159      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});160      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});161      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});162      const allowListBefore = await helper.nft.getAllowList(collectionId);163      expect(allowListBefore).to.be.not.deep.contains({Substrate: bob.address});164165      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});166167      const allowListAfter = await helper.nft.getAllowList(collectionId);168      expect(allowListAfter).to.be.not.deep.contains({Substrate: bob.address});169    });170  });171172  it('If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async () => {173    await usingPlaygrounds(async (helper) => {174      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});175      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});176      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});177      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});178179      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});180      await expect(transferResult()).to.be.rejected;181    });182  });183184  it('If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async () => {185    await usingPlaygrounds(async (helper) => {186      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});187      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});188      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});189      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});190      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});191      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});192      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address});193194      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});195      await expect(transferResult()).to.be.rejected;196    });197  });198199  it('If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async () => {200    await usingPlaygrounds(async (helper) => {201      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});202      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});203      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});204      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});205206      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});207      await expect(transferResult()).to.be.rejected;208    });209  });210211  it('If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async () => {212    await usingPlaygrounds(async (helper) => {213      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});214      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});215      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});216      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});217      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});218219      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});220      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address});221222      const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});223      await expect(transferResult()).to.be.rejected;224    });225  });226227  it('If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async () => {228    await usingPlaygrounds(async (helper) => {229      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});230      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});231      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});232      const burnTx = async () => helper.nft.burnToken(bob, collectionId, tokenId);233      await expect(burnTx()).to.be.rejected;234    });235  });236237  it('If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method)', async () => {238    await usingPlaygrounds(async (helper) => {239      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});240      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});241      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});242      const approveTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address});243      await expect(approveTx()).to.be.rejected;244    });245  });246247  it('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async () => {248    await usingPlaygrounds(async (helper) => {249      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});250      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});251      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});252      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});253      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});254      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});255      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);256      expect(owner.Substrate).to.be.equal(charlie.address);257    });258  });259260  it('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async () => {261    await usingPlaygrounds(async (helper) => {262      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});263      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});264      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});265      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});266      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});267      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});268269      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});270      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);271      expect(owner.Substrate).to.be.equal(charlie.address);272    });273  });274275  it('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async () => {276    await usingPlaygrounds(async (helper) => {277      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});278      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});279      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});280      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});281      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});282283      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});284      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);285      expect(owner.Substrate).to.be.equal(charlie.address);286    });287  });288289  it('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async () => {290    await usingPlaygrounds(async (helper) => {291      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});292      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});293      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});294      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});295      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});296      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});297298      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});299      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);300      expect(owner.Substrate).to.be.equal(charlie.address);301    });302  });303304  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner', async () => {305    await usingPlaygrounds(async (helper) => {306      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});307      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});308      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});309      const token = await helper.nft.getToken(collectionId, tokenId);310      expect(token).to.be.not.null;311    });312  });313314  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin', async () => {315    await usingPlaygrounds(async (helper) => {316      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});317      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});318      await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});319      const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});320      const token = await helper.nft.getToken(collectionId, tokenId);321      expect(token).to.be.not.null;322    });323  });324325  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow-listed address', async () => {326    await usingPlaygrounds(async (helper) => {327      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});328      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});329      await helper.collection.addToAllowList(alice, collectionId, {Substrate: bob.address});330      const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});331      await expect(mintTokenTx()).to.be.rejected;332    });333  });334335  it('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address', async () => {336    await usingPlaygrounds(async (helper) => {337      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});338      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false});339      const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});340      await expect(mintTokenTx()).to.be.rejected;341    });342  });343344  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner', async () => {345    await usingPlaygrounds(async (helper) => {346      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});347      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});348      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});349      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);350      expect(owner.Substrate).to.be.equal(alice.address);351    });352  });353354  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin', async () => {355    await usingPlaygrounds(async (helper) => {356      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});357      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});358      await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});359      const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});360      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);361      expect(owner.Substrate).to.be.equal(bob.address);362    });363  });364365  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address', async () => {366    await usingPlaygrounds(async (helper) => {367      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});368      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});369      const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});370      await expect(mintTokenTx()).to.be.rejected;371    });372  });373374  it('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address', async () => {375    await usingPlaygrounds(async (helper) => {376      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});377      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true});378      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});379      const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address});380      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);381      expect(owner.Substrate).to.be.equal(bob.address);382    });383  });384});
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -256,9 +256,9 @@
     return network;
   }
 
-  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{ 
-    api: ApiPromise; 
-    network: TUniqueNetworks; 
+  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{
+    api: ApiPromise;
+    network: TUniqueNetworks;
   }> {
     if(typeof network === 'undefined' || network === null) network = 'opal';
     const supportedRPC = {
@@ -466,7 +466,7 @@
 
   /**
    * Get the number of created collections.
-   * 
+   *
    * @returns number of created collections
    */
   async getTotalCount(): Promise<number> {
@@ -475,7 +475,7 @@
 
   /**
    * Get information about the collection with additional data, including the number of tokens it contains, its administrators, the normalized address of the collection's owner, and decoded name and description.
-   * 
+   *
    * @param collectionId ID of collection
    * @example await getData(2)
    * @returns collection information object
@@ -510,7 +510,7 @@
 
   /**
    * Get the normalized addresses of the collection's administrators.
-   * 
+   *
    * @param collectionId ID of collection
    * @example await getAdmins(1)
    * @returns array of administrators
@@ -542,7 +542,7 @@
 
   /**
    * Get the effective limits of the collection instead of null for default values
-   * 
+   *
    * @param collectionId ID of collection
    * @example await getEffectiveLimits(2)
    * @returns object of collection limits
@@ -553,7 +553,7 @@
 
   /**
    * Burns the collection if the signer has sufficient permissions and collection is empty.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param label extra label for log
@@ -573,7 +573,7 @@
 
   /**
    * Sets the sponsor for the collection (Requires the Substrate address).
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param sponsorAddress Sponsor substrate address
@@ -594,7 +594,7 @@
 
   /**
    * Confirms consent to sponsor the collection on behalf of the signer.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param label extra label for log
@@ -614,7 +614,7 @@
 
   /**
    * Sets the limits of the collection. At least one limit must be specified for a correct call.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param limits collection limits object
@@ -643,7 +643,7 @@
 
   /**
    * Changes the owner of the collection to the new Substrate address.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param ownerAddress substrate address of new owner
@@ -663,8 +663,8 @@
   }
 
   /**
-   * Adds a collection administrator. 
-   * 
+   * Adds a collection administrator.
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param adminAddressObj Administrator address (substrate or ethereum)
@@ -684,7 +684,7 @@
   }
 
   /**
-   * Adds an address to allow list 
+   * Adds an address to allow list
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param addressObj address to add to the allow list
@@ -703,8 +703,29 @@
   }
 
   /**
+   * Removes an address from allow list.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param addressObj address to be removed from allow list (substrate or ethereum)
+   * @param label extra label for log
+   * @example removeFromAllowList(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId, label?: string): Promise<boolean> {
+    if(typeof label === 'undefined') label = `collection #${collectionId}`;
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],
+      true, `Unable to remove address from allow list for ${label}`,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved', label);
+  }
+
+  /**
    * Removes a collection administrator.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param adminAddressObj Administrator address (substrate or ethereum)
@@ -725,7 +746,7 @@
 
   /**
    * Sets onchain permissions for selected collection.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param permissions collection permissions object
@@ -746,7 +767,7 @@
 
   /**
    * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param permissions nesting permissions object
@@ -760,7 +781,7 @@
 
   /**
    * Disables nesting for selected collection.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param label extra label for log
@@ -773,7 +794,7 @@
 
   /**
    * Sets onchain properties to the collection.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param properties array of property objects
@@ -794,7 +815,7 @@
 
   /**
    * Deletes onchain properties from the collection.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param propertyKeys array of property keys to delete
@@ -815,7 +836,7 @@
 
   /**
    * Changes the owner of the token.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
@@ -835,9 +856,9 @@
   }
 
   /**
-   * 
-   * Change ownership of a token(s) on behalf of the owner. 
-   * 
+   *
+   * Change ownership of a token(s) on behalf of the owner.
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
@@ -857,13 +878,13 @@
   }
 
   /**
-   * 
+   *
    * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param label 
+   * @param label
    * @param amount amount of tokens to be burned. For NFT must be set to 1n
    * @example burnToken(aliceKeyring, 10, 5);
    * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```
@@ -885,12 +906,12 @@
 
   /**
    * Destroys a concrete instance of NFT on behalf of the owner
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param fromAddressObj address on behalf of which the token will be burnt
    * @param tokenId ID of token
-   * @param label 
+   * @param label
    * @param amount amount of tokens to be burned. For NFT must be set to 1n
    * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})
    * @returns ```true``` if extrinsic success, otherwise ```false```
@@ -908,19 +929,19 @@
 
   /**
    * Set, change, or remove approved address to transfer the ownership of the NFT.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param toAddressObj 
-   * @param label 
+   * @param toAddressObj
+   * @param label
    * @param amount amount of token to be approved. For NFT must be set to 1n
    * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string, amount=1n) {
     if(typeof label === 'undefined') label = `collection #${collectionId}`;
     const approveResult = await this.helper.executeExtrinsic(
-      signer, 
+      signer,
       'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],
       true, `Unable to approve token for ${label}`,
     );
@@ -932,7 +953,7 @@
    * Get the amount of token pieces approved to transfer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param toAccountObj 
+   * @param toAccountObj
    * @param fromAccountObj
    * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})
    * @returns number of approved to transfer pieces
@@ -966,7 +987,7 @@
 class NFTnRFT extends CollectionGroup {
   /**
    * Get tokens owned by account
-   * 
+   *
    * @param collectionId ID of collection
    * @param addressObj tokens owner
    * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})
@@ -980,10 +1001,10 @@
    * Get token data
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param blockHashAt 
+   * @param blockHashAt
    * @param propertyKeys
    * @example getToken(10, 5);
-   * @returns human readable token data 
+   * @returns human readable token data
    */
   async getToken(collectionId: number, tokenId: number, blockHashAt?: string, propertyKeys?: string[]): Promise<{
     properties: IProperty[];
@@ -1017,7 +1038,7 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param permissions permissions to change a property by the collection owner or admin
-   * @param label 
+   * @param label
    * @example setTokenPropertyPermissions(
    *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]
    * )
@@ -1039,8 +1060,8 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param properties 
-   * @param label 
+   * @param properties
+   * @param label
    * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])
    * @returns ```true``` if extrinsic success, otherwise ```false```
    */
@@ -1060,8 +1081,8 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param propertyKeys property keys to be deleted 
-   * @param label 
+   * @param propertyKeys property keys to be deleted
+   * @param label
    * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])
    * @returns ```true``` if extrinsic success, otherwise ```false```
    */
@@ -1079,9 +1100,9 @@
   /**
    * Mint new collection
    * @param signer keyring of signer
-   * @param collectionOptions basic collection options and properties 
+   * @param collectionOptions basic collection options and properties
    * @param mode NFT or RFT type of a collection
-   * @param errorLabel 
+   * @param errorLabel
    * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")
    * @returns object of the created collection
    */
@@ -1135,7 +1156,7 @@
    * Get token's owner
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param blockHashAt 
+   * @param blockHashAt
    * @example getTokenOwner(10, 5);
    * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}
    */
@@ -1162,7 +1183,7 @@
 
   /**
    * Changes the owner of the token.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
@@ -1175,9 +1196,9 @@
   }
 
   /**
-   * 
-   * Change ownership of a NFT on behalf of the owner. 
-   * 
+   *
+   * Change ownership of a NFT on behalf of the owner.
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
@@ -1194,7 +1215,7 @@
    * Recursively find the address that owns the token
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param blockHashAt 
+   * @param blockHashAt
    * @example getTokenTopmostOwner(10, 5);
    * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}
    */
@@ -1217,9 +1238,9 @@
    * Get tokens nested in the provided token
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param blockHashAt 
+   * @param blockHashAt
    * @example getTokenChildren(10, 5);
-   * @returns tokens whose depth of nesting is <= 5 
+   * @returns tokens whose depth of nesting is <= 5
    */
   async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {
     let children;
@@ -1239,7 +1260,7 @@
    * @param signer keyring of signer
    * @param tokenObj token to be nested
    * @param rootTokenObj token to be parent
-   * @param label 
+   * @param label
    * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});
    * @returns ```true``` if extrinsic success, otherwise ```false```
    */
@@ -1257,8 +1278,8 @@
    * @param signer keyring of signer
    * @param tokenObj token to unnest
    * @param rootTokenObj parent of a token
-   * @param toAddressObj address of a new token owner 
-   * @param label 
+   * @param toAddressObj address of a new token owner
+   * @param label
    * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});
    * @returns ```true``` if extrinsic success, otherwise ```false```
    */
@@ -1275,8 +1296,8 @@
    * Mint new collection
    * @param signer keyring of signer
    * @param collectionOptions Collection options
-   * @param label 
-   * @example 
+   * @param label
+   * @example
    * mintCollection(aliceKeyring, {
    *   name: 'New',
    *   description: 'New collection',
@@ -1292,7 +1313,7 @@
    * Mint new token
    * @param signer keyring of signer
    * @param data token data
-   * @param label 
+   * @param label
    * @returns created token object
    */
   async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }, label?: string): Promise<UniqueNFTToken> {
@@ -1317,8 +1338,8 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokens array of tokens with owner and properties
-   * @param label 
-   * @example 
+   * @param label
+   * @example
    * mintMultipleTokens(aliceKeyring, 10, [{
    *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},
    *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],
@@ -1345,7 +1366,7 @@
    * @param collectionId ID of collection
    * @param owner tokens owner
    * @param tokens array of tokens with owner and properties
-   * @param label 
+   * @param label
    * @example
    * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{
    *   properties: [{
@@ -1379,7 +1400,7 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param label 
+   * @param label
    * @example burnToken(aliceKeyring, 10, 5);
    * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```
    */
@@ -1389,12 +1410,12 @@
 
   /**
    * Set, change, or remove approved address to transfer the ownership of the NFT.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
    * @param toAddressObj address to approve
-   * @param label 
+   * @param label
    * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
    * @returns ```true``` if extrinsic success, otherwise ```false```
    */
@@ -1427,7 +1448,7 @@
   }
 
   /**
-   * Get top 10 token owners with the largest number of pieces 
+   * Get top 10 token owners with the largest number of pieces
    * @param collectionId ID of collection
    * @param tokenId ID of token
    * @example getTokenTop10Owners(10, 5);
@@ -1464,7 +1485,7 @@
   }
 
   /**
-   * Change ownership of some pieces of RFT on behalf of the owner. 
+   * Change ownership of some pieces of RFT on behalf of the owner.
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
@@ -1482,7 +1503,7 @@
    * Mint new collection
    * @param signer keyring of signer
    * @param collectionOptions Collection options
-   * @param label 
+   * @param label
    * @example
    * mintCollection(aliceKeyring, {
    *   name: 'New',
@@ -1499,7 +1520,7 @@
    * Mint new token
    * @param signer keyring of signer
    * @param data token data
-   * @param label 
+   * @param label
    * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});
    * @returns created token object
    */
@@ -1539,7 +1560,7 @@
    * @param collectionId ID of collection
    * @param owner tokens owner
    * @param tokens array of tokens with properties and pieces
-   * @param label 
+   * @param label
    * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);
    * @returns array of newly created RFT tokens
    */
@@ -1564,7 +1585,7 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
-   * @param label 
+   * @param label
    * @param amount number of pieces to be burnt
    * @example burnToken(aliceKeyring, 10, 5);
    * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```
@@ -1575,12 +1596,12 @@
 
   /**
    * Set, change, or remove approved address to transfer the ownership of the RFT.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param tokenId ID of token
    * @param toAddressObj address to approve
-   * @param label 
+   * @param label
    * @param amount number of pieces to be approved
    * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);
    * @returns true if the token success, otherwise false
@@ -1606,7 +1627,7 @@
    * @param collectionId ID of collection
    * @param tokenId ID of token
    * @param amount new number of pieces
-   * @param label 
+   * @param label
    * @example repartitionToken(aliceKeyring, 10, 5, 12345n);
    * @returns true if the repartion was success, otherwise false
    */
@@ -1639,8 +1660,8 @@
    * Mint new fungible collection
    * @param signer keyring of signer
    * @param collectionOptions Collection options
-   * @param decimalPoints number of token decimals 
-   * @param errorLabel 
+   * @param decimalPoints number of token decimals
+   * @param errorLabel
    * @example
    * mintCollection(aliceKeyring, {
    *   name: 'New',
@@ -1670,9 +1691,9 @@
    * @param collectionId ID of collection
    * @param owner address owner of new tokens
    * @param amount amount of tokens to be meanted
-   * @param label 
+   * @param label
    * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async mintTokens(signer: TSigner, collectionId: number, owner: ICrossAccountId | string, amount: bigint, label?: string): Promise<boolean> {
     if(typeof label === 'undefined') label = `collection #${collectionId}`;
@@ -1694,8 +1715,8 @@
    * @param collectionId ID of collection
    * @param owner tokens owner
    * @param tokens array of tokens with properties and pieces
-   * @param label 
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @param label
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {value: bigint}[], label?: string): Promise<boolean> {
     if(typeof label === 'undefined') label = `collection #${collectionId}`;
@@ -1713,7 +1734,7 @@
   }
 
   /**
-   * Get the top 10 owners with the largest balance for the Fungible collection 
+   * Get the top 10 owners with the largest balance for the Fungible collection
    * @param collectionId ID of collection
    * @example getTop10Owners(10);
    * @returns array of ```ICrossAccountId```
@@ -1740,7 +1761,7 @@
    * @param toAddressObj address recepient
    * @param amount amount of tokens to be sent
    * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount: bigint) {
     return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);
@@ -1754,7 +1775,7 @@
    * @param toAddressObj address where token to be sent
    * @param amount number of tokens to be sent
    * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount: bigint) {
     return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);
@@ -1765,9 +1786,9 @@
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param amount amount of tokens to be destroyed
-   * @param label 
+   * @param label
    * @example burnTokens(aliceKeyring, 10, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async burnTokens(signer: IKeyringPair, collectionId: number, amount=100n, label?: string): Promise<boolean> {
     return (await super.burnToken(signer, collectionId, 0, label, amount)).success;
@@ -1779,9 +1800,9 @@
    * @param collectionId ID of collection
    * @param fromAddressObj address on behalf of which tokens will be burnt
    * @param amount amount of tokens to be burnt
-   * @param label 
+   * @param label
    * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=100n, label?: string): Promise<boolean> {
     return await super.burnTokenFrom(signer, collectionId, fromAddressObj, 0, label, amount);
@@ -1789,8 +1810,8 @@
 
   /**
    * Get total collection supply
-   * @param collectionId 
-   * @returns 
+   * @param collectionId
+   * @returns
    */
   async getTotalPieces(collectionId: number): Promise<bigint> {
     return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();
@@ -1798,14 +1819,14 @@
 
   /**
    * Set, change, or remove approved address to transfer tokens.
-   * 
+   *
    * @param signer keyring of signer
    * @param collectionId ID of collection
    * @param toAddressObj address to be approved
    * @param amount amount of tokens to be approved
-   * @param label 
+   * @param label
    * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)
-   * @returns ```true``` if extrinsic success, otherwise ```false``` 
+   * @returns ```true``` if extrinsic success, otherwise ```false```
    */
   async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=100n, label?: string) {
     return super.approveToken(signer, collectionId, 0, toAddressObj, label, amount);
@@ -1998,7 +2019,7 @@
     this.nft = new NFTGroup(this);
     this.rft = new RFTGroup(this);
     this.ft = new FTGroup(this);
-  }  
+  }
 }
 
 
@@ -2059,6 +2080,10 @@
     return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj, label);
   }
 
+  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId, label?: string) {
+    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj, label);
+  }
+
   async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId, label?: string) {
     return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj, label);
   }
@@ -2403,4 +2428,4 @@
   async burn(signer: TSigner, amount=100n, label?: string) {
     return await this.collection.burnToken(signer, this.tokenId, amount, label);
   }
-}
\ No newline at end of file
+}