difftreelog
feat add conditional supportInterface for ERC721Metadata
in: master
29 files changed
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -684,6 +684,11 @@
pub fn parent_nft() -> up_data_structs::PropertyKey {
property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)
}
+
+ /// Key "parentNft".
+ pub fn erc721_metadata() -> up_data_structs::PropertyKey {
+ property_key_from_bytes(b"ERC721Metadata").expect(EXPECT_CONVERT_ERROR)
+ }
}
/// Values.
@@ -693,10 +698,21 @@
/// Value "ERC721Metadata".
pub const ERC721_METADATA: &[u8] = b"ERC721Metadata";
+ /// Value "1" ERC721 metadata supported.
+ pub const ERC721_METADATA_SUPPORTED: &[u8] = b"1";
+
+ /// Value "0" ERC721 metadata supported.
+ pub const ERC721_METADATA_UNSUPPORTED: &[u8] = b"0";
+
/// Value for [`ERC721_METADATA`].
pub fn erc721() -> up_data_structs::PropertyValue {
property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)
}
+
+ /// Value for [`ERC721_METADATA`].
+ pub fn erc721_metadata_supported() -> up_data_structs::PropertyValue {
+ property_value_from_bytes(ERC721_METADATA_SUPPORTED).expect(EXPECT_CONVERT_ERROR)
+ }
}
/// Convert `byte` to [`PropertyKey`].
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -232,7 +232,7 @@
if !url.is_empty() {
return Ok(url);
}
- } else if !is_erc721_metadata_compatible::<T>(self.id) {
+ } else if !self.supports_metadata() {
return Err("tokenURI not set".into());
}
@@ -548,17 +548,6 @@
}
Err("Property tokenURI not found".into())
-}
-
-fn is_erc721_metadata_compatible<T: Config>(collection_id: CollectionId) -> bool {
- if let Some(shema_name) =
- pallet_common::Pallet::<T>::get_collection_property(collection_id, &key::schema_name())
- {
- let shema_name = shema_name.into_inner();
- shema_name == property_value::ERC721_METADATA
- } else {
- false
- }
}
fn get_token_permission<T: Config>(
@@ -577,16 +566,6 @@
Ok(a)
}
-fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {
- if let Ok(token_property_permissions) =
- CollectionPropertyPermissions::<T>::try_get(collection_id)
- {
- return token_property_permissions.contains_key(key);
- }
-
- false
-}
-
/// @title Unique extensions for ERC721.
#[solidity_interface(name = ERC721UniqueExtensions)]
impl<T: Config> NonfungibleHandle<T> {
@@ -731,7 +710,7 @@
name = UniqueNFT,
is(
ERC721,
- ERC721Metadata,
+ ERC721Metadata(if(this.supports_metadata())),
ERC721Enumerable,
ERC721UniqueExtensions,
ERC721Mintable,
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -108,6 +108,7 @@
use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
use pallet_common::{
Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,
+ erc::static_property::{key, value},
eth::collection_id_to_address,
};
use pallet_structure::{Pallet as PalletStructure, Error as StructureError};
@@ -295,6 +296,19 @@
&mut self.0
}
}
+
+impl<T: Config> NonfungibleHandle<T> {
+ pub fn supports_metadata(&self) -> bool {
+ if let Some(erc721_metadata) =
+ pallet_common::Pallet::<T>::get_collection_property(self.id, &key::erc721_metadata())
+ {
+ *erc721_metadata.into_inner() == *value::ERC721_METADATA_SUPPORTED
+ } else {
+ false
+ }
+ }
+}
+
impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {
fn recorder(&self) -> &SubstrateRecorder<T> {
self.0.recorder()
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -228,7 +228,7 @@
if !url.is_empty() {
return Ok(url);
}
- } else if !is_erc721_metadata_compatible::<T>(self.id) {
+ } else if !self.supports_metadata() {
return Err("tokenURI not set".into());
}
@@ -578,17 +578,6 @@
Err("Property tokenURI not found".into())
}
-fn is_erc721_metadata_compatible<T: Config>(collection_id: CollectionId) -> bool {
- if let Some(shema_name) =
- pallet_common::Pallet::<T>::get_collection_property(collection_id, &key::schema_name())
- {
- let shema_name = shema_name.into_inner();
- shema_name == property_value::ERC721_METADATA
- } else {
- false
- }
-}
-
fn get_token_permission<T: Config>(
collection_id: CollectionId,
key: &PropertyKey,
@@ -780,7 +769,7 @@
name = UniqueRefungible,
is(
ERC721,
- ERC721Metadata,
+ ERC721Metadata(if(this.supports_metadata())),
ERC721Enumerable,
ERC721UniqueExtensions,
ERC721Mintable,
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -92,14 +92,19 @@
use codec::{Encode, Decode, MaxEncodedLen};
use core::ops::Deref;
+use derivative::Derivative;
use evm_coder::ToLog;
use frame_support::{
- BoundedVec, ensure, fail, storage::with_transaction, transactional, pallet_prelude::ConstU32,
+ BoundedBTreeMap, BoundedVec, ensure, fail, storage::with_transaction, transactional,
+ pallet_prelude::ConstU32,
};
use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
use pallet_evm_coder_substrate::WithRecorder;
use pallet_common::{
- CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,
+ CommonCollectionOperations,
+ erc::static_property::{key, value},
+ Error as CommonError,
+ eth::collection_id_to_address,
Event as CommonEvent, Pallet as PalletCommon,
};
use pallet_structure::Pallet as PalletStructure;
@@ -113,8 +118,6 @@
MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,
PropertyScope, PropertyValue, TokenId, TrySetProperty,
};
-use frame_support::BoundedBTreeMap;
-use derivative::Derivative;
pub use pallet::*;
#[cfg(feature = "runtime-benchmarks")]
@@ -301,6 +304,18 @@
}
}
+impl<T: Config> RefungibleHandle<T> {
+ pub fn supports_metadata(&self) -> bool {
+ if let Some(erc721_metadata) =
+ pallet_common::Pallet::<T>::get_collection_property(self.id, &key::erc721_metadata())
+ {
+ *erc721_metadata.into_inner() == *value::ERC721_METADATA_SUPPORTED
+ } else {
+ false
+ }
+ }
+}
+
impl<T: Config> Deref for RefungibleHandle<T> {
type Target = pallet_common::CollectionHandle<T>;
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -130,6 +130,13 @@
})
.map_err(|e| Error::Revert(format!("{:?}", e)))?;
+ properties
+ .try_push(up_data_structs::Property {
+ key: key::erc721_metadata(),
+ value: property_value::erc721_metadata_supported(),
+ })
+ .map_err(|e| Error::Revert(format!("{:?}", e)))?;
+
if !base_uri_value.is_empty() {
properties
.try_push(up_data_structs::Property {
@@ -212,7 +219,8 @@
/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
/// @return address Address of the newly created collection
#[weight(<SelfWeightOf<T>>::create_collection())]
- fn create_nonfungible_collection(
+ #[solidity(rename_selector = "createNFTCollection")]
+ fn create_nft_collection(
&mut self,
caller: caller,
value: value,
@@ -239,9 +247,26 @@
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
}
+ /// Create an NFT collection
+ /// @param name Name of the collection
+ /// @param description Informative description of the collection
+ /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
+ /// @return address Address of the newly created collection
+ #[weight(<SelfWeightOf<T>>::create_collection())]
+ #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]
+ fn create_nonfungible_collection(
+ &mut self,
+ caller: caller,
+ value: value,
+ name: string,
+ description: string,
+ token_prefix: string,
+ ) -> Result<address> {
+ self.create_nft_collection(caller, value, name, description, token_prefix)
+ }
#[weight(<SelfWeightOf<T>>::create_collection())]
- #[solidity(rename_selector = "createERC721MetadataCompatibleCollection")]
+ #[solidity(rename_selector = "createERC721MetadataNFTCollection")]
fn create_nonfungible_collection_with_properties(
&mut self,
caller: caller,
@@ -273,6 +298,27 @@
#[weight(<SelfWeightOf<T>>::create_collection())]
#[solidity(rename_selector = "createRFTCollection")]
+ fn create_rft_collection(
+ &mut self,
+ caller: caller,
+ value: value,
+ name: string,
+ description: string,
+ token_prefix: string,
+ ) -> Result<address> {
+ create_refungible_collection_internal::<T>(
+ caller,
+ value,
+ name,
+ description,
+ token_prefix,
+ Default::default(),
+ false,
+ )
+ }
+
+ #[weight(<SelfWeightOf<T>>::create_collection())]
+ #[deprecated(note = "mathod was renamed to `create_rft_collection`, prefer it instead")]
fn create_refungible_collection(
&mut self,
caller: caller,
@@ -293,7 +339,7 @@
}
#[weight(<SelfWeightOf<T>>::create_collection())]
- #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]
+ #[solidity(rename_selector = "createERC721MetadataRFTCollection")]
fn create_refungible_collection_with_properties(
&mut self,
caller: caller,
pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -23,13 +23,33 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x5ad4f440
+/// @dev the ERC-165 identifier for this interface is 0xf62c7aa9
contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
/// @param description Informative description of the collection
/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
/// @return address Address of the newly created collection
+ /// @dev EVM selector for this function is: 0x844af658,
+ /// or in textual repr: createNFTCollection(string,string,string)
+ function createNFTCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix
+ ) public payable returns (address) {
+ require(false, stub_error);
+ name;
+ description;
+ tokenPrefix;
+ dummy = 0;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
+ /// Create an NFT collection
+ /// @param name Name of the collection
+ /// @param description Informative description of the collection
+ /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
+ /// @return address Address of the newly created collection
/// @dev EVM selector for this function is: 0xe34a6844,
/// or in textual repr: createNonfungibleCollection(string,string,string)
function createNonfungibleCollection(
@@ -45,9 +65,9 @@
return 0x0000000000000000000000000000000000000000;
}
- /// @dev EVM selector for this function is: 0xa634a5f9,
- /// or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)
- function createERC721MetadataCompatibleCollection(
+ /// @dev EVM selector for this function is: 0xd1df968c,
+ /// or in textual repr: createERC721MetadataNFTCollection(string,string,string,string)
+ function createERC721MetadataNFTCollection(
string memory name,
string memory description,
string memory tokenPrefix,
@@ -77,9 +97,24 @@
return 0x0000000000000000000000000000000000000000;
}
- /// @dev EVM selector for this function is: 0xa5596388,
- /// or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)
- function createERC721MetadataCompatibleRFTCollection(
+ /// @dev EVM selector for this function is: 0x44a68ad5,
+ /// or in textual repr: createRefungibleCollection(string,string,string)
+ function createRefungibleCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix
+ ) public payable returns (address) {
+ require(false, stub_error);
+ name;
+ description;
+ tokenPrefix;
+ dummy = 0;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
+ /// @dev EVM selector for this function is: 0xbea6a299,
+ /// or in textual repr: createERC721MetadataRFTCollection(string,string,string,string)
+ function createERC721MetadataRFTCollection(
string memory name,
string memory description,
string memory tokenPrefix,
tests/src/deprecated-helpers/eth/helpers.tsdiffbeforeafterboth--- a/tests/src/deprecated-helpers/eth/helpers.ts
+++ b/tests/src/deprecated-helpers/eth/helpers.ts
@@ -150,10 +150,10 @@
}
-export async function createNonfungibleCollection(api: ApiPromise, web3: Web3, owner: string) {
+export async function createNFTCollection(api: ApiPromise, web3: Web3, owner: string) {
const collectionHelper = evmCollectionHelpers(web3, owner);
const result = await collectionHelper.methods
- .createNonfungibleCollection('A', 'B', 'C')
+ .createNFTCollection('A', 'B', 'C')
.send({value: Number(2n * UNIQUE)});
return await getCollectionAddressFromResult(api, result);
}
tests/src/deprecated-helpers/helpers.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import '../interfaces/augment-api-rpc';18import '../interfaces/augment-api-query';19import {ApiPromise, Keyring} from '@polkadot/api';20import type {AccountId, EventRecord, Event, BlockNumber} from '@polkadot/types/interfaces';21import type {GenericEventData} from '@polkadot/types';22import {AnyTuple, IEvent, IKeyringPair} from '@polkadot/types/types';23import {evmToAddress} from '@polkadot/util-crypto';24import {AnyNumber} from '@polkadot/types-codec/types';25import BN from 'bn.js';26import chai from 'chai';27import chaiAsPromised from 'chai-as-promised';28import {default as usingApi, executeTransaction, submitTransactionAsync, submitTransactionExpectFailAsync} from '../substrate/substrate-api';29import {hexToStr, strToUTF16, utf16ToStr} from './util';30import {UpDataStructsRpcCollection, UpDataStructsCreateItemData, UpDataStructsProperty} from '@polkadot/types/lookup';31import {UpDataStructsTokenChild} from '../interfaces';32import {Context} from 'mocha';3334chai.use(chaiAsPromised);35const expect = chai.expect;3637export type CrossAccountId = {38 Substrate: string,39} | {40 Ethereum: string,41};424344export enum Pallets {45 Inflation = 'inflation',46 RmrkCore = 'rmrkcore',47 RmrkEquip = 'rmrkequip',48 ReFungible = 'refungible',49 Fungible = 'fungible',50 NFT = 'nonfungible',51 Scheduler = 'scheduler',52 AppPromotion = 'apppromotion',53}5455export async function isUnique(): Promise<boolean> {56 return usingApi(async api => {57 const chain = await api.rpc.system.chain();5859 return chain.eq('UNIQUE');60 });61}6263export async function isQuartz(): Promise<boolean> {64 return usingApi(async api => {65 const chain = await api.rpc.system.chain();6667 return chain.eq('QUARTZ');68 });69}7071let modulesNames: any;72export function getModuleNames(api: ApiPromise): string[] {73 if (typeof modulesNames === 'undefined')74 modulesNames = api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());75 return modulesNames;76}7778export async function missingRequiredPallets(requiredPallets: string[]): Promise<string[]> {79 return await usingApi(async api => {80 const pallets = getModuleNames(api);8182 return requiredPallets.filter(p => !pallets.includes(p));83 });84}8586export async function checkPalletsPresence(requiredPallets: string[]): Promise<boolean> {87 return (await missingRequiredPallets(requiredPallets)).length == 0;88}8990export async function requirePallets(mocha: Context, requiredPallets: string[]) {91 const missingPallets = await missingRequiredPallets(requiredPallets);9293 if (missingPallets.length > 0) {94 const skippingTestMsg = `\tSkipping test "${mocha.test?.title}".`;95 const missingPalletsMsg = `\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;96 const skipMsg = `${skippingTestMsg}\n${missingPalletsMsg}`;9798 console.error('\x1b[38:5:208m%s\x1b[0m', skipMsg);99100 mocha.skip();101 }102}103104export function bigIntToSub(api: ApiPromise, number: bigint) {105 return api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();106}107108export function bigIntToDecimals(number: bigint, decimals = 18): string {109 const numberStr = number.toString();110 const dotPos = numberStr.length - decimals;111112 if (dotPos <= 0) {113 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;114 } else {115 const intPart = numberStr.substring(0, dotPos);116 const fractPart = numberStr.substring(dotPos);117 return intPart + '.' + fractPart;118 }119}120121export function normalizeAccountId(input: string | AccountId | CrossAccountId | IKeyringPair): CrossAccountId {122 if (typeof input === 'string') {123 if (input.length >= 47) {124 return {Substrate: input};125 } else if (input.length === 42 && input.startsWith('0x')) {126 return {Ethereum: input.toLowerCase()};127 } else if (input.length === 40 && !input.startsWith('0x')) {128 return {Ethereum: '0x' + input.toLowerCase()};129 } else {130 throw new Error(`Unknown address format: "${input}"`);131 }132 }133 if ('address' in input) {134 return {Substrate: input.address};135 }136 if ('Ethereum' in input) {137 return {138 Ethereum: input.Ethereum.toLowerCase(),139 };140 } else if ('ethereum' in input) {141 return {142 Ethereum: (input as any).ethereum.toLowerCase(),143 };144 } else if ('Substrate' in input) {145 return input;146 } else if ('substrate' in input) {147 return {148 Substrate: (input as any).substrate,149 };150 }151152 // AccountId153 return {Substrate: input.toString()};154}155export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {156 input = normalizeAccountId(input);157 if ('Substrate' in input) {158 return input.Substrate;159 } else {160 return evmToAddress(input.Ethereum);161 }162}163164export const U128_MAX = (1n << 128n) - 1n;165166const MICROUNIQUE = 1_000_000_000_000n;167const MILLIUNIQUE = 1_000n * MICROUNIQUE;168const CENTIUNIQUE = 10n * MILLIUNIQUE;169export const UNIQUE = 100n * CENTIUNIQUE;170171interface GenericResult<T> {172 success: boolean;173 data: T | null;174}175176interface CreateCollectionResult {177 success: boolean;178 collectionId: number;179}180181interface CreateItemResult {182 success: boolean;183 collectionId: number;184 itemId: number;185 recipient?: CrossAccountId;186 amount?: number;187}188189interface DestroyItemResult {190 success: boolean;191 collectionId: number;192 itemId: number;193 owner: CrossAccountId;194 amount: number;195}196197interface TransferResult {198 collectionId: number;199 itemId: number;200 sender?: CrossAccountId;201 recipient?: CrossAccountId;202 value: bigint;203}204205interface IReFungibleOwner {206 fraction: BN;207 owner: number[];208}209210interface IGetMessage {211 checkMsgUnqMethod: string;212 checkMsgTrsMethod: string;213 checkMsgSysMethod: string;214}215216export interface IFungibleTokenDataType {217 value: number;218}219220export interface IChainLimits {221 collectionNumbersLimit: number;222 accountTokenOwnershipLimit: number;223 collectionsAdminsLimit: number;224 customDataLimit: number;225 nftSponsorTransferTimeout: number;226 fungibleSponsorTransferTimeout: number;227 refungibleSponsorTransferTimeout: number;228 //offchainSchemaLimit: number;229 //constOnChainSchemaLimit: number;230}231232export interface IReFungibleTokenDataType {233 owner: IReFungibleOwner[];234}235236export function uniqueEventMessage(events: EventRecord[]): IGetMessage {237 let checkMsgUnqMethod = '';238 let checkMsgTrsMethod = '';239 let checkMsgSysMethod = '';240 events.forEach(({event: {method, section}}) => {241 if (section === 'common') {242 checkMsgUnqMethod = method;243 } else if (section === 'treasury') {244 checkMsgTrsMethod = method;245 } else if (section === 'system') {246 checkMsgSysMethod = method;247 } else { return null; }248 });249 const result: IGetMessage = {250 checkMsgUnqMethod,251 checkMsgTrsMethod,252 checkMsgSysMethod,253 };254 return result;255}256257export function getEvent<T extends Event>(events: EventRecord[], check: (event: IEvent<AnyTuple>) => event is T): T | undefined {258 const event = events.find(r => check(r.event));259 if (!event) return;260 return event.event as T;261}262263export function getGenericResult<T>(events: EventRecord[]): GenericResult<T>;264export function getGenericResult<T>(265 events: EventRecord[],266 expectSection: string,267 expectMethod: string,268 extractAction: (data: GenericEventData) => T269): GenericResult<T>;270271export function getGenericResult<T>(272 events: EventRecord[],273 expectSection?: string,274 expectMethod?: string,275 extractAction?: (data: GenericEventData) => T,276): GenericResult<T> {277 let success = false;278 let successData = null;279280 events.forEach(({event: {data, method, section}}) => {281 // console.log(` ${phase}: ${section}.${method}:: ${data}`);282 if (method === 'ExtrinsicSuccess') {283 success = true;284 } else if ((expectSection == section) && (expectMethod == method)) {285 successData = extractAction!(data as any);286 }287 });288289 const result: GenericResult<T> = {290 success,291 data: successData,292 };293 return result;294}295296export function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {297 const genericResult = getGenericResult(events, 'common', 'CollectionCreated', (data) => parseInt(data[0].toString(), 10));298 const result: CreateCollectionResult = {299 success: genericResult.success,300 collectionId: genericResult.data ?? 0,301 };302 return result;303}304305export function getCreateItemsResult(events: EventRecord[]): CreateItemResult[] {306 const results: CreateItemResult[] = [];307308 const genericResult = getGenericResult<CreateItemResult[]>(events, 'common', 'ItemCreated', (data) => {309 const collectionId = parseInt(data[0].toString(), 10);310 const itemId = parseInt(data[1].toString(), 10);311 const recipient = normalizeAccountId(data[2].toJSON() as any);312 const amount = parseInt(data[3].toString(), 10);313314 const itemRes: CreateItemResult = {315 success: true,316 collectionId,317 itemId,318 recipient,319 amount,320 };321322 results.push(itemRes);323 return results;324 });325326 if (!genericResult.success) return [];327 return results;328}329330export function getCreateItemResult(events: EventRecord[]): CreateItemResult {331 const genericResult = getGenericResult(events, 'common', 'ItemCreated', (data) => data.map(function(value) { return value.toJSON(); }));332333 if (genericResult.data == null)334 return {335 success: genericResult.success,336 collectionId: 0,337 itemId: 0,338 amount: 0,339 };340 else341 return {342 success: genericResult.success,343 collectionId: genericResult.data[0] as number,344 itemId: genericResult.data[1] as number,345 recipient: normalizeAccountId(genericResult.data![2] as any),346 amount: genericResult.data[3] as number,347 };348}349350export function getDestroyItemsResult(events: EventRecord[]): DestroyItemResult[] {351 const results: DestroyItemResult[] = [];352353 const genericResult = getGenericResult<DestroyItemResult[]>(events, 'common', 'ItemDestroyed', (data) => {354 const collectionId = parseInt(data[0].toString(), 10);355 const itemId = parseInt(data[1].toString(), 10);356 const owner = normalizeAccountId(data[2].toJSON() as any);357 const amount = parseInt(data[3].toString(), 10);358359 const itemRes: DestroyItemResult = {360 success: true,361 collectionId,362 itemId,363 owner,364 amount,365 };366367 results.push(itemRes);368 return results;369 });370371 if (!genericResult.success) return [];372 return results;373}374375export function getTransferResult(api: ApiPromise, events: EventRecord[]): TransferResult {376 for (const {event} of events) {377 if (api.events.common.Transfer.is(event)) {378 const [collection, token, sender, recipient, value] = event.data;379 return {380 collectionId: collection.toNumber(),381 itemId: token.toNumber(),382 sender: normalizeAccountId(sender.toJSON() as any),383 recipient: normalizeAccountId(recipient.toJSON() as any),384 value: value.toBigInt(),385 };386 }387 }388 throw new Error('no transfer event');389}390391interface Nft {392 type: 'NFT';393}394395interface Fungible {396 type: 'Fungible';397 decimalPoints: number;398}399400interface ReFungible {401 type: 'ReFungible';402}403404export type CollectionMode = Nft | Fungible | ReFungible;405406export type Property = {407 key: any,408 value: any,409};410411type Permission = {412 mutable: boolean;413 collectionAdmin: boolean;414 tokenOwner: boolean;415}416417type PropertyPermission = {418 key: any;419 permission: Permission;420}421422export type CreateCollectionParams = {423 mode: CollectionMode,424 name: string,425 description: string,426 tokenPrefix: string,427 properties?: Array<Property>,428 propPerm?: Array<PropertyPermission>429};430431const defaultCreateCollectionParams: CreateCollectionParams = {432 description: 'description',433 mode: {type: 'NFT'},434 name: 'name',435 tokenPrefix: 'prefix',436};437438export async function439createCollection(440 api: ApiPromise,441 sender: IKeyringPair,442 params: Partial<CreateCollectionParams> = {},443): Promise<CreateCollectionResult> {444 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};445446 let modeprm = {};447 if (mode.type === 'NFT') {448 modeprm = {nft: null};449 } else if (mode.type === 'Fungible') {450 modeprm = {fungible: mode.decimalPoints};451 } else if (mode.type === 'ReFungible') {452 modeprm = {refungible: null};453 }454455 const tx = api.tx.unique.createCollectionEx({456 name: strToUTF16(name),457 description: strToUTF16(description),458 tokenPrefix: strToUTF16(tokenPrefix),459 mode: modeprm as any,460 });461 const events = await executeTransaction(api, sender, tx);462 return getCreateCollectionResult(events);463}464465export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {466 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};467468 let collectionId = 0;469 await usingApi(async (api, privateKeyWrapper) => {470 // Get number of collections before the transaction471 const collectionCountBefore = await getCreatedCollectionCount(api);472473 // Run the CreateCollection transaction474 const alicePrivateKey = privateKeyWrapper('//Alice');475476 const result = await createCollection(api, alicePrivateKey, params);477478 // Get number of collections after the transaction479 const collectionCountAfter = await getCreatedCollectionCount(api);480481 // Get the collection482 const collection = await queryCollectionExpectSuccess(api, result.collectionId);483484 // What to expect485 // tslint:disable-next-line:no-unused-expression486 expect(result.success).to.be.true;487 expect(result.collectionId).to.be.equal(collectionCountAfter);488 // tslint:disable-next-line:no-unused-expression489 expect(collection).to.be.not.null;490 expect(collectionCountAfter).to.be.equal(collectionCountBefore + 1, 'Error: NFT collection NOT created.');491 expect(collection.owner.toString()).to.be.equal(toSubstrateAddress(alicePrivateKey));492 expect(utf16ToStr(collection.name.toJSON() as any)).to.be.equal(name);493 expect(utf16ToStr(collection.description.toJSON() as any)).to.be.equal(description);494 expect(hexToStr(collection.tokenPrefix.toJSON())).to.be.equal(tokenPrefix);495496 collectionId = result.collectionId;497 });498499 return collectionId;500}501502export async function createCollectionWithPropsExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {503 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};504505 let collectionId = 0;506 await usingApi(async (api, privateKeyWrapper) => {507 // Get number of collections before the transaction508 const collectionCountBefore = await getCreatedCollectionCount(api);509510 // Run the CreateCollection transaction511 const alicePrivateKey = privateKeyWrapper('//Alice');512513 let modeprm = {};514 if (mode.type === 'NFT') {515 modeprm = {nft: null};516 } else if (mode.type === 'Fungible') {517 modeprm = {fungible: mode.decimalPoints};518 } else if (mode.type === 'ReFungible') {519 modeprm = {refungible: null};520 }521522 const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any, properties: params.properties, tokenPropertyPermissions: params.propPerm});523 const events = await submitTransactionAsync(alicePrivateKey, tx);524 const result = getCreateCollectionResult(events);525526 // Get number of collections after the transaction527 const collectionCountAfter = await getCreatedCollectionCount(api);528529 // Get the collection530 const collection = await queryCollectionExpectSuccess(api, result.collectionId);531532 // What to expect533 // tslint:disable-next-line:no-unused-expression534 expect(result.success).to.be.true;535 expect(result.collectionId).to.be.equal(collectionCountAfter);536 // tslint:disable-next-line:no-unused-expression537 expect(collection).to.be.not.null;538 expect(collectionCountAfter).to.be.equal(collectionCountBefore + 1, 'Error: NFT collection NOT created.');539 expect(collection.owner.toString()).to.be.equal(toSubstrateAddress(alicePrivateKey));540 expect(utf16ToStr(collection.name.toJSON() as any)).to.be.equal(name);541 expect(utf16ToStr(collection.description.toJSON() as any)).to.be.equal(description);542 expect(hexToStr(collection.tokenPrefix.toJSON())).to.be.equal(tokenPrefix);543544545 collectionId = result.collectionId;546 });547548 return collectionId;549}550551export async function createCollectionWithPropsExpectFailure(params: Partial<CreateCollectionParams> = {}) {552 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};553554 await usingApi(async (api, privateKeyWrapper) => {555 // Get number of collections before the transaction556 const collectionCountBefore = await getCreatedCollectionCount(api);557558 // Run the CreateCollection transaction559 const alicePrivateKey = privateKeyWrapper('//Alice');560561 let modeprm = {};562 if (mode.type === 'NFT') {563 modeprm = {nft: null};564 } else if (mode.type === 'Fungible') {565 modeprm = {fungible: mode.decimalPoints};566 } else if (mode.type === 'ReFungible') {567 modeprm = {refungible: null};568 }569570 const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any, properties: params.properties, tokenPropertyPermissions: params.propPerm});571 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;572573574 // Get number of collections after the transaction575 const collectionCountAfter = await getCreatedCollectionCount(api);576577 expect(collectionCountAfter).to.be.equal(collectionCountBefore, 'Error: Collection with incorrect data created.');578 });579}580581export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {582 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};583584 let modeprm = {};585 if (mode.type === 'NFT') {586 modeprm = {nft: null};587 } else if (mode.type === 'Fungible') {588 modeprm = {fungible: mode.decimalPoints};589 } else if (mode.type === 'ReFungible') {590 modeprm = {refungible: null};591 }592593 await usingApi(async (api, privateKeyWrapper) => {594 // Get number of collections before the transaction595 const collectionCountBefore = await getCreatedCollectionCount(api);596597 // Run the CreateCollection transaction598 const alicePrivateKey = privateKeyWrapper('//Alice');599 const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});600 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;601602 // Get number of collections after the transaction603 const collectionCountAfter = await getCreatedCollectionCount(api);604605 // What to expect606 expect(collectionCountAfter).to.be.equal(collectionCountBefore, 'Error: Collection with incorrect data created.');607 });608}609610export async function findUnusedAddress(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, seedAddition = ''): Promise<IKeyringPair> {611 let bal = 0n;612 let unused;613 do {614 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;615 unused = privateKeyWrapper(`//${randomSeed}`);616 bal = (await api.query.system.account(unused.address)).data.free.toBigInt();617 } while (bal !== 0n);618 return unused;619}620621export async function getAllowance(api: ApiPromise, collectionId: number, owner: CrossAccountId | string | IKeyringPair, approved: CrossAccountId | string | IKeyringPair, tokenId: number) {622 return (await api.rpc.unique.allowance(collectionId, normalizeAccountId(owner), normalizeAccountId(approved), tokenId)).toBigInt();623}624625export function findUnusedAddresses(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, amount: number): Promise<IKeyringPair[]> {626 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, privateKeyWrapper, '_' + Date.now())));627}628629export async function findNotExistingCollection(api: ApiPromise): Promise<number> {630 const totalNumber = await getCreatedCollectionCount(api);631 const newCollection: number = totalNumber + 1;632 return newCollection;633}634635function getDestroyResult(events: EventRecord[]): boolean {636 let success = false;637 events.forEach(({event: {method}}) => {638 if (method == 'ExtrinsicSuccess') {639 success = true;640 }641 });642 return success;643}644645export async function destroyCollectionExpectFailure(collectionId: number, senderSeed = '//Alice') {646 await usingApi(async (api, privateKeyWrapper) => {647 // Run the DestroyCollection transaction648 const alicePrivateKey = privateKeyWrapper(senderSeed);649 const tx = api.tx.unique.destroyCollection(collectionId);650 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;651 });652}653654export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed = '//Alice') {655 await usingApi(async (api, privateKeyWrapper) => {656 // Run the DestroyCollection transaction657 const alicePrivateKey = privateKeyWrapper(senderSeed);658 const tx = api.tx.unique.destroyCollection(collectionId);659 const events = await submitTransactionAsync(alicePrivateKey, tx);660 const result = getDestroyResult(events);661 expect(result).to.be.true;662663 // What to expect664 expect(await getDetailedCollectionInfo(api, collectionId)).to.be.null;665 });666}667668export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {669 await usingApi(async (api) => {670 const tx = api.tx.unique.setCollectionLimits(collectionId, limits);671 const events = await submitTransactionAsync(sender, tx);672 const result = getGenericResult(events);673674 expect(result.success).to.be.true;675 });676}677678export const setCollectionPermissionsExpectSuccess = async (sender: IKeyringPair, collectionId: number, permissions: any) => {679 await usingApi(async(api) => {680 const tx = api.tx.unique.setCollectionPermissions(collectionId, permissions);681 const events = await submitTransactionAsync(sender, tx);682 const result = getGenericResult(events);683684 expect(result.success).to.be.true;685 });686};687688export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {689 await usingApi(async (api) => {690 const tx = api.tx.unique.setCollectionLimits(collectionId, limits);691 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;692 const result = getGenericResult(events);693694 expect(result.success).to.be.false;695 });696}697698export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string, sender = '//Alice') {699 await usingApi(async (api, privateKeyWrapper) => {700701 // Run the transaction702 const senderPrivateKey = privateKeyWrapper(sender);703 const tx = api.tx.unique.setCollectionSponsor(collectionId, sponsor);704 const events = await submitTransactionAsync(senderPrivateKey, tx);705 const result = getGenericResult(events);706707 // Get the collection708 const collection = await queryCollectionExpectSuccess(api, collectionId);709710 // What to expect711 expect(result.success).to.be.true;712 expect(collection.sponsorship.toJSON()).to.deep.equal({713 unconfirmed: sponsor,714 });715 });716}717718export async function removeCollectionSponsorExpectSuccess(collectionId: number, sender = '//Alice') {719 await usingApi(async (api, privateKeyWrapper) => {720721 // Run the transaction722 const alicePrivateKey = privateKeyWrapper(sender);723 const tx = api.tx.unique.removeCollectionSponsor(collectionId);724 const events = await submitTransactionAsync(alicePrivateKey, tx);725 const result = getGenericResult(events);726727 // Get the collection728 const collection = await queryCollectionExpectSuccess(api, collectionId);729730 // What to expect731 expect(result.success).to.be.true;732 expect(collection.sponsorship.toJSON()).to.be.deep.equal({disabled: null});733 });734}735736export async function removeCollectionSponsorExpectFailure(collectionId: number, senderSeed = '//Alice') {737 await usingApi(async (api, privateKeyWrapper) => {738739 // Run the transaction740 const alicePrivateKey = privateKeyWrapper(senderSeed);741 const tx = api.tx.unique.removeCollectionSponsor(collectionId);742 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;743 });744}745746export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed = '//Alice') {747 await usingApi(async (api, privateKeyWrapper) => {748749 // Run the transaction750 const alicePrivateKey = privateKeyWrapper(senderSeed);751 const tx = api.tx.unique.setCollectionSponsor(collectionId, sponsor);752 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;753 });754}755756export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed = '//Alice') {757 await usingApi(async (api, privateKeyWrapper) => {758759 // Run the transaction760 const sender = privateKeyWrapper(senderSeed);761 await confirmSponsorshipByKeyExpectSuccess(collectionId, sender);762 });763}764765export async function confirmSponsorshipByKeyExpectSuccess(collectionId: number, sender: IKeyringPair) {766 await usingApi(async (api, privateKeyWrapper) => {767768 // Run the transaction769 const tx = api.tx.unique.confirmSponsorship(collectionId);770 const events = await submitTransactionAsync(sender, tx);771 const result = getGenericResult(events);772773 // Get the collection774 const collection = await queryCollectionExpectSuccess(api, collectionId);775776 // What to expect777 expect(result.success).to.be.true;778 expect(collection.sponsorship.toJSON()).to.be.deep.equal({779 confirmed: sender.address,780 });781 });782}783784785export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed = '//Alice') {786 await usingApi(async (api, privateKeyWrapper) => {787788 // Run the transaction789 const sender = privateKeyWrapper(senderSeed);790 const tx = api.tx.unique.confirmSponsorship(collectionId);791 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;792 });793}794795export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {796 await usingApi(async (api) => {797 const tx = api.tx.unique.enableContractSponsoring(contractAddress, enable);798 const events = await submitTransactionAsync(sender, tx);799 const result = getGenericResult(events);800801 expect(result.success).to.be.true;802 });803}804805export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {806 await usingApi(async (api) => {807 const tx = api.tx.unique.enableContractSponsoring(contractAddress, enable);808 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;809 const result = getGenericResult(events);810811 expect(result.success).to.be.false;812 });813}814815export async function setTransferFlagExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {816817 await usingApi(async (api) => {818819 const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);820 const events = await submitTransactionAsync(sender, tx);821 const result = getGenericResult(events);822823 expect(result.success).to.be.true;824 });825}826827export async function setTransferFlagExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {828829 await usingApi(async (api) => {830831 const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);832 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;833 const result = getGenericResult(events);834835 expect(result.success).to.be.false;836 });837}838839export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {840 await usingApi(async (api) => {841 const tx = api.tx.unique.setContractSponsoringRateLimit(contractAddress, rateLimit);842 const events = await submitTransactionAsync(sender, tx);843 const result = getGenericResult(events);844845 expect(result.success).to.be.true;846 });847}848849export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {850 await usingApi(async (api) => {851 const tx = api.tx.unique.setContractSponsoringRateLimit(contractAddress, rateLimit);852 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;853 const result = getGenericResult(events);854855 expect(result.success).to.be.false;856 });857}858859export async function getNextSponsored(860 api: ApiPromise,861 collectionId: number,862 account: string | CrossAccountId,863 tokenId: number,864): Promise<number> {865 return Number((await api.rpc.unique.nextSponsored(collectionId, account, tokenId)).unwrapOr(-1));866}867868export async function toggleContractAllowlistExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, value = true) {869 await usingApi(async (api) => {870 const tx = api.tx.unique.toggleContractAllowList(contractAddress, value);871 const events = await submitTransactionAsync(sender, tx);872 const result = getGenericResult(events);873874 expect(result.success).to.be.true;875 });876}877878export async function isAllowlistedInContract(contractAddress: AccountId | string, user: string) {879 let allowlisted = false;880 await usingApi(async (api) => {881 allowlisted = (await api.query.unique.contractAllowList(contractAddress, user)).toJSON() as boolean;882 });883 return allowlisted;884}885886export async function addToContractAllowListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {887 await usingApi(async (api) => {888 const tx = api.tx.unique.addToContractAllowList(contractAddress.toString(), user.toString());889 const events = await submitTransactionAsync(sender, tx);890 const result = getGenericResult(events);891892 expect(result.success).to.be.true;893 });894}895896export async function removeFromContractAllowListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {897 await usingApi(async (api) => {898 const tx = api.tx.unique.removeFromContractAllowList(contractAddress.toString(), user.toString());899 const events = await submitTransactionAsync(sender, tx);900 const result = getGenericResult(events);901902 expect(result.success).to.be.true;903 });904}905906export async function removeFromContractAllowListExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {907 await usingApi(async (api) => {908 const tx = api.tx.unique.removeFromContractAllowList(contractAddress.toString(), user.toString());909 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;910 const result = getGenericResult(events);911912 expect(result.success).to.be.false;913 });914}915916export interface CreateFungibleData {917 readonly Value: bigint;918}919920export interface CreateReFungibleData { }921export interface CreateNftData { }922923export type CreateItemData = {924 NFT: CreateNftData;925} | {926 Fungible: CreateFungibleData;927} | {928 ReFungible: CreateReFungibleData;929};930931export async function burnItem(api: ApiPromise, sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint) : Promise<boolean> {932 const tx = api.tx.unique.burnItem(collectionId, tokenId, value);933 const events = await submitTransactionAsync(sender, tx);934 return getGenericResult(events).success;935}936937export async function burnItemExpectSuccess(sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint = 1) {938 await usingApi(async (api) => {939 const balanceBefore = await getBalance(api, collectionId, normalizeAccountId(sender), tokenId);940 // if burning token by admin - use adminButnItemExpectSuccess941 expect(balanceBefore >= BigInt(value)).to.be.true;942943 expect(await burnItem(api, sender, collectionId, tokenId, value)).to.be.true;944945 const balanceAfter = await getBalance(api, collectionId, normalizeAccountId(sender), tokenId);946 expect(balanceAfter + BigInt(value)).to.be.equal(balanceBefore);947 });948}949950export async function burnItemExpectFailure(sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint = 1) {951 await usingApi(async (api) => {952 const tx = api.tx.unique.burnItem(collectionId, tokenId, value);953954 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;955 const result = getCreateCollectionResult(events);956 // tslint:disable-next-line:no-unused-expression957 expect(result.success).to.be.false;958 });959}960961export async function burnFromExpectSuccess(sender: IKeyringPair, from: IKeyringPair | CrossAccountId, collectionId: number, tokenId: number, value: number | bigint = 1) {962 await usingApi(async (api) => {963 const tx = api.tx.unique.burnFrom(collectionId, normalizeAccountId(from), tokenId, value);964 const events = await submitTransactionAsync(sender, tx);965 return getGenericResult(events).success;966 });967}968969export async function970approve(971 api: ApiPromise,972 collectionId: number,973 tokenId: number, owner: IKeyringPair, approved: CrossAccountId | string | IKeyringPair, amount: number | bigint,974) {975 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);976 const events = await submitTransactionAsync(owner, approveUniqueTx);977 return getGenericResult(events).success;978}979980export async function981approveExpectSuccess(982 collectionId: number,983 tokenId: number, owner: IKeyringPair, approved: CrossAccountId | string, amount: number | bigint = 1,984) {985 await usingApi(async (api: ApiPromise) => {986 const result = await approve(api, collectionId, tokenId, owner, approved, amount);987 expect(result).to.be.true;988989 expect(await getAllowance(api, collectionId, owner, approved, tokenId)).to.be.equal(BigInt(amount));990 });991}992993export async function adminApproveFromExpectSuccess(994 collectionId: number,995 tokenId: number, admin: IKeyringPair, owner: CrossAccountId | string, approved: CrossAccountId | string, amount: number | bigint = 1,996) {997 await usingApi(async (api: ApiPromise) => {998 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);999 const events = await submitTransactionAsync(admin, approveUniqueTx);1000 const result = getGenericResult(events);1001 expect(result.success).to.be.true;10021003 expect(await getAllowance(api, collectionId, owner, approved, tokenId)).to.be.equal(BigInt(amount));1004 });1005}10061007export async function1008transferFrom(1009 api: ApiPromise,1010 collectionId: number,1011 tokenId: number,1012 accountApproved: IKeyringPair,1013 accountFrom: IKeyringPair | CrossAccountId,1014 accountTo: IKeyringPair | CrossAccountId,1015 value: number | bigint,1016) {1017 const from = normalizeAccountId(accountFrom);1018 const to = normalizeAccountId(accountTo);1019 const transferFromTx = api.tx.unique.transferFrom(from, to, collectionId, tokenId, value);1020 const events = await submitTransactionAsync(accountApproved, transferFromTx);1021 return getGenericResult(events).success;1022}10231024export async function1025transferFromExpectSuccess(1026 collectionId: number,1027 tokenId: number,1028 accountApproved: IKeyringPair,1029 accountFrom: IKeyringPair | CrossAccountId,1030 accountTo: IKeyringPair | CrossAccountId,1031 value: number | bigint = 1,1032 type = 'NFT',1033) {1034 await usingApi(async (api: ApiPromise) => {1035 const from = normalizeAccountId(accountFrom);1036 const to = normalizeAccountId(accountTo);1037 let balanceBefore = 0n;1038 if (type === 'Fungible' || type === 'ReFungible') {1039 balanceBefore = await getBalance(api, collectionId, to, tokenId);1040 }1041 expect(await transferFrom(api, collectionId, tokenId, accountApproved, accountFrom, accountTo, value)).to.be.true;1042 if (type === 'NFT') {1043 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(to);1044 }1045 if (type === 'Fungible') {1046 const balanceAfter = await getBalance(api, collectionId, to, tokenId);1047 if (JSON.stringify(to) !== JSON.stringify(from)) {1048 expect(balanceAfter - balanceBefore).to.be.equal(BigInt(value));1049 } else {1050 expect(balanceAfter).to.be.equal(balanceBefore);1051 }1052 }1053 if (type === 'ReFungible') {1054 expect(await getBalance(api, collectionId, to, tokenId)).to.be.equal(balanceBefore + BigInt(value));1055 }1056 });1057}10581059export async function1060transferFromExpectFail(1061 collectionId: number,1062 tokenId: number,1063 accountApproved: IKeyringPair,1064 accountFrom: IKeyringPair,1065 accountTo: IKeyringPair,1066 value: number | bigint = 1,1067) {1068 await usingApi(async (api: ApiPromise) => {1069 const transferFromTx = api.tx.unique.transferFrom(normalizeAccountId(accountFrom.address), normalizeAccountId(accountTo.address), collectionId, tokenId, value);1070 const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;1071 const result = getCreateCollectionResult(events);1072 // tslint:disable-next-line:no-unused-expression1073 expect(result.success).to.be.false;1074 });1075}10761077/* eslint no-async-promise-executor: "off" */1078export async function getBlockNumber(api: ApiPromise): Promise<number> {1079 return new Promise<number>(async (resolve) => {1080 const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {1081 unsubscribe();1082 resolve(head.number.toNumber());1083 });1084 });1085}10861087export async function addCollectionAdminExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | CrossAccountId) {1088 await usingApi(async (api) => {1089 const changeAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(address));1090 const events = await submitTransactionAsync(sender, changeAdminTx);1091 const result = getCreateCollectionResult(events);1092 expect(result.success).to.be.true;1093 });1094}10951096export async function adminApproveFromExpectFail(1097 collectionId: number,1098 tokenId: number, admin: IKeyringPair, owner: CrossAccountId | string, approved: CrossAccountId | string, amount: number | bigint = 1,1099) {1100 await usingApi(async (api: ApiPromise) => {1101 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);1102 const events = await expect(submitTransactionAsync(admin, approveUniqueTx)).to.be.rejected;1103 const result = getGenericResult(events);1104 expect(result.success).to.be.false;1105 });1106}11071108export async function1109getFreeBalance(account: IKeyringPair): Promise<bigint> {1110 let balance = 0n;1111 await usingApi(async (api) => {1112 balance = BigInt((await api.query.system.account(account.address)).data.free.toString());1113 });11141115 return balance;1116}11171118export async function paraSiblingSovereignAccount(paraid: number): Promise<string> {1119 return usingApi(async api => {1120 // We are getting a *sibling* parachain sovereign account,1121 // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c1122 const siblingPrefix = '0x7369626c';11231124 const encodedParaId = api.createType('u32', paraid).toHex(true).substring(2);1125 const suffix = '000000000000000000000000000000000000000000000000';11261127 return siblingPrefix + encodedParaId + suffix;1128 });1129}11301131export async function transferBalanceTo(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) {1132 const tx = api.tx.balances.transfer(target, amount);1133 const events = await submitTransactionAsync(source, tx);1134 const result = getGenericResult(events);1135 expect(result.success).to.be.true;1136}11371138export async function1139scheduleExpectSuccess(1140 operationTx: any,1141 sender: IKeyringPair,1142 blockSchedule: number,1143 scheduledId: string,1144 period = 1,1145 repetitions = 1,1146) {1147 await usingApi(async (api: ApiPromise) => {1148 const blockNumber: number | undefined = await getBlockNumber(api);1149 const expectedBlockNumber = blockNumber + blockSchedule;11501151 expect(blockNumber).to.be.greaterThan(0);1152 const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule1153 scheduledId,1154 expectedBlockNumber,1155 repetitions > 1 ? [period, repetitions] : null,1156 0,1157 {Value: operationTx as any},1158 );11591160 const events = await submitTransactionAsync(sender, scheduleTx);1161 expect(getGenericResult(events).success).to.be.true;1162 });1163}11641165export async function1166scheduleExpectFailure(1167 operationTx: any,1168 sender: IKeyringPair,1169 blockSchedule: number,1170 scheduledId: string,1171 period = 1,1172 repetitions = 1,1173) {1174 await usingApi(async (api: ApiPromise) => {1175 const blockNumber: number | undefined = await getBlockNumber(api);1176 const expectedBlockNumber = blockNumber + blockSchedule;11771178 expect(blockNumber).to.be.greaterThan(0);1179 const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule1180 scheduledId,1181 expectedBlockNumber,1182 repetitions <= 1 ? null : [period, repetitions],1183 0,1184 {Value: operationTx as any},1185 );11861187 //const events =1188 await expect(submitTransactionExpectFailAsync(sender, scheduleTx)).to.be.rejected;1189 //expect(getGenericResult(events).success).to.be.false;1190 });1191}11921193export async function1194scheduleTransferAndWaitExpectSuccess(1195 collectionId: number,1196 tokenId: number,1197 sender: IKeyringPair,1198 recipient: IKeyringPair,1199 value: number | bigint = 1,1200 blockSchedule: number,1201 scheduledId: string,1202) {1203 await usingApi(async (api: ApiPromise) => {1204 await scheduleTransferExpectSuccess(collectionId, tokenId, sender, recipient, value, blockSchedule, scheduledId);12051206 const recipientBalanceBefore = (await api.query.system.account(recipient.address)).data.free.toBigInt();12071208 // sleep for n + 1 blocks1209 await waitNewBlocks(blockSchedule + 1);12101211 const recipientBalanceAfter = (await api.query.system.account(recipient.address)).data.free.toBigInt();12121213 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(recipient.address));1214 expect(recipientBalanceAfter).to.be.equal(recipientBalanceBefore);1215 });1216}12171218export async function1219scheduleTransferExpectSuccess(1220 collectionId: number,1221 tokenId: number,1222 sender: IKeyringPair,1223 recipient: IKeyringPair,1224 value: number | bigint = 1,1225 blockSchedule: number,1226 scheduledId: string,1227) {1228 await usingApi(async (api: ApiPromise) => {1229 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);12301231 await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId);12321233 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(sender.address));1234 });1235}12361237export async function1238scheduleTransferFundsPeriodicExpectSuccess(1239 amount: bigint,1240 sender: IKeyringPair,1241 recipient: IKeyringPair,1242 blockSchedule: number,1243 scheduledId: string,1244 period: number,1245 repetitions: number,1246) {1247 await usingApi(async (api: ApiPromise) => {1248 const transferTx = api.tx.balances.transfer(recipient.address, amount);12491250 const balanceBefore = await getFreeBalance(recipient);12511252 await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId, period, repetitions);12531254 expect(await getFreeBalance(recipient)).to.be.equal(balanceBefore);1255 });1256}12571258export async function1259transfer(1260 api: ApiPromise,1261 collectionId: number,1262 tokenId: number,1263 sender: IKeyringPair,1264 recipient: IKeyringPair | CrossAccountId,1265 value: number | bigint,1266) : Promise<boolean> {1267 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient), collectionId, tokenId, value);1268 const events = await executeTransaction(api, sender, transferTx);1269 return getGenericResult(events).success;1270}12711272export async function1273transferExpectSuccess(1274 collectionId: number,1275 tokenId: number,1276 sender: IKeyringPair,1277 recipient: IKeyringPair | CrossAccountId,1278 value: number | bigint = 1,1279 type = 'NFT',1280) {1281 await usingApi(async (api: ApiPromise) => {1282 const from = normalizeAccountId(sender);1283 const to = normalizeAccountId(recipient);12841285 let balanceBefore = 0n;1286 if (type === 'Fungible' || type === 'ReFungible') {1287 balanceBefore = await getBalance(api, collectionId, to, tokenId);1288 }12891290 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient), collectionId, tokenId, value);1291 const events = await executeTransaction(api, sender, transferTx);1292 const result = getTransferResult(api, events);12931294 expect(result.collectionId).to.be.equal(collectionId);1295 expect(result.itemId).to.be.equal(tokenId);1296 expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));1297 expect(result.recipient).to.be.deep.equal(to);1298 expect(result.value).to.be.equal(BigInt(value));12991300 if (type === 'NFT') {1301 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(to);1302 }1303 if (type === 'Fungible' || type === 'ReFungible') {1304 const balanceAfter = await getBalance(api, collectionId, to, tokenId);1305 if (JSON.stringify(to) !== JSON.stringify(from)) {1306 expect(balanceAfter - balanceBefore).to.be.equal(BigInt(value));1307 } else {1308 expect(balanceAfter).to.be.equal(balanceBefore);1309 }1310 }1311 });1312}13131314export async function1315transferExpectFailure(1316 collectionId: number,1317 tokenId: number,1318 sender: IKeyringPair,1319 recipient: IKeyringPair | CrossAccountId,1320 value: number | bigint = 1,1321) {1322 await usingApi(async (api: ApiPromise) => {1323 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient), collectionId, tokenId, value);1324 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;1325 const result = getGenericResult(events);1326 // if (events && Array.isArray(events)) {1327 // const result = getCreateCollectionResult(events);1328 // tslint:disable-next-line:no-unused-expression1329 expect(result.success).to.be.false;1330 //}1331 });1332}13331334export async function1335approveExpectFail(1336 collectionId: number,1337 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1,1338) {1339 await usingApi(async (api: ApiPromise) => {1340 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved.address), collectionId, tokenId, amount);1341 const events = await expect(submitTransactionExpectFailAsync(owner, approveUniqueTx)).to.be.rejected;1342 const result = getCreateCollectionResult(events);1343 // tslint:disable-next-line:no-unused-expression1344 expect(result.success).to.be.false;1345 });1346}13471348export async function getBalance(1349 api: ApiPromise,1350 collectionId: number,1351 owner: string | CrossAccountId | IKeyringPair,1352 token: number,1353): Promise<bigint> {1354 return (await api.rpc.unique.balance(collectionId, normalizeAccountId(owner), token)).toBigInt();1355}1356export async function getTokenOwner(1357 api: ApiPromise,1358 collectionId: number,1359 token: number,1360): Promise<CrossAccountId> {1361 const owner = (await api.rpc.unique.tokenOwner(collectionId, token)).toJSON() as any;1362 if (owner == null) throw new Error('owner == null');1363 return normalizeAccountId(owner);1364}1365export async function getTopmostTokenOwner(1366 api: ApiPromise,1367 collectionId: number,1368 token: number,1369): Promise<CrossAccountId> {1370 const owner = (await api.rpc.unique.topmostTokenOwner(collectionId, token)).toJSON() as any;1371 if (owner == null) throw new Error('owner == null');1372 return normalizeAccountId(owner);1373}1374export async function getTokenChildren(1375 api: ApiPromise,1376 collectionId: number,1377 tokenId: number,1378): Promise<UpDataStructsTokenChild[]> {1379 return (await api.rpc.unique.tokenChildren(collectionId, tokenId)).toJSON() as any;1380}1381export async function isTokenExists(1382 api: ApiPromise,1383 collectionId: number,1384 token: number,1385): Promise<boolean> {1386 return (await api.rpc.unique.tokenExists(collectionId, token)).toJSON();1387}1388export async function getLastTokenId(1389 api: ApiPromise,1390 collectionId: number,1391): Promise<number> {1392 return (await api.rpc.unique.lastTokenId(collectionId)).toJSON();1393}1394export async function getAdminList(1395 api: ApiPromise,1396 collectionId: number,1397): Promise<string[]> {1398 return (await api.rpc.unique.adminlist(collectionId)).toHuman() as any;1399}1400export async function getTokenProperties(1401 api: ApiPromise,1402 collectionId: number,1403 tokenId: number,1404 propertyKeys: string[],1405): Promise<UpDataStructsProperty[]> {1406 return (await api.rpc.unique.tokenProperties(collectionId, tokenId, propertyKeys)).toHuman() as any;1407}14081409export async function createFungibleItemExpectSuccess(1410 sender: IKeyringPair,1411 collectionId: number,1412 data: CreateFungibleData,1413 owner: CrossAccountId | string = sender.address,1414) {1415 return await usingApi(async (api) => {1416 const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), {Fungible: data});14171418 const events = await submitTransactionAsync(sender, tx);1419 const result = getCreateItemResult(events);14201421 expect(result.success).to.be.true;1422 return result.itemId;1423 });1424}14251426export async function createMultipleItemsExpectSuccess(sender: IKeyringPair, collectionId: number, itemsData: any, owner: CrossAccountId | string = sender.address) {1427 await usingApi(async (api) => {1428 const to = normalizeAccountId(owner);1429 const tx = api.tx.unique.createMultipleItems(collectionId, to, itemsData);14301431 const events = await submitTransactionAsync(sender, tx);1432 expect(getGenericResult(events).success).to.be.true;1433 });1434}14351436export async function createMultipleItemsWithPropsExpectSuccess(sender: IKeyringPair, collectionId: number, itemsData: any, owner: CrossAccountId | string = sender.address) {1437 await usingApi(async (api) => {1438 const to = normalizeAccountId(owner);1439 const tx = api.tx.unique.createMultipleItems(collectionId, to, itemsData);14401441 const events = await submitTransactionAsync(sender, tx);1442 const result = getCreateItemsResult(events);14431444 for (const res of result) {1445 expect(await api.rpc.unique.tokenProperties(collectionId, res.itemId)).not.to.be.empty;1446 }1447 });1448}14491450export async function createMultipleItemsExWithPropsExpectSuccess(sender: IKeyringPair, collectionId: number, itemsData: any) {1451 await usingApi(async (api) => {1452 const tx = api.tx.unique.createMultipleItemsEx(collectionId, itemsData);14531454 const events = await submitTransactionAsync(sender, tx);1455 const result = getCreateItemsResult(events);14561457 for (const res of result) {1458 expect(await api.rpc.unique.tokenProperties(collectionId, res.itemId)).not.to.be.empty;1459 }1460 });1461}14621463export async function createItemWithPropsExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, props: Array<Property>, owner: CrossAccountId | string = sender.address) {1464 let newItemId = 0;1465 await usingApi(async (api) => {1466 const to = normalizeAccountId(owner);1467 const itemCountBefore = await getLastTokenId(api, collectionId);1468 const itemBalanceBefore = await getBalance(api, collectionId, to, newItemId);14691470 let tx;1471 if (createMode === 'Fungible') {1472 const createData = {fungible: {value: 10}};1473 tx = api.tx.unique.createItem(collectionId, to, createData as any);1474 } else if (createMode === 'ReFungible') {1475 const createData = {refungible: {pieces: 100}};1476 tx = api.tx.unique.createItem(collectionId, to, createData as any);1477 } else {1478 const data = api.createType('UpDataStructsCreateItemData', {NFT: {properties: props}});1479 tx = api.tx.unique.createItem(collectionId, to, data as UpDataStructsCreateItemData);1480 }14811482 const events = await submitTransactionAsync(sender, tx);1483 const result = getCreateItemResult(events);14841485 const itemCountAfter = await getLastTokenId(api, collectionId);1486 const itemBalanceAfter = await getBalance(api, collectionId, to, newItemId);14871488 if (createMode === 'NFT') {1489 expect(await api.rpc.unique.tokenProperties(collectionId, result.itemId)).not.to.be.empty;1490 }14911492 // What to expect1493 // tslint:disable-next-line:no-unused-expression1494 expect(result.success).to.be.true;1495 if (createMode === 'Fungible') {1496 expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);1497 } else {1498 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);1499 }1500 expect(collectionId).to.be.equal(result.collectionId);1501 expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());1502 expect(to).to.be.deep.equal(result.recipient);1503 newItemId = result.itemId;1504 });1505 return newItemId;1506}15071508export async function createItemWithPropsExpectFailure(sender: IKeyringPair, collectionId: number, createMode: string, props: Array<Property>, owner: CrossAccountId | string = sender.address) {1509 await usingApi(async (api) => {15101511 let tx;1512 if (createMode === 'NFT') {1513 const data = api.createType('UpDataStructsCreateItemData', {NFT: {properties: props}}) as UpDataStructsCreateItemData;1514 tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), data);1515 } else {1516 tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createMode);1517 }151815191520 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1521 if(events.message && events.message.toString().indexOf('1002: Verification Error') > -1) return;1522 const result = getCreateItemResult(events);15231524 expect(result.success).to.be.false;1525 });1526}15271528export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {1529 let newItemId = 0;1530 await usingApi(async (api) => {1531 const to = normalizeAccountId(owner);1532 const itemCountBefore = await getLastTokenId(api, collectionId);1533 const itemBalanceBefore = await getBalance(api, collectionId, to, newItemId);15341535 let tx;1536 if (createMode === 'Fungible') {1537 const createData = {fungible: {value: 10}};1538 tx = api.tx.unique.createItem(collectionId, to, createData as any);1539 } else if (createMode === 'ReFungible') {1540 const createData = {refungible: {pieces: 100}};1541 tx = api.tx.unique.createItem(collectionId, to, createData as any);1542 } else {1543 const createData = {nft: {}};1544 tx = api.tx.unique.createItem(collectionId, to, createData as any);1545 }15461547 const events = await executeTransaction(api, sender, tx);1548 const result = getCreateItemResult(events);15491550 const itemCountAfter = await getLastTokenId(api, collectionId);1551 const itemBalanceAfter = await getBalance(api, collectionId, to, newItemId);15521553 // What to expect1554 // tslint:disable-next-line:no-unused-expression1555 expect(result.success).to.be.true;1556 if (createMode === 'Fungible') {1557 expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);1558 } else {1559 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);1560 }1561 expect(collectionId).to.be.equal(result.collectionId);1562 expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());1563 expect(to).to.be.deep.equal(result.recipient);1564 newItemId = result.itemId;1565 });1566 return newItemId;1567}15681569export async function createRefungibleToken(api: ApiPromise, sender: IKeyringPair, collectionId: number, amount: bigint, owner: CrossAccountId | IKeyringPair | string = sender.address) : Promise<CreateItemResult> {1570 const createData = {refungible: {pieces: amount}};1571 const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createData as any);15721573 const events = await submitTransactionAsync(sender, tx);1574 return getCreateItemResult(events);1575}15761577export async function createItemExpectFailure(sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {1578 await usingApi(async (api) => {1579 const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createMode);15801581 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1582 const result = getCreateItemResult(events);15831584 expect(result.success).to.be.false;1585 });1586}15871588export async function setPublicAccessModeExpectSuccess(1589 sender: IKeyringPair, collectionId: number,1590 accessMode: 'Normal' | 'AllowList',1591) {1592 await usingApi(async (api) => {15931594 // Run the transaction1595 const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: accessMode});1596 const events = await submitTransactionAsync(sender, tx);1597 const result = getGenericResult(events);15981599 // Get the collection1600 const collection = await queryCollectionExpectSuccess(api, collectionId);16011602 // What to expect1603 // tslint:disable-next-line:no-unused-expression1604 expect(result.success).to.be.true;1605 expect(collection.permissions.access.toHuman()).to.be.equal(accessMode);1606 });1607}16081609export async function setPublicAccessModeExpectFail(1610 sender: IKeyringPair, collectionId: number,1611 accessMode: 'Normal' | 'AllowList',1612) {1613 await usingApi(async (api) => {16141615 // Run the transaction1616 const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: accessMode});1617 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1618 const result = getGenericResult(events);16191620 // What to expect1621 // tslint:disable-next-line:no-unused-expression1622 expect(result.success).to.be.false;1623 });1624}16251626export async function enableAllowListExpectSuccess(sender: IKeyringPair, collectionId: number) {1627 await setPublicAccessModeExpectSuccess(sender, collectionId, 'AllowList');1628}16291630export async function enableAllowListExpectFail(sender: IKeyringPair, collectionId: number) {1631 await setPublicAccessModeExpectFail(sender, collectionId, 'AllowList');1632}16331634export async function disableAllowListExpectSuccess(sender: IKeyringPair, collectionId: number) {1635 await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');1636}16371638export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {1639 await usingApi(async (api) => {16401641 // Run the transaction1642 const tx = api.tx.unique.setCollectionPermissions(collectionId, {mintMode: enabled});1643 const events = await submitTransactionAsync(sender, tx);1644 const result = getGenericResult(events);1645 expect(result.success).to.be.true;16461647 // Get the collection1648 const collection = await queryCollectionExpectSuccess(api, collectionId);16491650 expect(collection.permissions.mintMode.toHuman()).to.be.equal(enabled);1651 });1652}16531654export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {1655 await setMintPermissionExpectSuccess(sender, collectionId, true);1656}16571658export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {1659 await usingApi(async (api) => {1660 // Run the transaction1661 const tx = api.tx.unique.setCollectionPermissions(collectionId, {mintMode: enabled});1662 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1663 const result = getCreateCollectionResult(events);1664 // tslint:disable-next-line:no-unused-expression1665 expect(result.success).to.be.false;1666 });1667}16681669export async function setChainLimitsExpectFailure(sender: IKeyringPair, limits: IChainLimits) {1670 await usingApi(async (api) => {1671 // Run the transaction1672 const tx = api.tx.unique.setChainLimits(limits);1673 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1674 const result = getCreateCollectionResult(events);1675 // tslint:disable-next-line:no-unused-expression1676 expect(result.success).to.be.false;1677 });1678}16791680export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId | IKeyringPair) {1681 return (await api.rpc.unique.allowed(collectionId, normalizeAccountId(address))).toJSON();1682}16831684export async function addToAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId | CrossAccountId) {1685 await usingApi(async (api) => {1686 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.false;16871688 // Run the transaction1689 const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(address));1690 const events = await submitTransactionAsync(sender, tx);1691 const result = getGenericResult(events);1692 expect(result.success).to.be.true;16931694 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;1695 });1696}16971698export async function addToAllowListAgainExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1699 await usingApi(async (api) => {17001701 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;17021703 // Run the transaction1704 const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(address));1705 const events = await submitTransactionAsync(sender, tx);1706 const result = getGenericResult(events);1707 expect(result.success).to.be.true;17081709 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;1710 });1711}17121713export async function addToAllowListExpectFail(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1714 await usingApi(async (api) => {17151716 // Run the transaction1717 const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(address));1718 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1719 const result = getGenericResult(events);17201721 // What to expect1722 // tslint:disable-next-line:no-unused-expression1723 expect(result.success).to.be.false;1724 });1725}17261727export async function removeFromAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1728 await usingApi(async (api) => {1729 // Run the transaction1730 const tx = api.tx.unique.removeFromAllowList(collectionId, normalizeAccountId(address));1731 const events = await submitTransactionAsync(sender, tx);1732 const result = getGenericResult(events);17331734 // What to expect1735 // tslint:disable-next-line:no-unused-expression1736 expect(result.success).to.be.true;1737 });1738}17391740export async function removeFromAllowListExpectFailure(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1741 await usingApi(async (api) => {1742 // Run the transaction1743 const tx = api.tx.unique.removeFromAllowList(collectionId, normalizeAccountId(address));1744 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1745 const result = getGenericResult(events);17461747 // What to expect1748 // tslint:disable-next-line:no-unused-expression1749 expect(result.success).to.be.false;1750 });1751}17521753export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1754 : Promise<UpDataStructsRpcCollection | null> => {1755 return (await api.rpc.unique.collectionById(collectionId)).unwrapOr(null);1756};17571758export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1759 // set global object - collectionsCount1760 return (await api.rpc.unique.collectionStats()).created.toNumber();1761};17621763export async function queryCollectionExpectSuccess(api: ApiPromise, collectionId: number): Promise<UpDataStructsRpcCollection> {1764 return (await api.rpc.unique.collectionById(collectionId)).unwrap();1765}17661767export const describe_xcm = (1768 process.env.RUN_XCM_TESTS1769 ? describe1770 : describe.skip1771);17721773export async function waitNewBlocks(blocksCount = 1): Promise<void> {1774 await usingApi(async (api) => {1775 const promise = new Promise<void>(async (resolve) => {1776 const unsubscribe = await api.rpc.chain.subscribeNewHeads(() => {1777 if (blocksCount > 0) {1778 blocksCount--;1779 } else {1780 unsubscribe();1781 resolve();1782 }1783 });1784 });1785 return promise;1786 });1787}17881789export async function waitEvent(1790 api: ApiPromise,1791 maxBlocksToWait: number,1792 eventSection: string,1793 eventMethod: string,1794): Promise<EventRecord | null> {17951796 const promise = new Promise<EventRecord | null>(async (resolve) => {1797 const unsubscribe = await api.rpc.chain.subscribeNewHeads(async header => {1798 const blockNumber = header.number.toHuman();1799 const blockHash = header.hash;1800 const eventIdStr = `${eventSection}.${eventMethod}`;1801 const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;18021803 console.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);18041805 const apiAt = await api.at(blockHash);1806 const eventRecords = await apiAt.query.system.events();18071808 const neededEvent = eventRecords.find(r => {1809 return r.event.section == eventSection && r.event.method == eventMethod;1810 });18111812 if (neededEvent) {1813 unsubscribe();1814 resolve(neededEvent);1815 } else if (maxBlocksToWait > 0) {1816 maxBlocksToWait--;1817 } else {1818 console.log(`Event \`${eventIdStr}\` is NOT found`);18191820 unsubscribe();1821 resolve(null);1822 }1823 });1824 });1825 return promise;1826}18271828export async function repartitionRFT(1829 api: ApiPromise,1830 collectionId: number,1831 sender: IKeyringPair,1832 tokenId: number,1833 amount: bigint,1834): Promise<boolean> {1835 const tx = api.tx.unique.repartition(collectionId, tokenId, amount);1836 const events = await submitTransactionAsync(sender, tx);1837 const result = getGenericResult(events);18381839 return result.success;1840}18411842export async function itApi(name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean } = {}) {1843 let i: any = it;1844 if (opts.only) i = i.only;1845 else if (opts.skip) i = i.skip;1846 i(name, async () => {1847 await usingApi(async (api, privateKeyWrapper) => {1848 await cb({api, privateKeyWrapper});1849 });1850 });1851}18521853itApi.only = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {only: true});1854itApi.skip = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {skip: true});18551856let accountSeed = 10000;1857export function generateKeyringPair(keyring: Keyring) {1858 const privateKey = `0xDEADBEEF${(Date.now()+(accountSeed++)).toString(16).padStart(64-8,'0')}`;1859 return keyring.addFromUri(privateKey);1860}18611862export async function expectSubstrateEventsAtBlock(api: ApiPromise, blockNumber: AnyNumber | BlockNumber, section: string, methods: string[], dryRun = false) {1863 const blockHash = await api.rpc.chain.getBlockHash(blockNumber);1864 const subEvents = (await api.query.system.events.at(blockHash))1865 .filter(x => x.event.section === section)1866 .map((x) => x.toHuman());1867 const events = methods.map((m) => {1868 return {1869 event: {1870 method: m,1871 section,1872 },1873 };1874 });1875 if (!dryRun) {1876 expect(subEvents).to.be.like(events);1877 }1878 return subEvents;1879}tests/src/eth/allowlist.test.tsdiffbeforeafterboth--- a/tests/src/eth/allowlist.test.ts
+++ b/tests/src/eth/allowlist.test.ts
@@ -78,7 +78,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const user = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
@@ -94,7 +94,7 @@
// const owner = await helper.eth.createAccountWithBalance(donor);
// const user = donor;
- // const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
// const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
// expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
@@ -110,7 +110,7 @@
const notOwner = await helper.eth.createAccountWithBalance(donor);
const user = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
@@ -129,7 +129,7 @@
// const notOwner = await helper.eth.createAccountWithBalance(donor);
// const user = donor;
- // const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
// const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
// expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -18,13 +18,26 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x5ad4f440
+/// @dev the ERC-165 identifier for this interface is 0xf62c7aa9
interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
/// @param description Informative description of the collection
/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
/// @return address Address of the newly created collection
+ /// @dev EVM selector for this function is: 0x844af658,
+ /// or in textual repr: createNFTCollection(string,string,string)
+ function createNFTCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix
+ ) external payable returns (address);
+
+ /// Create an NFT collection
+ /// @param name Name of the collection
+ /// @param description Informative description of the collection
+ /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
+ /// @return address Address of the newly created collection
/// @dev EVM selector for this function is: 0xe34a6844,
/// or in textual repr: createNonfungibleCollection(string,string,string)
function createNonfungibleCollection(
@@ -33,9 +46,9 @@
string memory tokenPrefix
) external payable returns (address);
- /// @dev EVM selector for this function is: 0xa634a5f9,
- /// or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)
- function createERC721MetadataCompatibleCollection(
+ /// @dev EVM selector for this function is: 0xd1df968c,
+ /// or in textual repr: createERC721MetadataNFTCollection(string,string,string,string)
+ function createERC721MetadataNFTCollection(
string memory name,
string memory description,
string memory tokenPrefix,
@@ -50,9 +63,17 @@
string memory tokenPrefix
) external payable returns (address);
- /// @dev EVM selector for this function is: 0xa5596388,
- /// or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)
- function createERC721MetadataCompatibleRFTCollection(
+ /// @dev EVM selector for this function is: 0x44a68ad5,
+ /// or in textual repr: createRefungibleCollection(string,string,string)
+ function createRefungibleCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix
+ ) external payable returns (address);
+
+ /// @dev EVM selector for this function is: 0xbea6a299,
+ /// or in textual repr: createERC721MetadataRFTCollection(string,string,string,string)
+ function createERC721MetadataRFTCollection(
string memory name,
string memory description,
string memory tokenPrefix,
tests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionAdmin.test.ts
+++ b/tests/src/eth/collectionAdmin.test.ts
@@ -38,7 +38,7 @@
itEth('Add admin by owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const newAdmin = helper.eth.createAccount();
@@ -51,7 +51,7 @@
itEth.skip('Add substrate admin by owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const [newAdmin] = await helper.arrange.createAccounts([10n], donor);
@@ -64,7 +64,7 @@
itEth('Verify owner or admin', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const newAdmin = helper.eth.createAccount();
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -75,7 +75,7 @@
itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const admin = await helper.eth.createAccountWithBalance(donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -93,7 +93,7 @@
itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const notAdmin = await helper.eth.createAccountWithBalance(donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -108,7 +108,7 @@
itEth.skip('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const admin = await helper.eth.createAccountWithBalance(donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -126,7 +126,7 @@
itEth.skip('(!negative tests!) Add substrate admin by USER is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const notAdmin0 = await helper.eth.createAccountWithBalance(donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -150,7 +150,7 @@
itEth('Remove admin by owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const newAdmin = helper.eth.createAccount();
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -170,7 +170,7 @@
itEth.skip('Remove substrate admin by owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const [newAdmin] = await helper.arrange.createAccounts([10n], donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -188,7 +188,7 @@
itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -210,7 +210,7 @@
itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -230,7 +230,7 @@
itEth.skip('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const [adminSub] = await helper.arrange.createAccounts([10n], donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -250,7 +250,7 @@
itEth.skip('(!negative tests!) Remove substrate admin by USER is not allowed', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const [adminSub] = await helper.arrange.createAccounts([10n], donor);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -279,7 +279,7 @@
itEth('Change owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const newOwner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await collectionEvm.methods.setOwner(newOwner).send();
@@ -291,7 +291,7 @@
itEth('change owner call fee', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const newOwner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwner(newOwner).send());
expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
@@ -301,7 +301,7 @@
itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const newOwner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await expect(collectionEvm.methods.setOwner(newOwner).send({from: newOwner})).to.be.rejected;
@@ -321,7 +321,7 @@
itEth.skip('Change owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const [newOwner] = await helper.arrange.createAccounts([10n], donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.true;
@@ -336,7 +336,7 @@
itEth.skip('change owner call fee', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const [newOwner] = await helper.arrange.createAccounts([10n], donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send());
@@ -348,7 +348,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const otherReceiver = await helper.eth.createAccountWithBalance(donor);
const [newOwner] = await helper.arrange.createAccounts([10n], donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await expect(collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send({from: otherReceiver})).to.be.rejected;
tests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth--- a/tests/src/eth/collectionHelpersAbi.json
+++ b/tests/src/eth/collectionHelpersAbi.json
@@ -32,7 +32,7 @@
{ "internalType": "string", "name": "tokenPrefix", "type": "string" },
{ "internalType": "string", "name": "baseUri", "type": "string" }
],
- "name": "createERC721MetadataCompatibleCollection",
+ "name": "createERC721MetadataNFTCollection",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "payable",
"type": "function"
@@ -44,7 +44,18 @@
{ "internalType": "string", "name": "tokenPrefix", "type": "string" },
{ "internalType": "string", "name": "baseUri", "type": "string" }
],
- "name": "createERC721MetadataCompatibleRFTCollection",
+ "name": "createERC721MetadataRFTCollection",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "string", "name": "name", "type": "string" },
+ { "internalType": "string", "name": "description", "type": "string" },
+ { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+ ],
+ "name": "createNFTCollection",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "payable",
"type": "function"
@@ -73,6 +84,17 @@
},
{
"inputs": [
+ { "internalType": "string", "name": "name", "type": "string" },
+ { "internalType": "string", "name": "description", "type": "string" },
+ { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+ ],
+ "name": "createRefungibleCollection",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{
"internalType": "address",
"name": "collectionAddress",
tests/src/eth/collectionProperties.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionProperties.test.ts
+++ b/tests/src/eth/collectionProperties.test.ts
@@ -24,7 +24,7 @@
const raw = (await collection.getData())?.raw;
- expect(raw.properties[0].value).to.equal('testValue');
+ expect(raw.properties[1].value).to.equal('testValue');
});
itEth('Can be deleted', async({helper}) => {
@@ -54,3 +54,46 @@
expect(value).to.equal(helper.getWeb3().utils.toHex('testValue'));
});
});
+
+describe('Supports ERC721Metadata', () => {
+ let donor: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (_helper, privateKey) => {
+ donor = privateKey('//Alice');
+ });
+ });
+
+ itEth('ERC721Metadata property can be set for NFT collection', async({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.nft.mintCollection(donor, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+
+ await collection.addAdmin(donor, {Ethereum: caller});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
+
+ await contract.methods.setCollectionProperty('ERC721Metadata', Buffer.from('1')).send({from: caller});
+
+ expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true;
+
+ await contract.methods.setCollectionProperty('ERC721Metadata', Buffer.from('0')).send({from: caller});
+
+ expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false;
+ });
+
+ itEth('ERC721Metadata property can be set for RFT collection', async({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(donor, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+
+ await collection.addAdmin(donor, {Ethereum: caller});
+
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
+
+ await contract.methods.setCollectionProperty('ERC721Metadata', Buffer.from('1')).send({from: caller});
+
+ expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true;
+
+ await contract.methods.setCollectionProperty('ERC721Metadata', Buffer.from('0')).send({from: caller});
+
+ expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false;
+ });
+});
tests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionSponsoring.test.ts
+++ b/tests/src/eth/collectionSponsoring.test.ts
@@ -54,7 +54,7 @@
// itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {
// const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
// const collectionHelpers = evmCollectionHelpers(web3, owner);
- // let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+ // let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send();
// const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
// const sponsor = privateKeyWrapper('//Alice');
// const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
@@ -75,7 +75,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
- let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
+ let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
const sponsor = await helper.eth.createAccountWithBalance(donor);
const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner);
@@ -97,7 +97,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
- let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
+ let result = await collectionHelpers.methods.createERC721MetadataNFTCollection('Sponsor collection', '1', '1', '').send({value: Number(2n * nominal)});
const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
const collectionId = helper.ethAddress.extractCollectionId(collectionIdAddress);
const collection = helper.nft.getCollectionObject(collectionId);
@@ -167,7 +167,7 @@
// itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {
// const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
// const collectionHelpers = evmCollectionHelpers(web3, owner);
- // const result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+ // const result = await collectionHelpers.methods.createERC721MetadataNFTCollection('Sponsor collection', '1', '1', '').send();
// const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
// const sponsor = privateKeyWrapper('//Alice');
// const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
@@ -223,7 +223,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
- let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
+ let result = await collectionHelpers.methods.createERC721MetadataNFTCollection('Sponsor collection', '1', '1', '').send({value: Number(2n * nominal)});
const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
const collectionId = helper.ethAddress.extractCollectionId(collectionIdAddress);
const collection = helper.nft.getCollectionObject(collectionId);
tests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createNFTCollection.test.ts
+++ b/tests/src/eth/createNFTCollection.test.ts
@@ -37,7 +37,7 @@
// todo:playgrounds this might fail when in async environment.
const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
- const {collectionId} = await helper.eth.createNonfungibleCollection(owner, name, description, prefix);
+ const {collectionId} = await helper.eth.createNFTCollection(owner, name, description, prefix);
const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
const collection = helper.nft.getCollectionObject(collectionId);
@@ -64,7 +64,7 @@
.call()).to.be.false;
await collectionHelpers.methods
- .createNonfungibleCollection('A', 'A', 'A')
+ .createNFTCollection('A', 'A', 'A')
.send({value: Number(2n * helper.balance.getOneTokenNominal())});
expect(await collectionHelpers.methods
@@ -76,7 +76,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const sponsor = await helper.eth.createAccountWithBalance(donor);
const ss58Format = helper.chain.getChainProperties().ss58Format;
- const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+ const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await collection.methods.setCollectionSponsor(sponsor).send();
@@ -95,7 +95,7 @@
itEth('Set limits', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'FLO');
+ const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'FLO');
const limits = {
accountTokenOwnershipLimit: 1000,
sponsoredDataSize: 1024,
@@ -138,7 +138,7 @@
.methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
.to.be.false;
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Exister', 'absolutely anything', 'EVC');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Exister', 'absolutely anything', 'EVC');
expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)
.methods.isCollectionExist(collectionAddress).call())
.to.be.true;
@@ -166,7 +166,7 @@
const tokenPrefix = 'A';
await expect(collectionHelper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .createNFTCollection(collectionName, description, tokenPrefix)
.call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
}
@@ -176,7 +176,7 @@
const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
const tokenPrefix = 'A';
await expect(collectionHelper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .createNFTCollection(collectionName, description, tokenPrefix)
.call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
}
{
@@ -185,7 +185,7 @@
const description = 'A';
const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
await expect(collectionHelper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .createNFTCollection(collectionName, description, tokenPrefix)
.call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
}
});
@@ -194,14 +194,14 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
await expect(collectionHelper.methods
- .createNonfungibleCollection('Peasantry', 'absolutely anything', 'CVE')
+ .createNFTCollection('Peasantry', 'absolutely anything', 'CVE')
.call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
});
itEth('(!negative test!) Check owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const malfeasant = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
const EXPECTED_ERROR = 'NoPermission';
{
@@ -224,7 +224,7 @@
itEth('(!negative test!) Set limits', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'OLF');
+ const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await expect(collectionEvm.methods
.setCollectionLimit('badLimit', 'true')
tests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createRFTCollection.test.ts
+++ b/tests/src/eth/createRFTCollection.test.ts
@@ -39,7 +39,7 @@
// todo:playgrounds this might fail when in async environment.
const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
- const {collectionId} = await helper.eth.createRefungibleCollection(owner, name, description, prefix);
+ const {collectionId} = await helper.eth.createRFTCollection(owner, name, description, prefix);
const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
const data = (await helper.rft.getData(collectionId))!;
@@ -77,7 +77,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const sponsor = await helper.eth.createAccountWithBalance(donor);
const ss58Format = helper.chain.getChainProperties().ss58Format;
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
await collection.methods.setCollectionSponsor(sponsor).send();
@@ -96,7 +96,7 @@
itEth('Set limits', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'INSI');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'INSI');
const limits = {
accountTokenOwnershipLimit: 1000,
sponsoredDataSize: 1024,
@@ -139,7 +139,7 @@
.methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
.to.be.false;
- const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Exister', 'absolutely anything', 'WIWT');
+ const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Exister', 'absolutely anything', 'WIWT');
expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)
.methods.isCollectionExist(collectionAddress).call())
.to.be.true;
@@ -202,7 +202,7 @@
itEth('(!negative test!) Check owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const peasant = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
+ const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
const EXPECTED_ERROR = 'NoPermission';
{
@@ -225,7 +225,7 @@
itEth('(!negative test!) Set limits', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'ISNI');
+ const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'ISNI');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
await expect(collectionEvm.methods
.setCollectionLimit('badLimit', 'true')
tests/src/eth/evmCoder.test.tsdiffbeforeafterboth--- a/tests/src/eth/evmCoder.test.ts
+++ b/tests/src/eth/evmCoder.test.ts
@@ -65,7 +65,7 @@
itEth('Call non-existing function', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collection = await helper.eth.createNonfungibleCollection(owner, 'EVMCODER', '', 'TEST');
+ const collection = await helper.eth.createNFTCollection(owner, 'EVMCODER', '', 'TEST');
const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c'));
const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address));
{
tests/src/eth/fractionalizer/fractionalizer.test.tsdiffbeforeafterboth--- a/tests/src/eth/fractionalizer/fractionalizer.test.ts
+++ b/tests/src/eth/fractionalizer/fractionalizer.test.ts
@@ -62,7 +62,7 @@
const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{
nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string
}> => {
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
@@ -92,7 +92,7 @@
itEth('Set RFT collection', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 10n);
const fractionalizer = await deployContract(helper, owner);
- const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');
+ const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
const rftContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
await rftContract.methods.addCollectionAdmin(fractionalizer.options.address).send({from: owner});
@@ -121,7 +121,7 @@
itEth('Set Allowlist', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
const {contract: fractionalizer} = await initContract(helper, owner);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
expect(result1.events).to.be.like({
@@ -146,7 +146,7 @@
itEth('NFT to RFT', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
@@ -231,7 +231,7 @@
itEth('call setRFTCollection twice', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');
+ const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
const fractionalizer = await deployContract(helper, owner);
@@ -244,7 +244,7 @@
itEth('call setRFTCollection with NFT collection', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const fractionalizer = await deployContract(helper, owner);
@@ -257,7 +257,7 @@
itEth('call setRFTCollection while not collection admin', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
const fractionalizer = await deployContract(helper, owner);
- const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');
+ const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())
.to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g);
@@ -278,7 +278,7 @@
itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
@@ -293,7 +293,7 @@
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
const nftOwner = await helper.eth.createAccountWithBalance(donor, 10n);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
@@ -310,7 +310,7 @@
itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
@@ -325,7 +325,7 @@
itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
@@ -341,7 +341,7 @@
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
const fractionalizer = await deployContract(helper, owner);
- const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');
+ const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
const rftTokenId = await refungibleContract.methods.nextTokenId().call();
await refungibleContract.methods.mint(owner, rftTokenId).send({from: owner});
@@ -354,7 +354,7 @@
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
const {contract: fractionalizer} = await initContract(helper, owner);
- const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');
+ const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
const rftTokenId = await refungibleContract.methods.nextTokenId().call();
await refungibleContract.methods.mint(owner, rftTokenId).send({from: owner});
@@ -365,7 +365,7 @@
itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 20n);
- const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');
+ const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
const fractionalizer = await deployContract(helper, owner);
@@ -432,7 +432,7 @@
await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner});
await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true);
- const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');
+ const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
const nftTokenId = await nftContract.methods.nextTokenId().call();
await nftContract.methods.mint(owner, nftTokenId).send({from: owner});
tests/src/eth/nesting/nest.test.tsdiffbeforeafterboth--- a/tests/src/eth/nesting/nest.test.ts
+++ b/tests/src/eth/nesting/nest.test.ts
@@ -7,7 +7,7 @@
helper: EthUniqueHelper,
owner: string,
): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await contract.methods.setCollectionNesting(true).send({from: owner});
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -84,7 +84,7 @@
const receiver = helper.eth.createAccount();
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
- let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send({value: Number(2n * helper.balance.getOneTokenNominal())});
+ let result = await collectionHelper.methods.createERC721MetadataNFTCollection('Mint collection', 'a', 'b', tokenPrefix).send({value: Number(2n * helper.balance.getOneTokenNominal())});
const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -146,7 +146,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Minty', '6', '6');
+ const {collectionAddress} = await helper.eth.createERC721MetadataNFTCollection(owner, 'Mint collection', '6', '6', '');
const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const nextTokenId = await contract.methods.nextTokenId().call();
tests/src/eth/payable.test.tsdiffbeforeafterboth--- a/tests/src/eth/payable.test.ts
+++ b/tests/src/eth/payable.test.ts
@@ -146,7 +146,7 @@
const caller = await helper.eth.createAccountWithBalance(donor);
const contract = await deployProxyContract(helper, deployer);
- const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;
+ const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;
const initialCallerBalance = await helper.balance.getEthereum(caller);
const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
await contract.methods.mintNftToken(collectionAddress).send({from: caller});
@@ -164,7 +164,7 @@
const initialCallerBalance = await helper.balance.getEthereum(caller);
const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
- await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});
+ await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});
const finalCallerBalance = await helper.balance.getEthereum(caller);
const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
expect(finalCallerBalance < initialCallerBalance).to.be.true;
@@ -177,8 +177,8 @@
const caller = await helper.eth.createAccountWithBalance(donor);
const collectionHelper = helper.ethNativeContract.collectionHelpers(caller);
- await expect(collectionHelper.methods.createNonfungibleCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
- await expect(collectionHelper.methods.createNonfungibleCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+ await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+ await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
});
itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {
@@ -227,9 +227,9 @@
InnerContract(innerContract).flip();
}
- function createNonfungibleCollection() external payable {
+ function createNFTCollection() external payable {
address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
- address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection{value: msg.value}("A", "B", "C");
+ address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C");
emit CollectionCreated(nftCollection);
}
tests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth--- a/tests/src/eth/proxy/nonFungibleProxy.test.ts
+++ b/tests/src/eth/proxy/nonFungibleProxy.test.ts
@@ -101,7 +101,7 @@
itEth('Can perform mint()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'A', 'A');
+ const {collectionAddress} = await helper.eth.createERC721MetadataNFTCollection(owner, 'A', 'A', 'A', '');
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -31,7 +31,7 @@
itEth('totalSupply', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TotalSupply', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'TotalSupply', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const nextTokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, nextTokenId).send();
@@ -41,7 +41,7 @@
itEth('balanceOf', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'BalanceOf', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'BalanceOf', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
{
@@ -63,7 +63,7 @@
itEth('ownerOf', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -76,7 +76,7 @@
itEth('ownerOf after burn', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf-AfterBurn', '6', '6');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf-AfterBurn', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -95,7 +95,7 @@
itEth('ownerOf for partial ownership', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Partial-OwnerOf', '6', '6');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -124,7 +124,7 @@
itEth('Can perform mint()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Minty', '6', '6');
+ const {collectionAddress} = await helper.eth.createERC721MetadataRFTCollection(owner, 'Minty', '6', '6', '');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
const nextTokenId = await contract.methods.nextTokenId().call();
@@ -147,7 +147,7 @@
itEth('Can perform mintBulk()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'MintBulky', '6', '6');
+ const {collectionAddress} = await helper.eth.createERC721MetadataRFTCollection(owner, 'MintBulky', '6', '6', '');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
{
@@ -179,7 +179,7 @@
itEth('Can perform burn()', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Burny', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -197,7 +197,7 @@
itEth('Can perform transferFrom()', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TransferFromy', '6', '6');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -241,7 +241,7 @@
itEth('Can perform transfer()', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -271,7 +271,7 @@
itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Partial-to-Full', '6', '6');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -298,7 +298,7 @@
itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Full-to-Partial', '6', '6');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -336,7 +336,7 @@
itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer-From', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -350,7 +350,7 @@
itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer', '6', '6');
+ const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -386,8 +386,8 @@
itEth('Returns symbol name', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Leviathan', '', '12');
- const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+ const {collectionId} = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '12'});
+ const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);
const symbol = await contract.methods.symbol().call();
expect(symbol).to.equal('12');
});
tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungibleToken.test.ts
+++ b/tests/src/eth/reFungibleToken.test.ts
@@ -81,7 +81,7 @@
const receiver = helper.eth.createAccount();
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
- let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send({value: Number(2n * helper.balance.getOneTokenNominal())});
+ let result = await collectionHelper.methods.createERC721MetadataNFTCollection('Mint collection', 'a', 'b', tokenPrefix).send({value: Number(2n * helper.balance.getOneTokenNominal())});
const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
@@ -294,7 +294,7 @@
itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = await helper.eth.createAccountWithBalance(donor);
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Devastation', '6', '6');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');
const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
@@ -479,7 +479,7 @@
itEth('Default parent token address and id', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sands', '', 'GRAIN');
+ const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');
const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
const tokenId = await collectionContract.methods.nextTokenId().call();
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -174,11 +174,23 @@
return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);
}
- async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
+ async createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();
const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
- const result = await collectionHelper.methods.createNonfungibleCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});
+ const result = await collectionHelper.methods.createNFTCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});
+
+ const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+ const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
+
+ return {collectionId, collectionAddress};
+ }
+
+ async createERC721MetadataNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string}> {
+ const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();
+ const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
+
+ const result = await collectionHelper.methods.createERC721MetadataNFTCollection(name, description, tokenPrefix, baseUri).send({value: Number(collectionCreationPrice)});
const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
@@ -186,7 +198,7 @@
return {collectionId, collectionAddress};
}
- async createRefungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
+ async createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();
const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
@@ -198,6 +210,18 @@
return {collectionId, collectionAddress};
}
+ async createERC721MetadataRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string}> {
+ const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();
+ const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
+
+ const result = await collectionHelper.methods.createERC721MetadataRFTCollection(name, description, tokenPrefix, baseUri).send({value: Number(collectionCreationPrice)});
+
+ const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+ const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
+
+ return {collectionId, collectionAddress};
+ }
+
async deployCollectorContract(signer: string): Promise<Contract> {
return await this.helper.ethContract.deployByCode(signer, 'Collector', `
// SPDX-License-Identifier: UNLICENSED
tests/src/nesting/properties.test.tsdiffbeforeafterboth--- a/tests/src/nesting/properties.test.ts
+++ b/tests/src/nesting/properties.test.ts
@@ -18,6 +18,52 @@
import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../util/playgrounds';
import {UniqueHelper, UniqueBaseCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '../util/playgrounds/unique';
+
+describe('Composite Properties Test', () => {
+ let alice: IKeyringPair;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([50n], donor);
+ });
+ });
+
+ async function testMakeSureSuppliesRequired(baseCollection: UniqueNFTCollection | UniqueRFTCollection) {
+
+ const collectionOption = await baseCollection.getOptions();
+ expect(collectionOption).is.not.null;
+ let collection = collectionOption;
+ expect(collection.tokenPropertyPermissions).to.be.empty;
+ expect(collection.properties).to.be.deep.equal([{key: 'ERC721Metadata', value: '1'}]);
+
+ const propertyPermissions = [
+ {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},
+ {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},
+ ];
+ await expect(await baseCollection.setTokenPropertyPermissions(alice, propertyPermissions)).to.be.true;
+
+ const collectionProperties = [
+ {key: 'ERC721Metadata', value: '1'},
+ {key: 'black_hole', value: 'LIGO'},
+ {key: 'electron', value: 'come bond'},
+ ];
+
+ await expect(await baseCollection.setProperties(alice, collectionProperties)).to.be.true;
+
+ collection = await baseCollection.getOptions();
+ expect(collection.tokenPropertyPermissions).to.be.deep.equal(propertyPermissions);
+ expect(collection.properties).to.be.deep.equal(collectionProperties);
+ }
+
+ itSub('Makes sure collectionById supplies required fields for NFT', async ({helper}) => {
+ await testMakeSureSuppliesRequired(await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}));
+ });
+
+ itSub.ifWithPallets('Makes sure collectionById supplies required fields for ReFungible', [Pallets.ReFungible], async ({helper}) => {
+ await testMakeSureSuppliesRequired(await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}));
+ });
+});
// ---------- COLLECTION PROPERTIES
describe('Integration Test: Collection Properties', () => {
@@ -33,7 +79,11 @@
itSub('Properties are initially empty', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice);
- expect(await collection.getProperties()).to.be.empty;
+ const properties = await collection.getProperties();
+ expect(properties).to.be.deep.equal([{
+ 'key': 'ERC721Metadata',
+ 'value': '1',
+ }]);
});
async function testSetsPropertiesForCollection(collection: UniqueBaseCollection) {
@@ -150,7 +200,11 @@
await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))
.to.be.rejectedWith(/common\.NoPermission/);
- expect(await collection.getProperties()).to.be.empty;
+ const properties = await collection.getProperties();
+ expect(properties).to.be.deep.equal([{
+ 'key': 'ERC721Metadata',
+ 'value': '1',
+ }]);
}
itSub('Fails to set properties in a NFT collection if not its onwer/administrator', async ({helper}) => {
@@ -202,7 +256,11 @@
await expect(collection.setProperties(alice, propertiesToBeSet)).
to.be.rejectedWith(/common\.PropertyLimitReached/);
- expect(await collection.getProperties()).to.be.empty;
+ const properties = await collection.getProperties();
+ expect(properties).to.be.deep.equal([{
+ 'key': 'ERC721Metadata',
+ 'value': '1',
+ }]);
}
itSub('Fails to set more properties than it is allowed (NFT)', async ({helper}) => {
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -981,6 +981,10 @@
return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();
}
+ async getCollectionOptions(collectionId: number) {
+ return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();
+ }
+
/**
* Deletes onchain properties from the collection.
*
@@ -1293,6 +1297,7 @@
async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {
collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};
+ collectionOptions.properties = collectionOptions.properties || [{key: 'ERC721Metadata', value: '1'}];
for (const key of ['name', 'description', 'tokenPrefix']) {
if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
}
@@ -2476,6 +2481,10 @@
return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);
}
+ async getOptions() {
+ return await this.helper.collection.getCollectionOptions(this.collectionId);
+ }
+
async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {
return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);
}