difftreelog
Merge pull request #368 from UniqueNetwork/feature/CORE-386_1
in: master
Feature/core 386 1
27 files changed
pallets/common/src/erc.rsdiffbeforeafterboth--- 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<T: Config> CollectionHandle<T>
-// where
-// T::AccountId: From<H256>
+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<void> {
let caller = T::CrossAccountId::from_eth(caller);
let key = <Vec<u8>>::from(key)
.try_into()
@@ -83,7 +90,7 @@
}
fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {
- 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::<T>)?
{
- 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<void> {
- 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<void> {
- 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<void> {
- // 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);
- // <Pallet<T>>::toggle_admin(&self, &caller, &new_admin, true)
- // .map_err(dispatch_to_evm::<T>)?;
- // Ok(())
- // }
+ fn add_collection_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {
+ 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);
+ <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
- // fn remove_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {
- // 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);
- // <Pallet<T>>::toggle_admin(&self, &caller, &new_admin, false)
- // .map_err(dispatch_to_evm::<T>)?;
- // Ok(())
- // }
+ fn remove_collection_admin_substrate(
+ &self,
+ caller: caller,
+ new_admin: uint256,
+ ) -> Result<void> {
+ 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);
+ <Pallet<T>>::toggle_admin(self, &caller, &new_admin, false)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
fn add_collection_admin(&self, caller: caller, new_admin: address) -> Result<void> {
let caller = T::CrossAccountId::from_eth(caller);
- self.check_is_owner_or_admin(&caller)
- .map_err(dispatch_to_evm::<T>)?;
let new_admin = T::CrossAccountId::from_eth(new_admin);
- <Pallet<T>>::toggle_admin(&self, &caller, &new_admin, true)
- .map_err(dispatch_to_evm::<T>)?;
+ <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
Ok(())
}
fn remove_collection_admin(&self, caller: caller, admin: address) -> Result<void> {
let caller = T::CrossAccountId::from_eth(caller);
- self.check_is_owner_or_admin(&caller)
- .map_err(dispatch_to_evm::<T>)?;
let admin = T::CrossAccountId::from_eth(admin);
- <Pallet<T>>::toggle_admin(&self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
+ <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
Ok(())
}
#[solidity(rename_selector = "setCollectionNesting")]
fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- self.check_is_owner_or_admin(&caller)
- .map_err(dispatch_to_evm::<T>)?;
+ 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<address>,
) -> Result<void> {
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::<T>)?;
+ 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<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- self.check_is_owner_or_admin(&caller)
- .map_err(dispatch_to_evm::<T>)?;
- 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 = <Pallet<T>>::clamp_permissions(
+ self.collection.mode.clone(),
+ &self.collection.permissions,
+ permissions,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
+
+ save(self)
}
fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {
- let caller = check_is_owner_or_admin(caller, self)?;
+ let caller = T::CrossAccountId::from_eth(caller);
let user = T::CrossAccountId::from_eth(user);
<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
Ok(())
}
fn remove_from_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {
- let caller = check_is_owner_or_admin(caller, self)?;
+ let caller = T::CrossAccountId::from_eth(caller);
let user = T::CrossAccountId::from_eth(user);
<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
Ok(())
@@ -308,18 +316,19 @@
fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {
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 = <Pallet<T>>::clamp_permissions(
+ self.collection.mode.clone(),
+ &self.collection.permissions,
+ permissions,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
-fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- collection
- .check_is_owner(&caller)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
- Ok(())
+ save(self)
+ }
}
fn check_is_owner_or_admin<T: Config>(
pallets/common/src/lib.rsdiffbeforeafterboth--- 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 {
<CollectionById<T>>::insert(self.id, self.collection);
Ok(())
}
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -152,12 +152,15 @@
via("CollectionHandle<T>", common_mut, Collection)
)
)]
-impl<T: Config> FungibleHandle<T> {}
+impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);
generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);
-impl<T: Config> CommonEvmHandler for FungibleHandle<T> {
+impl<T: Config> CommonEvmHandler for FungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");
fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -583,13 +583,16 @@
TokenProperties,
)
)]
-impl<T: Config> NonfungibleHandle<T> {}
+impl<T: Config> NonfungibleHandle<T> 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<T: Config> CommonEvmHandler for NonfungibleHandle<T> {
+impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");
fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- 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;
}
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- 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 = <CollectionHandle<T>>::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 = <CollectionHandle<T>>::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 = <PalletCommon<T>>::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 = <CollectionHandle<T>>::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 = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- 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);
}
}
runtime/common/src/dispatch.rsdiffbeforeafterboth--- 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()
runtime/tests/src/tests.rsdiffbeforeafterboth--- 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)
));
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- 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
tests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth--- /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 <http://www.gnu.org/licenses/>.
+
+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
tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth--- 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();
tests/src/eth/createCollection.test.tsdiffbeforeafterboth--- 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
tests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth--- 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",
tests/src/interfaces/augment-api-query.tsdiffbeforeafterboth--- 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<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<Option<PalletUnqSchedulerScheduledV3>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+ agenda: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<Option<PalletUniqueSchedulerScheduledV3>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
* Lookup from identity to the block number and index of the task.
**/
tests/src/interfaces/augment-types.tsdiffbeforeafterboth--- 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;
tests/src/interfaces/default/types.tsdiffbeforeafterboth--- 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<U8aFixed>;
readonly priority: u8;
readonly call: FrameSupportScheduleMaybeHashed;
tests/src/interfaces/lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1526,9 +1526,9 @@
users: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>'
},
/**
- * Lookup206: pallet_unq_scheduler::pallet::Call<T>
+ * Lookup206: pallet_unique_scheduler::pallet::Call<T>
**/
- PalletUnqSchedulerCall: {
+ PalletUniqueSchedulerCall: {
_enum: {
schedule_named: {
id: '[u8;16]',
@@ -2181,9 +2181,9 @@
}
},
/**
- * Lookup283: pallet_unq_scheduler::pallet::Event<T>
+ * Lookup283: pallet_unique_scheduler::pallet::Event<T>
**/
- PalletUnqSchedulerEvent: {
+ PalletUniqueSchedulerEvent: {
_enum: {
Scheduled: {
when: 'u32',
@@ -2589,9 +2589,9 @@
_enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument']
},
/**
- * Lookup344: pallet_unq_scheduler::ScheduledV3<frame_support::traits::schedule::MaybeHashed<opal_runtime::Call, primitive_types::H256>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
+ * Lookup344: pallet_unique_scheduler::ScheduledV3<frame_support::traits::schedule::MaybeHashed<opal_runtime::Call, primitive_types::H256>, 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<T>
+ * Lookup351: pallet_unique_scheduler::pallet::Error<T>
**/
- PalletUnqSchedulerError: {
+ PalletUniqueSchedulerError: {
_enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange']
},
/**
tests/src/interfaces/registry.tsdiffbeforeafterboth--- 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;
tests/src/interfaces/types-lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1650,8 +1650,8 @@
readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
}
- /** @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<U8aFixed>;
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;
tests/src/nesting/properties.test.tsdiffbeforeafterboth1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4 addCollectionAdminExpectSuccess,5 createCollectionExpectSuccess,6 createItemExpectSuccess,7 getCreateCollectionResult,8 transferExpectSuccess,9} from '../util/helpers';10import {IKeyringPair} from '@polkadot/types/types';1112let alice: IKeyringPair;13let bob: IKeyringPair;14let charlie: IKeyringPair;1516describe('Composite Properties Test', () => {17 before(async () => {18 await usingApi(async (api, privateKeyWrapper) => {19 alice = privateKeyWrapper('//Alice');20 bob = privateKeyWrapper('//Bob');21 });22 });2324 it('Makes sure collectionById supplies required fields', async () => {25 await usingApi(async api => {26 const collectionId = await createCollectionExpectSuccess();2728 const collectionOption = await api.rpc.unique.collectionById(collectionId);29 expect(collectionOption.isSome).to.be.true;30 let collection = collectionOption.unwrap();31 expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;32 expect(collection.properties.toHuman()).to.be.empty;3334 const propertyPermissions = [35 {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},36 {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},37 ];38 await expect(executeTransaction(39 api, 40 alice, 41 api.tx.unique.setPropertyPermissions(collectionId, propertyPermissions), 42 )).to.not.be.rejected;4344 const collectionProperties = [45 {key: 'black_hole', value: 'LIGO'},46 {key: 'electron', value: 'come bond'}, 47 ];48 await expect(executeTransaction(49 api, 50 alice, 51 api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 52 )).to.not.be.rejected;5354 collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();55 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);56 expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);57 });58 });59});6061// ---------- COLLECTION PROPERTIES6263describe('Integration Test: Collection Properties', () => {64 before(async () => {65 await usingApi(async (api, privateKeyWrapper) => {66 alice = privateKeyWrapper('//Alice');67 bob = privateKeyWrapper('//Bob');68 });69 });7071 it('Reads properties from a collection', async () => {72 await usingApi(async api => {73 const collection = await createCollectionExpectSuccess();74 const properties = (await api.query.common.collectionProperties(collection)).toJSON();75 expect(properties.map).to.be.empty;76 expect(properties.consumedSpace).to.equal(0);77 });78 });7980 it('Sets properties for a collection', async () => {81 await usingApi(async api => {82 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));83 const {collectionId} = getCreateCollectionResult(events);8485 // As owner86 await expect(executeTransaction(87 api, 88 bob, 89 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 90 )).to.not.be.rejected;9192 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);9394 // As administrator95 await expect(executeTransaction(96 api, 97 alice, 98 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 99 )).to.not.be.rejected;100101 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();102 expect(properties).to.be.deep.equal([103 {key: 'electron', value: 'come bond'},104 {key: 'black_hole', value: ''},105 ]);106 });107 });108109 it('Changes properties of a collection', async () => {110 await usingApi(async api => {111 const collection = await createCollectionExpectSuccess();112113 await expect(executeTransaction(114 api, 115 alice, 116 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 117 )).to.not.be.rejected;118119 // Mutate the properties120 await expect(executeTransaction(121 api, 122 alice, 123 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 124 )).to.not.be.rejected;125126 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();127 expect(properties).to.be.deep.equal([128 {key: 'electron', value: 'bonded'},129 {key: 'black_hole', value: 'LIGO'},130 ]);131 });132 });133134 it('Deletes properties of a collection', async () => {135 await usingApi(async api => {136 const collection = await createCollectionExpectSuccess();137138 await expect(executeTransaction(139 api, 140 alice, 141 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 142 )).to.not.be.rejected;143144 await expect(executeTransaction(145 api, 146 alice, 147 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 148 )).to.not.be.rejected;149150 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();151 expect(properties).to.be.deep.equal([152 {key: 'black_hole', value: 'LIGO'},153 ]);154 });155 });156});157158describe('Negative Integration Test: Collection Properties', () => {159 before(async () => {160 await usingApi(async (api, privateKeyWrapper) => {161 alice = privateKeyWrapper('//Alice');162 bob = privateKeyWrapper('//Bob');163 });164 });165 166 it('Fails to set properties in a collection if not its onwer/administrator', async () => {167 await usingApi(async api => {168 const collection = await createCollectionExpectSuccess();169170 await expect(executeTransaction(171 api, 172 bob, 173 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 174 )).to.be.rejectedWith(/common\.NoPermission/);175 176 const properties = (await api.query.common.collectionProperties(collection)).toJSON();177 expect(properties.map).to.be.empty;178 expect(properties.consumedSpace).to.equal(0);179 });180 });181 182 it('Fails to set properties that exceed the limits', async () => {183 await usingApi(async api => {184 const collection = await createCollectionExpectSuccess();185 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 186187 // Mute the general tx parsing error, too many bytes to process188 {189 console.error = () => {};190 await expect(executeTransaction(191 api, 192 alice, 193 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 194 )).to.be.rejected;195 }196197 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();198 expect(properties).to.be.empty;199200 await expect(executeTransaction(201 api, 202 alice, 203 api.tx.unique.setCollectionProperties(collection, [204 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 205 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 206 ]), 207 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);208209 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();210 expect(properties).to.be.empty;211 });212 });213 214 it('Fails to set more properties than it is allowed', async () => {215 await usingApi(async api => {216 const collection = await createCollectionExpectSuccess();217218 const propertiesToBeSet = [];219 for (let i = 0; i < 65; i++) {220 propertiesToBeSet.push({221 key: 'electron_' + i,222 value: Math.random() > 0.5 ? 'high' : 'low',223 });224 }225226 await expect(executeTransaction(227 api, 228 alice, 229 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 230 )).to.be.rejectedWith(/common\.PropertyLimitReached/);231232 const properties = (await api.query.common.collectionProperties(collection)).toJSON();233 expect(properties.map).to.be.empty;234 expect(properties.consumedSpace).to.equal(0);235 });236 });237238 it('Fails to set properties with invalid names', async () => {239 await usingApi(async api => {240 const collection = await createCollectionExpectSuccess();241242 const invalidProperties = [243 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],244 [{key: 'Mr.Sandman', value: 'Bring me a gene'}],245 [{key: 'déjà vu', value: 'hmm...'}],246 ];247248 for (let i = 0; i < invalidProperties.length; i++) {249 await expect(executeTransaction(250 api, 251 alice, 252 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 253 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);254 }255256 await expect(executeTransaction(257 api, 258 alice, 259 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 260 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);261262 await expect(executeTransaction(263 api, 264 alice, 265 api.tx.unique.setCollectionProperties(collection, [266 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},267 ]), 268 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;269270 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');271272 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();273 expect(properties).to.be.deep.equal([274 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},275 ]);276277 for (let i = 0; i < invalidProperties.length; i++) {278 await expect(executeTransaction(279 api, 280 alice, 281 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 282 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);283 }284 });285 });286});287288// ---------- ACCESS RIGHTS289290describe('Integration Test: Access Rights to Token Properties', () => {291 before(async () => {292 await usingApi(async (api, privateKeyWrapper) => {293 alice = privateKeyWrapper('//Alice');294 bob = privateKeyWrapper('//Bob');295 });296 });297 298 it('Reads access rights to properties of a collection', async () => {299 await usingApi(async api => {300 const collection = await createCollectionExpectSuccess();301 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();302 expect(propertyRights).to.be.empty;303 });304 });305 306 it('Sets access rights to properties of a collection', async () => {307 await usingApi(async api => {308 const collection = await createCollectionExpectSuccess();309310 await expect(executeTransaction(311 api, 312 alice, 313 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 314 )).to.not.be.rejected;315316 await addCollectionAdminExpectSuccess(alice, collection, bob.address);317318 await expect(executeTransaction(319 api, 320 alice, 321 api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 322 )).to.not.be.rejected;323324 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();325 expect(propertyRights).to.be.deep.equal([326 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},327 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},328 ]);329 });330 });331 332 it('Changes access rights to properties of a collection', async () => {333 await usingApi(async api => {334 const collection = await createCollectionExpectSuccess();335336 await expect(executeTransaction(337 api, 338 alice, 339 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 340 )).to.not.be.rejected;341342 await expect(executeTransaction(343 api, 344 alice, 345 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 346 )).to.not.be.rejected;347348 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();349 expect(propertyRights).to.be.deep.equal([350 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},351 ]);352 });353 });354});355356describe('Negative Integration Test: Access Rights to Token Properties', () => {357 before(async () => {358 await usingApi(async (api, privateKeyWrapper) => {359 alice = privateKeyWrapper('//Alice');360 bob = privateKeyWrapper('//Bob');361 });362 });363364 it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {365 await usingApi(async api => {366 const collection = await createCollectionExpectSuccess();367368 await expect(executeTransaction(369 api, 370 bob, 371 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 372 )).to.be.rejectedWith(/common\.NoPermission/);373374 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();375 expect(propertyRights).to.be.empty;376 });377 });378379 it('Prevents from adding too many possible properties', async () => {380 await usingApi(async api => {381 const collection = await createCollectionExpectSuccess();382383 const constitution = [];384 for (let i = 0; i < 65; i++) {385 constitution.push({386 key: 'property_' + i,387 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},388 });389 }390391 await expect(executeTransaction(392 api, 393 alice, 394 api.tx.unique.setPropertyPermissions(collection, constitution), 395 )).to.be.rejectedWith(/common\.PropertyLimitReached/);396397 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();398 expect(propertyRights).to.be.empty;399 });400 });401402 it('Prevents access rights to be modified if constant', async () => {403 await usingApi(async api => {404 const collection = await createCollectionExpectSuccess();405406 await expect(executeTransaction(407 api, 408 alice, 409 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 410 )).to.not.be.rejected;411412 await expect(executeTransaction(413 api, 414 alice, 415 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 416 )).to.be.rejectedWith(/common\.NoPermission/);417418 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();419 expect(propertyRights).to.deep.equal([420 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},421 ]);422 });423 });424425 it('Prevents adding properties with invalid names', async () => {426 await usingApi(async api => {427 const collection = await createCollectionExpectSuccess();428429 const invalidProperties = [430 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],431 [{key: 'G#4', permission: {tokenOwner: true}}],432 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],433 ];434435 for (let i = 0; i < invalidProperties.length; i++) {436 await expect(executeTransaction(437 api, 438 alice, 439 api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 440 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);441 }442443 await expect(executeTransaction(444 api, 445 alice, 446 api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 447 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);448449 const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string450 await expect(executeTransaction(451 api, 452 alice, 453 api.tx.unique.setPropertyPermissions(collection, [454 {key: correctKey, permission: {collectionAdmin: true}},455 ]), 456 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;457458 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');459460 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();461 expect(propertyRights).to.be.deep.equal([462 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},463 ]);464 });465 });466});467468// ---------- TOKEN PROPERTIES469470describe('Integration Test: Token Properties', () => {471 let collection: number;472 let token: number;473 let permissions: {permission: any, signers: IKeyringPair[]}[];474475 before(async () => {476 await usingApi(async (api, privateKeyWrapper) => {477 alice = privateKeyWrapper('//Alice');478 bob = privateKeyWrapper('//Bob');479 charlie = privateKeyWrapper('//Charlie');480481 permissions = [482 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},483 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},484 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},485 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},486 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},487 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},488 ];489 });490 });491492 beforeEach(async () => {493 await usingApi(async () => {494 collection = await createCollectionExpectSuccess();495 token = await createItemExpectSuccess(alice, collection, 'NFT');496 await addCollectionAdminExpectSuccess(alice, collection, bob.address);497 await transferExpectSuccess(collection, token, alice, charlie);498 });499 });500 501 it('Reads yet empty properties of a token', async () => {502 await usingApi(async api => {503 const collection = await createCollectionExpectSuccess();504 const token = await createItemExpectSuccess(alice, collection, 'NFT');505 506 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();507 expect(properties.map).to.be.empty;508 expect(properties.consumedSpace).to.be.equal(0);509510 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;511 expect(tokenData).to.be.empty;512 });513 });514515 it('Assigns properties to a token according to permissions', async () => {516 await usingApi(async api => {517 const propertyKeys: string[] = [];518 let i = 0;519 for (const permission of permissions) {520 for (const signer of permission.signers) {521 const key = i + '_' + signer.address;522 propertyKeys.push(key);523524 await expect(executeTransaction(525 api, 526 alice, 527 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 528 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;529530 await expect(executeTransaction(531 api, 532 signer, 533 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 534 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;535 }536537 i++;538 }539540 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];541 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];542 for (let i = 0; i < properties.length; i++) {543 expect(properties[i].value).to.be.equal('Serotonin increase');544 expect(tokensData[i].value).to.be.equal('Serotonin increase');545 }546 });547 });548549 it('Changes properties of a token according to permissions', async () => {550 await usingApi(async api => {551 const propertyKeys: string[] = [];552 let i = 0;553 for (const permission of permissions) {554 if (!permission.permission.mutable) continue;555 556 for (const signer of permission.signers) {557 const key = i + '_' + signer.address;558 propertyKeys.push(key);559560 await expect(executeTransaction(561 api, 562 alice, 563 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 564 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;565566 await expect(executeTransaction(567 api, 568 signer, 569 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 570 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;571572 await expect(executeTransaction(573 api, 574 signer, 575 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 576 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;577 }578579 i++;580 }581582 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];583 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];584 for (let i = 0; i < properties.length; i++) {585 expect(properties[i].value).to.be.equal('Serotonin stable');586 expect(tokensData[i].value).to.be.equal('Serotonin stable');587 }588 });589 });590591 it('Deletes properties of a token according to permissions', async () => {592 await usingApi(async api => {593 const propertyKeys: string[] = [];594 let i = 0;595596 for (const permission of permissions) {597 if (!permission.permission.mutable) continue;598 599 for (const signer of permission.signers) {600 const key = i + '_' + signer.address;601 propertyKeys.push(key);602603 await expect(executeTransaction(604 api, 605 alice, 606 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 607 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;608609 await expect(executeTransaction(610 api, 611 signer, 612 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 613 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;614615 await expect(executeTransaction(616 api, 617 signer, 618 api.tx.unique.deleteTokenProperties(collection, token, [key]), 619 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;620 }621 622 i++;623 }624625 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];626 expect(properties).to.be.empty;627 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];628 expect(tokensData).to.be.empty;629 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);630 });631 });632});633634describe('Negative Integration Test: Token Properties', () => {635 let collection: number;636 let token: number;637 let originalSpace: number;638 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];639640 before(async () => {641 await usingApi(async (api, privateKeyWrapper) => {642 alice = privateKeyWrapper('//Alice');643 bob = privateKeyWrapper('//Bob');644 charlie = privateKeyWrapper('//Charlie');645 const dave = privateKeyWrapper('//Dave');646647 constitution = [648 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},649 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},650 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},651 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},652 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},653 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},654 ];655 });656 });657658 beforeEach(async () => {659 collection = await createCollectionExpectSuccess();660 token = await createItemExpectSuccess(alice, collection, 'NFT');661 await addCollectionAdminExpectSuccess(alice, collection, bob.address);662 await transferExpectSuccess(collection, token, alice, charlie);663 664 await usingApi(async api => {665 let i = 0;666 for (const passage of constitution) {667 const signer = passage.signers[0];668 669 await expect(executeTransaction(670 api, 671 alice, 672 api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 673 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;674675 await expect(executeTransaction(676 api, 677 signer, 678 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 679 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;680681 i++;682 }683684 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;685 });686 });687688 it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {689 await usingApi(async api => {690 let i = -1;691 for (const forbiddance of constitution) {692 i++;693 if (!forbiddance.permission.mutable) continue;694695 await expect(executeTransaction(696 api, 697 forbiddance.sinner, 698 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 699 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);700701 await expect(executeTransaction(702 api, 703 forbiddance.sinner, 704 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 705 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);706 }707708 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();709 expect(properties.consumedSpace).to.be.equal(originalSpace);710 });711 });712713 it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {714 await usingApi(async api => {715 let i = -1;716 for (const permission of constitution) {717 i++;718 if (permission.permission.mutable) continue;719720 await expect(executeTransaction(721 api, 722 permission.signers[0], 723 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 724 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);725726 await expect(executeTransaction(727 api, 728 permission.signers[0], 729 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 730 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);731 }732733 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();734 expect(properties.consumedSpace).to.be.equal(originalSpace);735 });736 });737738 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {739 await usingApi(async api => {740 await expect(executeTransaction(741 api, 742 alice, 743 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 744 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);745 746 await expect(executeTransaction(747 api, 748 alice, 749 api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 750 ), 'on setting a new non-permitted property').to.not.be.rejected;751752 await expect(executeTransaction(753 api, 754 alice, 755 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 756 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);757758 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;759 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();760 expect(properties.consumedSpace).to.be.equal(originalSpace);761 });762 });763764 it('Forbids adding too many properties to a token', async () => {765 await usingApi(async api => {766 await expect(executeTransaction(767 api, 768 alice, 769 api.tx.unique.setPropertyPermissions(collection, [770 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 771 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},772 ]), 773 ), 'on setting a new non-permitted property').to.not.be.rejected;774775 // Mute the general tx parsing error776 {777 console.error = () => {};778 await expect(executeTransaction(779 api, 780 alice, 781 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 782 )).to.be.rejected;783 }784785 await expect(executeTransaction(786 api, 787 alice, 788 api.tx.unique.setTokenProperties(collection, token, [789 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 790 {key: 'young_years', value: 'neverending'.repeat(1490)},791 ]), 792 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);793794 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;795 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();796 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);797 });798 });799});1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4 addCollectionAdminExpectSuccess,5 createCollectionExpectSuccess,6 createItemExpectSuccess,7 getCreateCollectionResult,8 transferExpectSuccess,9} from '../util/helpers';10import {IKeyringPair} from '@polkadot/types/types';1112let alice: IKeyringPair;13let bob: IKeyringPair;14let charlie: IKeyringPair;1516describe('Composite Properties Test', () => {17 before(async () => {18 await usingApi(async (api, privateKeyWrapper) => {19 alice = privateKeyWrapper('//Alice');20 bob = privateKeyWrapper('//Bob');21 });22 });2324 it('Makes sure collectionById supplies required fields', async () => {25 await usingApi(async api => {26 const collectionId = await createCollectionExpectSuccess();2728 const collectionOption = await api.rpc.unique.collectionById(collectionId);29 expect(collectionOption.isSome).to.be.true;30 let collection = collectionOption.unwrap();31 expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;32 expect(collection.properties.toHuman()).to.be.empty;3334 const propertyPermissions = [35 {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},36 {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},37 ];38 await expect(executeTransaction(39 api, 40 alice, 41 api.tx.unique.setPropertyPermissions(collectionId, propertyPermissions), 42 )).to.not.be.rejected;4344 const collectionProperties = [45 {key: 'black_hole', value: 'LIGO'},46 {key: 'electron', value: 'come bond'}, 47 ];48 await expect(executeTransaction(49 api, 50 alice, 51 api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 52 )).to.not.be.rejected;5354 collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();55 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);56 expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);57 });58 });59});6061// ---------- COLLECTION PROPERTIES6263describe('Integration Test: Collection Properties', () => {64 before(async () => {65 await usingApi(async (api, privateKeyWrapper) => {66 alice = privateKeyWrapper('//Alice');67 bob = privateKeyWrapper('//Bob');68 });69 });7071 it('Reads properties from a collection', async () => {72 await usingApi(async api => {73 const collection = await createCollectionExpectSuccess();74 const properties = (await api.query.common.collectionProperties(collection)).toJSON();75 expect(properties.map).to.be.empty;76 expect(properties.consumedSpace).to.equal(0);77 });78 });7980 it('Sets properties for a collection', async () => {81 await usingApi(async api => {82 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));83 const {collectionId} = getCreateCollectionResult(events);8485 // As owner86 await expect(executeTransaction(87 api, 88 bob, 89 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 90 )).to.not.be.rejected;9192 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);9394 // As administrator95 await expect(executeTransaction(96 api, 97 alice, 98 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 99 )).to.not.be.rejected;100101 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();102 expect(properties).to.be.deep.equal([103 {key: 'electron', value: 'come bond'},104 {key: 'black_hole', value: ''},105 ]);106 });107 });108109 it('Check valid names for collection properties keys', async () => {110 await usingApi(async api => {111 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));112 const {collectionId} = getCreateCollectionResult(events);113114 // alpha symbols115 await expect(executeTransaction(116 api, 117 bob, 118 api.tx.unique.setCollectionProperties(collectionId, [{key: 'alpha'}]), 119 )).to.not.be.rejected;120121 // numeric symbols122 await expect(executeTransaction(123 api, 124 bob, 125 api.tx.unique.setCollectionProperties(collectionId, [{key: '123'}]), 126 )).to.not.be.rejected;127128 // underscore symbol129 await expect(executeTransaction(130 api, 131 bob, 132 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 133 )).to.not.be.rejected;134135 // dash symbol136 await expect(executeTransaction(137 api, 138 bob, 139 api.tx.unique.setCollectionProperties(collectionId, [{key: 'semi-automatic'}]), 140 )).to.not.be.rejected;141142 // underscore symbol143 await expect(executeTransaction(144 api, 145 bob, 146 api.tx.unique.setCollectionProperties(collectionId, [{key: 'build.rs'}]), 147 )).to.not.be.rejected;148149 const propertyKeys = ['alpha', '123', 'black_hole', 'semi-automatic', 'build.rs'];150 const properties = (await api.rpc.unique.collectionProperties(collectionId, propertyKeys)).toHuman();151 expect(properties).to.be.deep.equal([152 {key: 'alpha', value: ''},153 {key: '123', value: ''},154 {key: 'black_hole', value: ''},155 {key: 'semi-automatic', value: ''},156 {key: 'build.rs', value: ''},157 ]);158 });159 });160161 it('Changes properties of a collection', async () => {162 await usingApi(async api => {163 const collection = await createCollectionExpectSuccess();164165 await expect(executeTransaction(166 api, 167 alice, 168 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 169 )).to.not.be.rejected;170171 // Mutate the properties172 await expect(executeTransaction(173 api, 174 alice, 175 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 176 )).to.not.be.rejected;177178 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();179 expect(properties).to.be.deep.equal([180 {key: 'electron', value: 'bonded'},181 {key: 'black_hole', value: 'LIGO'},182 ]);183 });184 });185186 it('Deletes properties of a collection', async () => {187 await usingApi(async api => {188 const collection = await createCollectionExpectSuccess();189190 await expect(executeTransaction(191 api, 192 alice, 193 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 194 )).to.not.be.rejected;195196 await expect(executeTransaction(197 api, 198 alice, 199 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 200 )).to.not.be.rejected;201202 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();203 expect(properties).to.be.deep.equal([204 {key: 'black_hole', value: 'LIGO'},205 ]);206 });207 });208});209210describe('Negative Integration Test: Collection Properties', () => {211 before(async () => {212 await usingApi(async (api, privateKeyWrapper) => {213 alice = privateKeyWrapper('//Alice');214 bob = privateKeyWrapper('//Bob');215 });216 });217 218 it('Fails to set properties in a collection if not its onwer/administrator', async () => {219 await usingApi(async api => {220 const collection = await createCollectionExpectSuccess();221222 await expect(executeTransaction(223 api, 224 bob, 225 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 226 )).to.be.rejectedWith(/common\.NoPermission/);227 228 const properties = (await api.query.common.collectionProperties(collection)).toJSON();229 expect(properties.map).to.be.empty;230 expect(properties.consumedSpace).to.equal(0);231 });232 });233 234 it('Fails to set properties that exceed the limits', async () => {235 await usingApi(async api => {236 const collection = await createCollectionExpectSuccess();237 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 238239 // Mute the general tx parsing error, too many bytes to process240 {241 console.error = () => {};242 await expect(executeTransaction(243 api, 244 alice, 245 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 246 )).to.be.rejected;247 }248249 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();250 expect(properties).to.be.empty;251252 await expect(executeTransaction(253 api, 254 alice, 255 api.tx.unique.setCollectionProperties(collection, [256 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 257 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 258 ]), 259 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);260261 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();262 expect(properties).to.be.empty;263 });264 });265 266 it('Fails to set more properties than it is allowed', async () => {267 await usingApi(async api => {268 const collection = await createCollectionExpectSuccess();269270 const propertiesToBeSet = [];271 for (let i = 0; i < 65; i++) {272 propertiesToBeSet.push({273 key: 'electron_' + i,274 value: Math.random() > 0.5 ? 'high' : 'low',275 });276 }277278 await expect(executeTransaction(279 api, 280 alice, 281 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 282 )).to.be.rejectedWith(/common\.PropertyLimitReached/);283284 const properties = (await api.query.common.collectionProperties(collection)).toJSON();285 expect(properties.map).to.be.empty;286 expect(properties.consumedSpace).to.equal(0);287 });288 });289290 it('Fails to set properties with invalid names', async () => {291 await usingApi(async api => {292 const collection = await createCollectionExpectSuccess();293294 const invalidProperties = [295 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],296 [{key: 'Mr/Sandman', value: 'Bring me a gene'}],297 [{key: 'déjà vu', value: 'hmm...'}],298 ];299300 for (let i = 0; i < invalidProperties.length; i++) {301 await expect(executeTransaction(302 api, 303 alice, 304 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 305 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);306 }307308 await expect(executeTransaction(309 api, 310 alice, 311 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 312 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);313314 await expect(executeTransaction(315 api, 316 alice, 317 api.tx.unique.setCollectionProperties(collection, [318 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},319 ]), 320 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;321322 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');323324 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();325 expect(properties).to.be.deep.equal([326 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},327 ]);328329 for (let i = 0; i < invalidProperties.length; i++) {330 await expect(executeTransaction(331 api, 332 alice, 333 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 334 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);335 }336 });337 });338});339340// ---------- ACCESS RIGHTS341342describe('Integration Test: Access Rights to Token Properties', () => {343 before(async () => {344 await usingApi(async (api, privateKeyWrapper) => {345 alice = privateKeyWrapper('//Alice');346 bob = privateKeyWrapper('//Bob');347 });348 });349 350 it('Reads access rights to properties of a collection', async () => {351 await usingApi(async api => {352 const collection = await createCollectionExpectSuccess();353 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();354 expect(propertyRights).to.be.empty;355 });356 });357 358 it('Sets access rights to properties of a collection', async () => {359 await usingApi(async api => {360 const collection = await createCollectionExpectSuccess();361362 await expect(executeTransaction(363 api, 364 alice, 365 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 366 )).to.not.be.rejected;367368 await addCollectionAdminExpectSuccess(alice, collection, bob.address);369370 await expect(executeTransaction(371 api, 372 alice, 373 api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 374 )).to.not.be.rejected;375376 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();377 expect(propertyRights).to.be.deep.equal([378 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},379 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},380 ]);381 });382 });383 384 it('Changes access rights to properties of a collection', async () => {385 await usingApi(async api => {386 const collection = await createCollectionExpectSuccess();387388 await expect(executeTransaction(389 api, 390 alice, 391 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 392 )).to.not.be.rejected;393394 await expect(executeTransaction(395 api, 396 alice, 397 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 398 )).to.not.be.rejected;399400 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();401 expect(propertyRights).to.be.deep.equal([402 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},403 ]);404 });405 });406});407408describe('Negative Integration Test: Access Rights to Token Properties', () => {409 before(async () => {410 await usingApi(async (api, privateKeyWrapper) => {411 alice = privateKeyWrapper('//Alice');412 bob = privateKeyWrapper('//Bob');413 });414 });415416 it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {417 await usingApi(async api => {418 const collection = await createCollectionExpectSuccess();419420 await expect(executeTransaction(421 api, 422 bob, 423 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 424 )).to.be.rejectedWith(/common\.NoPermission/);425426 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();427 expect(propertyRights).to.be.empty;428 });429 });430431 it('Prevents from adding too many possible properties', async () => {432 await usingApi(async api => {433 const collection = await createCollectionExpectSuccess();434435 const constitution = [];436 for (let i = 0; i < 65; i++) {437 constitution.push({438 key: 'property_' + i,439 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},440 });441 }442443 await expect(executeTransaction(444 api, 445 alice, 446 api.tx.unique.setPropertyPermissions(collection, constitution), 447 )).to.be.rejectedWith(/common\.PropertyLimitReached/);448449 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();450 expect(propertyRights).to.be.empty;451 });452 });453454 it('Prevents access rights to be modified if constant', async () => {455 await usingApi(async api => {456 const collection = await createCollectionExpectSuccess();457458 await expect(executeTransaction(459 api, 460 alice, 461 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 462 )).to.not.be.rejected;463464 await expect(executeTransaction(465 api, 466 alice, 467 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 468 )).to.be.rejectedWith(/common\.NoPermission/);469470 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();471 expect(propertyRights).to.deep.equal([472 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},473 ]);474 });475 });476477 it('Prevents adding properties with invalid names', async () => {478 await usingApi(async api => {479 const collection = await createCollectionExpectSuccess();480481 const invalidProperties = [482 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],483 [{key: 'G#4', permission: {tokenOwner: true}}],484 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],485 ];486487 for (let i = 0; i < invalidProperties.length; i++) {488 await expect(executeTransaction(489 api, 490 alice, 491 api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 492 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);493 }494495 await expect(executeTransaction(496 api, 497 alice, 498 api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 499 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);500501 const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string502 await expect(executeTransaction(503 api, 504 alice, 505 api.tx.unique.setPropertyPermissions(collection, [506 {key: correctKey, permission: {collectionAdmin: true}},507 ]), 508 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;509510 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');511512 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();513 expect(propertyRights).to.be.deep.equal([514 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},515 ]);516 });517 });518});519520// ---------- TOKEN PROPERTIES521522describe('Integration Test: Token Properties', () => {523 let collection: number;524 let token: number;525 let permissions: {permission: any, signers: IKeyringPair[]}[];526527 before(async () => {528 await usingApi(async (api, privateKeyWrapper) => {529 alice = privateKeyWrapper('//Alice');530 bob = privateKeyWrapper('//Bob');531 charlie = privateKeyWrapper('//Charlie');532533 permissions = [534 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},535 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},536 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},537 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},538 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},539 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},540 ];541 });542 });543544 beforeEach(async () => {545 await usingApi(async () => {546 collection = await createCollectionExpectSuccess();547 token = await createItemExpectSuccess(alice, collection, 'NFT');548 await addCollectionAdminExpectSuccess(alice, collection, bob.address);549 await transferExpectSuccess(collection, token, alice, charlie);550 });551 });552 553 it('Reads yet empty properties of a token', async () => {554 await usingApi(async api => {555 const collection = await createCollectionExpectSuccess();556 const token = await createItemExpectSuccess(alice, collection, 'NFT');557 558 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();559 expect(properties.map).to.be.empty;560 expect(properties.consumedSpace).to.be.equal(0);561562 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;563 expect(tokenData).to.be.empty;564 });565 });566567 it('Assigns properties to a token according to permissions', async () => {568 await usingApi(async api => {569 const propertyKeys: string[] = [];570 let i = 0;571 for (const permission of permissions) {572 for (const signer of permission.signers) {573 const key = i + '_' + signer.address;574 propertyKeys.push(key);575576 await expect(executeTransaction(577 api, 578 alice, 579 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 580 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;581582 await expect(executeTransaction(583 api, 584 signer, 585 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 586 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;587 }588589 i++;590 }591592 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];593 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];594 for (let i = 0; i < properties.length; i++) {595 expect(properties[i].value).to.be.equal('Serotonin increase');596 expect(tokensData[i].value).to.be.equal('Serotonin increase');597 }598 });599 });600601 it('Changes properties of a token according to permissions', async () => {602 await usingApi(async api => {603 const propertyKeys: string[] = [];604 let i = 0;605 for (const permission of permissions) {606 if (!permission.permission.mutable) continue;607 608 for (const signer of permission.signers) {609 const key = i + '_' + signer.address;610 propertyKeys.push(key);611612 await expect(executeTransaction(613 api, 614 alice, 615 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 616 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;617618 await expect(executeTransaction(619 api, 620 signer, 621 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 622 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;623624 await expect(executeTransaction(625 api, 626 signer, 627 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 628 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;629 }630631 i++;632 }633634 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];635 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];636 for (let i = 0; i < properties.length; i++) {637 expect(properties[i].value).to.be.equal('Serotonin stable');638 expect(tokensData[i].value).to.be.equal('Serotonin stable');639 }640 });641 });642643 it('Deletes properties of a token according to permissions', async () => {644 await usingApi(async api => {645 const propertyKeys: string[] = [];646 let i = 0;647648 for (const permission of permissions) {649 if (!permission.permission.mutable) continue;650 651 for (const signer of permission.signers) {652 const key = i + '_' + signer.address;653 propertyKeys.push(key);654655 await expect(executeTransaction(656 api, 657 alice, 658 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 659 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;660661 await expect(executeTransaction(662 api, 663 signer, 664 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 665 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;666667 await expect(executeTransaction(668 api, 669 signer, 670 api.tx.unique.deleteTokenProperties(collection, token, [key]), 671 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;672 }673 674 i++;675 }676677 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];678 expect(properties).to.be.empty;679 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];680 expect(tokensData).to.be.empty;681 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);682 });683 });684});685686describe('Negative Integration Test: Token Properties', () => {687 let collection: number;688 let token: number;689 let originalSpace: number;690 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];691692 before(async () => {693 await usingApi(async (api, privateKeyWrapper) => {694 alice = privateKeyWrapper('//Alice');695 bob = privateKeyWrapper('//Bob');696 charlie = privateKeyWrapper('//Charlie');697 const dave = privateKeyWrapper('//Dave');698699 constitution = [700 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},701 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},702 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},703 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},704 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},705 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},706 ];707 });708 });709710 beforeEach(async () => {711 collection = await createCollectionExpectSuccess();712 token = await createItemExpectSuccess(alice, collection, 'NFT');713 await addCollectionAdminExpectSuccess(alice, collection, bob.address);714 await transferExpectSuccess(collection, token, alice, charlie);715 716 await usingApi(async api => {717 let i = 0;718 for (const passage of constitution) {719 const signer = passage.signers[0];720 721 await expect(executeTransaction(722 api, 723 alice, 724 api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 725 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;726727 await expect(executeTransaction(728 api, 729 signer, 730 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 731 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;732733 i++;734 }735736 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;737 });738 });739740 it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {741 await usingApi(async api => {742 let i = -1;743 for (const forbiddance of constitution) {744 i++;745 if (!forbiddance.permission.mutable) continue;746747 await expect(executeTransaction(748 api, 749 forbiddance.sinner, 750 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 751 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);752753 await expect(executeTransaction(754 api, 755 forbiddance.sinner, 756 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 757 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);758 }759760 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();761 expect(properties.consumedSpace).to.be.equal(originalSpace);762 });763 });764765 it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {766 await usingApi(async api => {767 let i = -1;768 for (const permission of constitution) {769 i++;770 if (permission.permission.mutable) continue;771772 await expect(executeTransaction(773 api, 774 permission.signers[0], 775 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 776 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);777778 await expect(executeTransaction(779 api, 780 permission.signers[0], 781 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 782 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);783 }784785 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();786 expect(properties.consumedSpace).to.be.equal(originalSpace);787 });788 });789790 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {791 await usingApi(async api => {792 await expect(executeTransaction(793 api, 794 alice, 795 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 796 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);797 798 await expect(executeTransaction(799 api, 800 alice, 801 api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 802 ), 'on setting a new non-permitted property').to.not.be.rejected;803804 await expect(executeTransaction(805 api, 806 alice, 807 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 808 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);809810 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;811 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();812 expect(properties.consumedSpace).to.be.equal(originalSpace);813 });814 });815816 it('Forbids adding too many properties to a token', async () => {817 await usingApi(async api => {818 await expect(executeTransaction(819 api, 820 alice, 821 api.tx.unique.setPropertyPermissions(collection, [822 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 823 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},824 ]), 825 ), 'on setting a new non-permitted property').to.not.be.rejected;826827 // Mute the general tx parsing error828 {829 console.error = () => {};830 await expect(executeTransaction(831 api, 832 alice, 833 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 834 )).to.be.rejected;835 }836837 await expect(executeTransaction(838 api, 839 alice, 840 api.tx.unique.setTokenProperties(collection, token, [841 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 842 {key: 'young_years', value: 'neverending'.repeat(1490)},843 ]), 844 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);845846 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;847 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();848 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);849 });850 });851});tests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth--- 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));
tests/src/setCollectionLimits.test.tsdiffbeforeafterboth--- 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,
tests/src/setCollectionSponsor.test.tsdiffbeforeafterboth--- 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');
});
});
tests/src/setMintPermission.test.tsdiffbeforeafterboth--- 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 () => {
tests/src/setPublicAccessMode.test.tsdiffbeforeafterboth--- 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');
});
});
});