--- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -22,7 +22,9 @@ pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId}; use pallet_evm_coder_substrate::dispatch_to_evm; use sp_std::vec::Vec; -use up_data_structs::{Property, SponsoringRateLimit, OwnerRestrictedSet, AccessMode}; +use up_data_structs::{ + Property, SponsoringRateLimit, OwnerRestrictedSet, AccessMode, CollectionPermissions, +}; use alloc::format; use crate::{Pallet, CollectionHandle, Config, CollectionProperties}; @@ -47,10 +49,15 @@ #[solidity_interface(name = "Collection")] impl CollectionHandle -// where -// T::AccountId: From +where + T::AccountId: From<[u8; 32]>, { - fn set_collection_property(&mut self, caller: caller, key: string, value: bytes) -> Result<()> { + fn set_collection_property( + &mut self, + caller: caller, + key: string, + value: bytes, + ) -> Result { let caller = T::CrossAccountId::from_eth(caller); let key = >::from(key) .try_into() @@ -83,7 +90,7 @@ } fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result { - check_is_owner(caller, self)?; + check_is_owner_or_admin(caller, self)?; let sponsor = T::CrossAccountId::from_eth(sponsor); self.set_sponsor(sponsor.as_sub().clone()) @@ -97,14 +104,14 @@ .confirm_sponsorship(caller.as_sub()) .map_err(dispatch_to_evm::)? { - return Err(Error::Revert("Caller is not set as sponsor".into())); + return Err("caller is not set as sponsor".into()); } save(self) } #[solidity(rename_selector = "setCollectionLimit")] fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result { - check_is_owner(caller, self)?; + check_is_owner_or_admin(caller, self)?; let mut limits = self.limits.clone(); match limit.as_str() { @@ -128,7 +135,7 @@ } _ => { return Err(Error::Revert(format!( - "Unknown integer limit \"{}\"", + "unknown integer limit \"{}\"", limit ))) } @@ -140,7 +147,7 @@ #[solidity(rename_selector = "setCollectionLimit")] fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result { - check_is_owner(caller, self)?; + check_is_owner_or_admin(caller, self)?; let mut limits = self.limits.clone(); match limit.as_str() { @@ -155,7 +162,7 @@ } _ => { return Err(Error::Revert(format!( - "Unknown boolean limit \"{}\"", + "unknown boolean limit \"{}\"", limit ))) } @@ -169,52 +176,48 @@ Ok(crate::eth::collection_id_to_address(self.id)) } - // fn add_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result { - // let mut new_admin_h256 = H256::default(); - // new_admin.to_little_endian(&mut new_admin_h256.0); - // let account_id = T::AccountId::from(new_admin_h256); - // let caller = T::CrossAccountId::from_eth(caller); - // let new_admin = T::CrossAccountId::from_sub(account_id); - // >::toggle_admin(&self, &caller, &new_admin, true) - // .map_err(dispatch_to_evm::)?; - // Ok(()) - // } + fn add_collection_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let mut new_admin_arr: [u8; 32] = Default::default(); + new_admin.to_big_endian(&mut new_admin_arr); + let account_id = T::AccountId::from(new_admin_arr); + let new_admin = T::CrossAccountId::from_sub(account_id); + >::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::)?; + Ok(()) + } - // fn remove_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result { - // let mut new_admin_h256 = H256::default(); - // new_admin.to_little_endian(&mut new_admin_h256.0); - // let account_id = T::AccountId::from(new_admin_h256); - // let caller = T::CrossAccountId::from_eth(caller); - // let new_admin = T::CrossAccountId::from_sub(account_id); - // >::toggle_admin(&self, &caller, &new_admin, false) - // .map_err(dispatch_to_evm::)?; - // Ok(()) - // } + fn remove_collection_admin_substrate( + &self, + caller: caller, + new_admin: uint256, + ) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let mut new_admin_arr: [u8; 32] = Default::default(); + new_admin.to_big_endian(&mut new_admin_arr); + let account_id = T::AccountId::from(new_admin_arr); + let new_admin = T::CrossAccountId::from_sub(account_id); + >::toggle_admin(self, &caller, &new_admin, false) + .map_err(dispatch_to_evm::)?; + Ok(()) + } fn add_collection_admin(&self, caller: caller, new_admin: address) -> Result { let caller = T::CrossAccountId::from_eth(caller); - self.check_is_owner_or_admin(&caller) - .map_err(dispatch_to_evm::)?; let new_admin = T::CrossAccountId::from_eth(new_admin); - >::toggle_admin(&self, &caller, &new_admin, true) - .map_err(dispatch_to_evm::)?; + >::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::)?; Ok(()) } fn remove_collection_admin(&self, caller: caller, admin: address) -> Result { let caller = T::CrossAccountId::from_eth(caller); - self.check_is_owner_or_admin(&caller) - .map_err(dispatch_to_evm::)?; let admin = T::CrossAccountId::from_eth(admin); - >::toggle_admin(&self, &caller, &admin, false).map_err(dispatch_to_evm::)?; + >::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::)?; Ok(()) } #[solidity(rename_selector = "setCollectionNesting")] fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result { - let caller = T::CrossAccountId::from_eth(caller); - self.check_is_owner_or_admin(&caller) - .map_err(dispatch_to_evm::)?; + check_is_owner_or_admin(caller, self)?; let mut permissions = self.collection.permissions.clone(); let mut nesting = permissions.nesting().clone(); @@ -240,11 +243,9 @@ collections: Vec
, ) -> Result { if collections.is_empty() { - return Err("No addresses provided".into()); + return Err("no addresses provided".into()); } - let caller = T::CrossAccountId::from_eth(caller); - self.check_is_owner_or_admin(&caller) - .map_err(dispatch_to_evm::)?; + check_is_owner_or_admin(caller, self)?; let mut permissions = self.collection.permissions.clone(); match enable { @@ -280,27 +281,34 @@ } fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result { - let caller = T::CrossAccountId::from_eth(caller); - self.check_is_owner_or_admin(&caller) - .map_err(dispatch_to_evm::)?; - self.collection.permissions.access = Some(match mode { - 0 => AccessMode::Normal, - 1 => AccessMode::AllowList, - _ => return Err("Not supported access mode".into()), - }); - save(self)?; - Ok(()) + check_is_owner_or_admin(caller, self)?; + let permissions = CollectionPermissions { + access: Some(match mode { + 0 => AccessMode::Normal, + 1 => AccessMode::AllowList, + _ => return Err("not supported access mode".into()), + }), + ..Default::default() + }; + self.collection.permissions = >::clamp_permissions( + self.collection.mode.clone(), + &self.collection.permissions, + permissions, + ) + .map_err(dispatch_to_evm::)?; + + save(self) } fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result { - let caller = check_is_owner_or_admin(caller, self)?; + let caller = T::CrossAccountId::from_eth(caller); let user = T::CrossAccountId::from_eth(user); >::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::)?; Ok(()) } fn remove_from_collection_allow_list(&self, caller: caller, user: address) -> Result { - let caller = check_is_owner_or_admin(caller, self)?; + let caller = T::CrossAccountId::from_eth(caller); let user = T::CrossAccountId::from_eth(user); >::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::)?; Ok(()) @@ -308,18 +316,19 @@ fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result { check_is_owner_or_admin(caller, self)?; - self.collection.permissions.mint_mode = Some(mode); - save(self)?; - Ok(()) - } -} + let permissions = CollectionPermissions { + mint_mode: Some(mode), + ..Default::default() + }; + self.collection.permissions = >::clamp_permissions( + self.collection.mode.clone(), + &self.collection.permissions, + permissions, + ) + .map_err(dispatch_to_evm::)?; -fn check_is_owner(caller: caller, collection: &CollectionHandle) -> Result { - let caller = T::CrossAccountId::from_eth(caller); - collection - .check_is_owner(&caller) - .map_err(pallet_evm_coder_substrate::dispatch_to_evm::)?; - Ok(()) + save(self) + } } fn check_is_owner_or_admin( --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -148,7 +148,7 @@ .saturating_mul(writes), )) } - pub fn save(self) -> Result<(), DispatchError> { + pub fn save(self) -> DispatchResult { >::insert(self.id, self.collection); Ok(()) } --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -152,12 +152,15 @@ via("CollectionHandle", common_mut, Collection) ) )] -impl FungibleHandle {} +impl FungibleHandle where T::AccountId: From<[u8; 32]> {} generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true); generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false); -impl CommonEvmHandler for FungibleHandle { +impl CommonEvmHandler for FungibleHandle +where + T::AccountId: From<[u8; 32]>, +{ const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw"); fn call(self, handle: &mut impl PrecompileHandle) -> Option { --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -583,13 +583,16 @@ TokenProperties, ) )] -impl NonfungibleHandle {} +impl NonfungibleHandle where T::AccountId: From<[u8; 32]> {} // Not a tests, but code generators generate_stubgen!(gen_impl, UniqueNFTCall<()>, true); generate_stubgen!(gen_iface, UniqueNFTCall<()>, false); -impl CommonEvmHandler for NonfungibleHandle { +impl CommonEvmHandler for NonfungibleHandle +where + T::AccountId: From<[u8; 32]>, +{ const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw"); fn call(self, handle: &mut impl PrecompileHandle) -> Option { --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -297,7 +297,40 @@ } } -// Selector: 6aea9834 +// Selector: 780e9d63 +contract ERC721Enumerable is Dummy, ERC165 { + // Selector: tokenByIndex(uint256) 4f6ccce7 + function tokenByIndex(uint256 index) public view returns (uint256) { + require(false, stub_error); + index; + dummy; + return 0; + } + + // Not implemented + // + // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59 + function tokenOfOwnerByIndex(address owner, uint256 index) + public + view + returns (uint256) + { + require(false, stub_error); + owner; + index; + dummy; + return 0; + } + + // Selector: totalSupply() 18160ddd + function totalSupply() public view returns (uint256) { + require(false, stub_error); + dummy; + return 0; + } +} + +// Selector: 7d9262e6 contract Collection is Dummy, ERC165 { // Selector: setCollectionProperty(string,bytes) 2f073f66 function setCollectionProperty(string memory key, bytes memory value) @@ -366,6 +399,20 @@ return 0x0000000000000000000000000000000000000000; } + // Selector: addCollectionAdminSubstrate(uint256) 5730062b + function addCollectionAdminSubstrate(uint256 newAdmin) public view { + require(false, stub_error); + newAdmin; + dummy; + } + + // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9 + function removeCollectionAdminSubstrate(uint256 newAdmin) public view { + require(false, stub_error); + newAdmin; + dummy; + } + // Selector: addCollectionAdmin(address) 92e462c7 function addCollectionAdmin(address newAdmin) public view { require(false, stub_error); @@ -423,39 +470,6 @@ require(false, stub_error); mode; dummy = 0; - } -} - -// Selector: 780e9d63 -contract ERC721Enumerable is Dummy, ERC165 { - // Selector: tokenByIndex(uint256) 4f6ccce7 - function tokenByIndex(uint256 index) public view returns (uint256) { - require(false, stub_error); - index; - dummy; - return 0; - } - - // Not implemented - // - // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59 - function tokenOfOwnerByIndex(address owner, uint256 index) - public - view - returns (uint256) - { - require(false, stub_error); - owner; - index; - dummy; - return 0; - } - - // Selector: totalSupply() 18160ddd - function totalSupply() public view returns (uint256) { - require(false, stub_error); - dummy; - return 0; } } --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -491,7 +491,7 @@ let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); let mut target_collection = >::try_get(collection_id)?; - target_collection.check_is_owner(&sender)?; + target_collection.check_is_owner_or_admin(&sender)?; target_collection.check_is_internal()?; target_collection.set_sponsor(new_sponsor.clone())?; @@ -867,7 +867,7 @@ let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); let mut target_collection = >::try_get(collection_id)?; target_collection.check_is_internal()?; - target_collection.check_is_owner(&sender)?; + target_collection.check_is_owner_or_admin(&sender)?; let old_limit = &target_collection.limits; target_collection.limits = >::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?; @@ -889,7 +889,7 @@ let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); let mut target_collection = >::try_get(collection_id)?; target_collection.check_is_internal()?; - target_collection.check_is_owner(&sender)?; + target_collection.check_is_owner_or_admin(&sender)?; let old_limit = &target_collection.permissions; target_collection.permissions = >::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?; --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -812,7 +812,7 @@ for byte in key.as_slice().iter() { let byte = *byte; - if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' { + if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' { return Err(PropertiesError::InvalidCharacterInPropertyKey); } } --- a/runtime/common/src/dispatch.rs +++ b/runtime/common/src/dispatch.rs @@ -112,6 +112,7 @@ + pallet_fungible::Config + pallet_nonfungible::Config + pallet_refungible::Config, + T::AccountId: From<[u8; 32]>, { fn is_reserved(target: &H160) -> bool { map_eth_to_id(target).is_some() --- a/runtime/tests/src/tests.rs +++ b/runtime/tests/src/tests.rs @@ -1264,7 +1264,6 @@ let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1)); let origin1 = Origin::signed(1); - let origin2 = Origin::signed(2); // Add collection admins 2 and 3 assert_ok!(Unique::add_collection_admin( @@ -1273,7 +1272,7 @@ account(2) )); assert_ok!(Unique::add_collection_admin( - origin1, + origin1.clone(), collection1_id, account(3) )); @@ -1289,7 +1288,7 @@ // remove admin 3 assert_ok!(Unique::remove_collection_admin( - origin2, + origin1, CollectionId(1), account(3) )); --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -174,7 +174,24 @@ function finishMinting() external returns (bool); } -// Selector: 6aea9834 +// Selector: 780e9d63 +interface ERC721Enumerable is Dummy, ERC165 { + // Selector: tokenByIndex(uint256) 4f6ccce7 + function tokenByIndex(uint256 index) external view returns (uint256); + + // Not implemented + // + // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59 + function tokenOfOwnerByIndex(address owner, uint256 index) + external + view + returns (uint256); + + // Selector: totalSupply() 18160ddd + function totalSupply() external view returns (uint256); +} + +// Selector: 7d9262e6 interface Collection is Dummy, ERC165 { // Selector: setCollectionProperty(string,bytes) 2f073f66 function setCollectionProperty(string memory key, bytes memory value) @@ -206,6 +223,12 @@ // Selector: contractAddress() f6b4dfb4 function contractAddress() external view returns (address); + // Selector: addCollectionAdminSubstrate(uint256) 5730062b + function addCollectionAdminSubstrate(uint256 newAdmin) external view; + + // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9 + function removeCollectionAdminSubstrate(uint256 newAdmin) external view; + // Selector: addCollectionAdmin(address) 92e462c7 function addCollectionAdmin(address newAdmin) external view; @@ -230,23 +253,6 @@ // Selector: setCollectionMintMode(bool) 00018e84 function setCollectionMintMode(bool mode) external; -} - -// Selector: 780e9d63 -interface ERC721Enumerable is Dummy, ERC165 { - // Selector: tokenByIndex(uint256) 4f6ccce7 - function tokenByIndex(uint256 index) external view returns (uint256); - - // Not implemented - // - // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59 - function tokenOfOwnerByIndex(address owner, uint256 index) - external - view - returns (uint256); - - // Selector: totalSupply() 18160ddd - function totalSupply() external view returns (uint256); } // Selector: d74d154f --- /dev/null +++ b/tests/src/eth/collectionAdmin.test.ts @@ -0,0 +1,296 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect} from 'chai'; +import privateKey from '../substrate/privateKey'; +import { + createEthAccount, + createEthAccountWithBalance, + evmCollection, + evmCollectionHelpers, + getCollectionAddressFromResult, + itWeb3, +} from './util/helpers'; + +describe('Add collection admins', () => { + itWeb3('Add admin by owner', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const newAdmin = await createEthAccount(web3); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdmin(newAdmin).send(); + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(newAdmin.toLocaleLowerCase()); + }); + + itWeb3('Add substrate admin by owner', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const newAdmin = privateKey('//Alice'); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send(); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList[0].asSubstrate.toString().toLocaleLowerCase()) + .to.be.eq(newAdmin.address.toLocaleLowerCase()); + }); + + itWeb3('(!negative tests!) Add admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdmin(admin).send(); + + const user = await createEthAccount(web3); + await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(admin.toLocaleLowerCase()); + }); + + itWeb3('(!negative tests!) Add admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const notAdmin = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + + const user = await createEthAccount(web3); + await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(0); + }); + + itWeb3('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdmin(admin).send(); + + const notAdmin = privateKey('//Alice'); + await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin.addressRaw).call({from: admin})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(admin.toLocaleLowerCase()); + }); + + itWeb3('(!negative tests!) Add substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const notAdmin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + const notAdmin1 = privateKey('//Alice'); + await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin1.addressRaw).call({from: notAdmin0})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(0); + }); +}); + +describe('Remove collection admins', () => { + itWeb3('Remove admin by owner', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const newAdmin = await createEthAccount(web3); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdmin(newAdmin).send(); + { + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(newAdmin.toLocaleLowerCase()); + } + + await collectionEvm.methods.removeCollectionAdmin(newAdmin).send(); + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(0); + }); + + itWeb3('Remove substrate admin by owner', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const newAdmin = privateKey('//Alice'); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send(); + { + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList[0].asSubstrate.toString().toLocaleLowerCase()) + .to.be.eq(newAdmin.address.toLocaleLowerCase()); + } + + await collectionEvm.methods.removeCollectionAdminSubstrate(newAdmin.addressRaw).send(); + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(0); + }); + + itWeb3('(!negative tests!) Remove admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + + const admin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + await collectionEvm.methods.addCollectionAdmin(admin0).send(); + const admin1 = await createEthAccount(web3); + await collectionEvm.methods.addCollectionAdmin(admin1).send(); + + await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0})) + .to.be.rejectedWith('NoPermission'); + { + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(2); + expect(adminList.toString().toLocaleLowerCase()) + .to.be.deep.contains(admin0.toLocaleLowerCase()) + .to.be.deep.contains(admin1.toLocaleLowerCase()); + } + }); + + itWeb3('(!negative tests!) Remove admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + + const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + await collectionEvm.methods.addCollectionAdmin(admin).send(); + const notAdmin = await createEthAccount(web3); + + await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin})) + .to.be.rejectedWith('NoPermission'); + { + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(admin.toLocaleLowerCase()); + expect(adminList.length).to.be.eq(1); + } + }); + + itWeb3('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const adminSub = privateKey('//Alice'); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send(); + const adminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + await collectionEvm.methods.addCollectionAdmin(adminEth).send(); + + await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: adminEth})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(2); + expect(adminList.toString().toLocaleLowerCase()) + .to.be.deep.contains(adminSub.address.toLocaleLowerCase()) + .to.be.deep.contains(adminEth.toLocaleLowerCase()); + }); + + itWeb3('(!negative tests!) Remove substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collectionHelper = evmCollectionHelpers(web3, owner); + + const result = await collectionHelper.methods + .createNonfungibleCollection('A', 'B', 'C') + .send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + + const adminSub = privateKey('//Alice'); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send(); + const notAdminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + + await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: notAdminEth})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await api.rpc.unique.adminlist(collectionId); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asSubstrate.toString().toLocaleLowerCase()) + .to.be.eq(adminSub.address.toLocaleLowerCase()); + }); +}); \ No newline at end of file --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -233,7 +233,7 @@ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor'); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; @@ -241,14 +241,13 @@ expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); const user = createEthAccount(web3); - let nextTokenId = await collectionEvm.methods.nextTokenId().call(); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); expect(nextTokenId).to.be.equal('1'); const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); expect(oldPermissions.mintMode).to.be.false; expect(oldPermissions.access).to.be.equal('Normal'); - //TODO: change value, when enum generated await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); @@ -260,34 +259,35 @@ const ownerBalanceBefore = await ethBalanceViaSub(api, owner); const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor); - nextTokenId = await collectionEvm.methods.nextTokenId().call({from: user}); - expect(nextTokenId).to.be.equal('1'); - result = await collectionEvm.methods.mintWithTokenURI( - user, - nextTokenId, - 'Test URI', - ).send({from: user}); - const events = normalizeEvents(result.events); - events[0].address = events[0].address.toLocaleLowerCase(); + { + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await collectionEvm.methods.mintWithTokenURI( + user, + nextTokenId, + 'Test URI', + ).send({from: user}); + const events = normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address: collectionIdAddress.toLocaleLowerCase(), - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: nextTokenId, + expect(events).to.be.deep.equal([ + { + address: collectionIdAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: nextTokenId, + }, }, - }, - ]); + ]); - expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + const ownerBalanceAfter = await ethBalanceViaSub(api, owner); + const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor); - const ownerBalanceAfter = await ethBalanceViaSub(api, owner); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } }); itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3, privateKeyWrapper}) => { @@ -302,7 +302,7 @@ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor'); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); await sponsorCollection.methods.confirmCollectionSponsorship().send(); collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; @@ -314,6 +314,7 @@ const ownerBalanceBefore = await ethBalanceViaSub(api, owner); const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor); + const userCollectionEvm = evmCollection(web3, user, collectionIdAddress); const nextTokenId = await userCollectionEvm.methods.nextTokenId().call(); --- a/tests/src/eth/createCollection.test.ts +++ b/tests/src/eth/createCollection.test.ts @@ -80,7 +80,7 @@ expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor'); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); await sponsorCollection.methods.confirmCollectionSponsorship().send(); collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; @@ -208,7 +208,7 @@ const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); await expect(sponsorCollection.methods .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('Caller is not set as sponsor'); + .call()).to.be.rejectedWith('caller is not set as sponsor'); } { await expect(contractEvmFromNotOwner.methods @@ -225,6 +225,6 @@ const collectionEvm = evmCollection(web3, owner, collectionIdAddress); await expect(collectionEvm.methods .setCollectionLimit('badLimit', 'true') - .call()).to.be.rejectedWith('Unknown boolean limit "badLimit"'); + .call()).to.be.rejectedWith('unknown boolean limit "badLimit"'); }); }); \ No newline at end of file --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -91,6 +91,15 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "newAdmin", "type": "uint256" } + ], + "name": "addCollectionAdminSubstrate", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "user", "type": "address" } ], "name": "addToCollectionAllowList", @@ -300,6 +309,15 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "newAdmin", "type": "uint256" } + ], + "name": "removeCollectionAdminSubstrate", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "user", "type": "address" } ], "name": "removeFromCollectionAllowList", --- a/tests/src/interfaces/augment-api-query.ts +++ b/tests/src/interfaces/augment-api-query.ts @@ -5,7 +5,7 @@ import type { BTreeMap, Bytes, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types-codec'; import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUnqSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsTokenChild } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsTokenChild } from '@polkadot/types/lookup'; import type { Observable } from '@polkadot/types/types'; declare module '@polkadot/api-base/types/storage' { @@ -436,7 +436,7 @@ /** * Items to be executed, indexed by the block number that they should be executed on. **/ - agenda: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; + agenda: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; /** * Lookup from identity to the block number and index of the task. **/ --- a/tests/src/interfaces/augment-types.ts +++ b/tests/src/interfaces/augment-types.ts @@ -1,7 +1,7 @@ // Auto-generated via `yarn polkadot-types-from-defs`, do not edit /* eslint-disable */ -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUnqSchedulerCall, PalletUnqSchedulerError, PalletUnqSchedulerEvent, PalletUnqSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; import type { Data, StorageKey } from '@polkadot/types'; import type { BitVec, Bool, Bytes, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec'; import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets'; @@ -819,10 +819,10 @@ PalletUniqueCall: PalletUniqueCall; PalletUniqueError: PalletUniqueError; PalletUniqueRawEvent: PalletUniqueRawEvent; - PalletUnqSchedulerCall: PalletUnqSchedulerCall; - PalletUnqSchedulerError: PalletUnqSchedulerError; - PalletUnqSchedulerEvent: PalletUnqSchedulerEvent; - PalletUnqSchedulerScheduledV3: PalletUnqSchedulerScheduledV3; + PalletUniqueSchedulerCall: PalletUniqueSchedulerCall; + PalletUniqueSchedulerError: PalletUniqueSchedulerError; + PalletUniqueSchedulerEvent: PalletUniqueSchedulerEvent; + PalletUniqueSchedulerScheduledV3: PalletUniqueSchedulerScheduledV3; PalletVersion: PalletVersion; PalletXcmCall: PalletXcmCall; PalletXcmError: PalletXcmError; --- a/tests/src/interfaces/default/types.ts +++ b/tests/src/interfaces/default/types.ts @@ -1784,8 +1784,8 @@ readonly type: 'CollectionSponsorRemoved' | 'CollectionAdminAdded' | 'CollectionOwnedChanged' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionAdminRemoved' | 'AllowListAddressRemoved' | 'AllowListAddressAdded' | 'CollectionLimitSet' | 'CollectionPermissionSet'; } -/** @name PalletUnqSchedulerCall */ -export interface PalletUnqSchedulerCall extends Enum { +/** @name PalletUniqueSchedulerCall */ +export interface PalletUniqueSchedulerCall extends Enum { readonly isScheduleNamed: boolean; readonly asScheduleNamed: { readonly id: U8aFixed; @@ -1809,8 +1809,8 @@ readonly type: 'ScheduleNamed' | 'CancelNamed' | 'ScheduleNamedAfter'; } -/** @name PalletUnqSchedulerError */ -export interface PalletUnqSchedulerError extends Enum { +/** @name PalletUniqueSchedulerError */ +export interface PalletUniqueSchedulerError extends Enum { readonly isFailedToSchedule: boolean; readonly isNotFound: boolean; readonly isTargetBlockNumberInPast: boolean; @@ -1818,8 +1818,8 @@ readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange'; } -/** @name PalletUnqSchedulerEvent */ -export interface PalletUnqSchedulerEvent extends Enum { +/** @name PalletUniqueSchedulerEvent */ +export interface PalletUniqueSchedulerEvent extends Enum { readonly isScheduled: boolean; readonly asScheduled: { readonly when: u32; @@ -1845,8 +1845,8 @@ readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallLookupFailed'; } -/** @name PalletUnqSchedulerScheduledV3 */ -export interface PalletUnqSchedulerScheduledV3 extends Struct { +/** @name PalletUniqueSchedulerScheduledV3 */ +export interface PalletUniqueSchedulerScheduledV3 extends Struct { readonly maybeId: Option; readonly priority: u8; readonly call: FrameSupportScheduleMaybeHashed; --- a/tests/src/interfaces/lookup.ts +++ b/tests/src/interfaces/lookup.ts @@ -1526,9 +1526,9 @@ users: 'BTreeMap' }, /** - * Lookup206: pallet_unq_scheduler::pallet::Call + * Lookup206: pallet_unique_scheduler::pallet::Call **/ - PalletUnqSchedulerCall: { + PalletUniqueSchedulerCall: { _enum: { schedule_named: { id: '[u8;16]', @@ -2181,9 +2181,9 @@ } }, /** - * Lookup283: pallet_unq_scheduler::pallet::Event + * Lookup283: pallet_unique_scheduler::pallet::Event **/ - PalletUnqSchedulerEvent: { + PalletUniqueSchedulerEvent: { _enum: { Scheduled: { when: 'u32', @@ -2589,9 +2589,9 @@ _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument'] }, /** - * Lookup344: pallet_unq_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> + * Lookup344: pallet_unique_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> **/ - PalletUnqSchedulerScheduledV3: { + PalletUniqueSchedulerScheduledV3: { maybeId: 'Option<[u8;16]>', priority: 'u8', call: 'FrameSupportScheduleMaybeHashed', @@ -2748,9 +2748,9 @@ **/ SpCoreVoid: 'Null', /** - * Lookup351: pallet_unq_scheduler::pallet::Error + * Lookup351: pallet_unique_scheduler::pallet::Error **/ - PalletUnqSchedulerError: { + PalletUniqueSchedulerError: { _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange'] }, /** --- a/tests/src/interfaces/registry.ts +++ b/tests/src/interfaces/registry.ts @@ -1,7 +1,7 @@ // Auto-generated via `yarn polkadot-types-from-defs`, do not edit /* eslint-disable */ -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUnqSchedulerCall, PalletUnqSchedulerError, PalletUnqSchedulerEvent, PalletUnqSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; declare module '@polkadot/types/types/registry' { export interface InterfaceTypes { @@ -133,10 +133,10 @@ PalletUniqueCall: PalletUniqueCall; PalletUniqueError: PalletUniqueError; PalletUniqueRawEvent: PalletUniqueRawEvent; - PalletUnqSchedulerCall: PalletUnqSchedulerCall; - PalletUnqSchedulerError: PalletUnqSchedulerError; - PalletUnqSchedulerEvent: PalletUnqSchedulerEvent; - PalletUnqSchedulerScheduledV3: PalletUnqSchedulerScheduledV3; + PalletUniqueSchedulerCall: PalletUniqueSchedulerCall; + PalletUniqueSchedulerError: PalletUniqueSchedulerError; + PalletUniqueSchedulerEvent: PalletUniqueSchedulerEvent; + PalletUniqueSchedulerScheduledV3: PalletUniqueSchedulerScheduledV3; PalletXcmCall: PalletXcmCall; PalletXcmError: PalletXcmError; PalletXcmEvent: PalletXcmEvent; --- a/tests/src/interfaces/types-lookup.ts +++ b/tests/src/interfaces/types-lookup.ts @@ -1650,8 +1650,8 @@ readonly users: BTreeMap; } - /** @name PalletUnqSchedulerCall (206) */ - export interface PalletUnqSchedulerCall extends Enum { + /** @name PalletUniqueSchedulerCall (206) */ + export interface PalletUniqueSchedulerCall extends Enum { readonly isScheduleNamed: boolean; readonly asScheduleNamed: { readonly id: U8aFixed; @@ -2350,8 +2350,8 @@ readonly type: 'CollectionSponsorRemoved' | 'CollectionAdminAdded' | 'CollectionOwnedChanged' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionAdminRemoved' | 'AllowListAddressRemoved' | 'AllowListAddressAdded' | 'CollectionLimitSet' | 'CollectionPermissionSet'; } - /** @name PalletUnqSchedulerEvent (283) */ - export interface PalletUnqSchedulerEvent extends Enum { + /** @name PalletUniqueSchedulerEvent (283) */ + export interface PalletUniqueSchedulerEvent extends Enum { readonly isScheduled: boolean; readonly asScheduled: { readonly when: u32; @@ -2805,8 +2805,8 @@ readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument'; } - /** @name PalletUnqSchedulerScheduledV3 (344) */ - export interface PalletUnqSchedulerScheduledV3 extends Struct { + /** @name PalletUniqueSchedulerScheduledV3 (344) */ + export interface PalletUniqueSchedulerScheduledV3 extends Struct { readonly maybeId: Option; readonly priority: u8; readonly call: FrameSupportScheduleMaybeHashed; @@ -2864,8 +2864,8 @@ /** @name SpCoreVoid (350) */ export type SpCoreVoid = Null; - /** @name PalletUnqSchedulerError (351) */ - export interface PalletUnqSchedulerError extends Enum { + /** @name PalletUniqueSchedulerError (351) */ + export interface PalletUniqueSchedulerError extends Enum { readonly isFailedToSchedule: boolean; readonly isNotFound: boolean; readonly isTargetBlockNumberInPast: boolean; --- a/tests/src/nesting/properties.test.ts +++ b/tests/src/nesting/properties.test.ts @@ -106,6 +106,58 @@ }); }); + it('Check valid names for collection properties keys', async () => { + await usingApi(async api => { + const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'})); + const {collectionId} = getCreateCollectionResult(events); + + // alpha symbols + await expect(executeTransaction( + api, + bob, + api.tx.unique.setCollectionProperties(collectionId, [{key: 'alpha'}]), + )).to.not.be.rejected; + + // numeric symbols + await expect(executeTransaction( + api, + bob, + api.tx.unique.setCollectionProperties(collectionId, [{key: '123'}]), + )).to.not.be.rejected; + + // underscore symbol + await expect(executeTransaction( + api, + bob, + api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), + )).to.not.be.rejected; + + // dash symbol + await expect(executeTransaction( + api, + bob, + api.tx.unique.setCollectionProperties(collectionId, [{key: 'semi-automatic'}]), + )).to.not.be.rejected; + + // underscore symbol + await expect(executeTransaction( + api, + bob, + api.tx.unique.setCollectionProperties(collectionId, [{key: 'build.rs'}]), + )).to.not.be.rejected; + + const propertyKeys = ['alpha', '123', 'black_hole', 'semi-automatic', 'build.rs']; + const properties = (await api.rpc.unique.collectionProperties(collectionId, propertyKeys)).toHuman(); + expect(properties).to.be.deep.equal([ + {key: 'alpha', value: ''}, + {key: '123', value: ''}, + {key: 'black_hole', value: ''}, + {key: 'semi-automatic', value: ''}, + {key: 'build.rs', value: ''}, + ]); + }); + }); + it('Changes properties of a collection', async () => { await usingApi(async api => { const collection = await createCollectionExpectSuccess(); @@ -241,7 +293,7 @@ const invalidProperties = [ [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}], - [{key: 'Mr.Sandman', value: 'Bring me a gene'}], + [{key: 'Mr/Sandman', value: 'Bring me a gene'}], [{key: 'déjà vu', value: 'hmm...'}], ]; --- a/tests/src/removeCollectionAdmin.test.ts +++ b/tests/src/removeCollectionAdmin.test.ts @@ -118,11 +118,17 @@ const bob = privateKeyWrapper('//Bob'); const charlie = privateKeyWrapper('//Charlie'); + const addBobAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(bob.address)); + await submitTransactionAsync(alice, addBobAdminTx); + const addCharlieAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(charlie.address)); + await submitTransactionAsync(alice, addCharlieAdminTx); + const adminListAfterAddAdmin = await getAdminList(api, collectionId); expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address)); + expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(charlie.address)); const removeAdminTx = api.tx.unique.removeCollectionAdmin(collectionId, normalizeAccountId(bob.address)); - await expect(submitTransactionAsync(charlie, removeAdminTx)).to.be.rejected; + await expect(submitTransactionExpectFailAsync(charlie, removeAdminTx)).to.be.rejected; const adminListAfterRemoveAdmin = await getAdminList(api, collectionId); expect(adminListAfterRemoveAdmin).to.be.deep.contains(normalizeAccountId(bob.address)); --- a/tests/src/setCollectionLimits.test.ts +++ b/tests/src/setCollectionLimits.test.ts @@ -46,6 +46,7 @@ before(async () => { await usingApi(async (api, privateKeyWrapper) => { alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}}); }); }); @@ -115,6 +116,21 @@ }); }); + it('execute setCollectionLimits from admin collection', async () => { + await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob.address); + await usingApi(async (api: ApiPromise) => { + tx = api.tx.unique.setCollectionLimits( + collectionIdForTesting, + { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + }, + ); + await expect(submitTransactionAsync(bob, tx)).to.be.not.rejected; + }); + }); }); describe('setCollectionLimits negative', () => { @@ -143,21 +159,6 @@ }); }); it('execute setCollectionLimits from user who is not owner of this collection', async () => { - await usingApi(async (api: ApiPromise) => { - tx = api.tx.unique.setCollectionLimits( - collectionIdForTesting, - { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - }, - ); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; - }); - }); - it('execute setCollectionLimits from admin collection', async () => { - await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob.address); await usingApi(async (api: ApiPromise) => { tx = api.tx.unique.setCollectionLimits( collectionIdForTesting, --- a/tests/src/setCollectionSponsor.test.ts +++ b/tests/src/setCollectionSponsor.test.ts @@ -65,6 +65,11 @@ await setCollectionSponsorExpectSuccess(collectionId, bob.address); await setCollectionSponsorExpectSuccess(collectionId, charlie.address); }); + it('Collection admin add sponsor', async () => { + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); + await setCollectionSponsorExpectSuccess(collectionId, charlie.address, '//Bob'); + }); }); describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => { @@ -93,10 +98,5 @@ const collectionId = await createCollectionExpectSuccess(); await destroyCollectionExpectSuccess(collectionId); await setCollectionSponsorExpectFailure(collectionId, bob.address); - }); - it('(!negative test!) Collection admin add sponsor', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Bob'); }); }); --- a/tests/src/setMintPermission.test.ts +++ b/tests/src/setMintPermission.test.ts @@ -67,6 +67,14 @@ await setMintPermissionExpectSuccess(alice, collectionId, false); }); }); + + it('Collection admin success on set', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); + await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); + await setMintPermissionExpectSuccess(bob, collectionId, true); + }); + }); }); describe('Negative Integration Test setMintPermission', () => { @@ -100,14 +108,6 @@ const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); await enableAllowListExpectSuccess(alice, collectionId); await setMintPermissionExpectFailure(bob, collectionId, true); - }); - - it('Collection admin fails on set', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await setMintPermissionExpectFailure(bob, collectionId, true); - }); }); it('ensure non-allow-listed non-privileged address can\'t mint tokens', async () => { --- a/tests/src/setPublicAccessMode.test.ts +++ b/tests/src/setPublicAccessMode.test.ts @@ -103,22 +103,23 @@ await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; }); }); -}); -describe('Negative Integration Test ext. collection admin setPublicAccessMode(): ', () => { - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - }); - }); it('setPublicAccessMode by collection admin', async () => { await usingApi(async (api: ApiPromise) => { // tslint:disable-next-line: no-bitwise const collectionId = await createCollectionExpectSuccess(); await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: 'AllowList'}); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; + await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.not.rejected; + }); + }); +}); + +describe('Negative Integration Test ext. collection admin setPublicAccessMode(): ', () => { + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); }); }); });